Update to Drupal 8.0.3. For more information, see https://www.drupal.org/drupal-8.0.3-release-notes
This commit is contained in:
parent
10f9f7fbde
commit
9db4fae9a7
202 changed files with 3806 additions and 760 deletions
|
@ -28,6 +28,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
class MessageAction extends ConfigurableActionBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The token service.
|
||||
*
|
||||
* @var \Drupal\Core\Utility\Token
|
||||
*/
|
||||
protected $token;
|
||||
|
@ -48,10 +50,8 @@ class MessageAction extends ConfigurableActionBase implements ContainerFactoryPl
|
|||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Utility\Token
|
||||
* The token service.
|
||||
* @param \Drupal\Core\Utility\Token $token
|
||||
* The token replacement service.
|
||||
* The token service.
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer.
|
||||
*/
|
||||
|
|
|
@ -323,14 +323,22 @@ function book_node_prepare_form(NodeInterface $node, $operation, FormStateInterf
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for node_delete_confirm().
|
||||
* Implements hook_form_BASE_FORM_ID_alter().
|
||||
*
|
||||
* Alters the confirm form for a single node deletion.
|
||||
*
|
||||
* @see node_delete_confirm()
|
||||
*/
|
||||
function book_form_node_delete_confirm_alter(&$form, FormStateInterface $form_state) {
|
||||
$node = Node::load($form['nid']['#value']);
|
||||
function book_form_node_confirm_form_alter(&$form, FormStateInterface $form_state) {
|
||||
// Only need to alter the delete operation form.
|
||||
if ($form_state->getFormObject()->getOperation() !== 'delete') {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $form_state->getFormObject()->getEntity();
|
||||
if (!book_type_is_allowed($node->getType())) {
|
||||
// Not a book node.
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($node->book) && $node->book['has_children']) {
|
||||
$form['book_warning'] = array(
|
||||
|
|
|
@ -439,9 +439,9 @@ class BookManager implements BookManagerInterface {
|
|||
if ($nid == $original['bid']) {
|
||||
// Handle deletion of a top-level post.
|
||||
$result = $this->bookOutlineStorage->loadBookChildren($nid);
|
||||
|
||||
foreach ($result as $child) {
|
||||
$child['bid'] = $child['nid'];
|
||||
$children = $this->entityManager->getStorage('node')->loadMultiple(array_keys($result));
|
||||
foreach ($children as $child) {
|
||||
$child->book['bid'] = $child->id();
|
||||
$this->updateOutline($child);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ class BookTest extends WebTestBase {
|
|||
/**
|
||||
* A book node.
|
||||
*
|
||||
* @var object
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $book;
|
||||
|
||||
|
@ -77,11 +77,13 @@ class BookTest extends WebTestBase {
|
|||
$this->bookAuthor = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books'));
|
||||
$this->webUser = $this->drupalCreateUser(array('access printer-friendly version', 'node test view'));
|
||||
$this->webUserWithoutNodeAccess = $this->drupalCreateUser(array('access printer-friendly version'));
|
||||
$this->adminUser = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books', 'administer blocks', 'administer permissions', 'administer book outlines', 'node test view', 'administer content types', 'administer site configuration'));
|
||||
$this->adminUser = $this->drupalCreateUser(array('create new books', 'create book content', 'edit any book content', 'delete any book content', 'add content to books', 'administer blocks', 'administer permissions', 'administer book outlines', 'node test view', 'administer content types', 'administer site configuration'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new book with a page hierarchy.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
function createBook() {
|
||||
// Create new book.
|
||||
|
@ -498,6 +500,25 @@ class BookTest extends WebTestBase {
|
|||
$node_storage->resetCache(array($this->book->id()));
|
||||
$node = $node_storage->load($this->book->id());
|
||||
$this->assertTrue(empty($node->book), 'Deleting childless top-level book node properly allowed.');
|
||||
|
||||
// Tests directly deleting a book parent.
|
||||
$nodes = $this->createBook();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet($this->book->urlInfo('delete-form'));
|
||||
$this->assertRaw(t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', ['%title' => $this->book->label()]));
|
||||
// Delete parent, and visit a child page.
|
||||
$this->drupalPostForm($this->book->urlInfo('delete-form'), [], t('Delete'));
|
||||
$this->drupalGet($nodes[0]->urlInfo());
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($nodes[0]->label());
|
||||
// The book parents should be updated.
|
||||
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$node_storage->resetCache();
|
||||
$child = $node_storage->load($nodes[0]->id());
|
||||
$this->assertEqual($child->id(), $child->book['bid'], 'Child node book ID updated when parent is deleted.');
|
||||
// 3rd-level children should now be 2nd-level.
|
||||
$second = $node_storage->load($nodes[1]->id());
|
||||
$this->assertEqual($child->id(), $second->book['bid'], '3rd-level child node is now second level when top-level node is deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -35,10 +35,11 @@ function hook_ckeditor_plugin_info_alter(array &$plugins) {
|
|||
* iframe versions of CKEditor.
|
||||
*
|
||||
* Front-end themes (and base themes) can easily specify CSS files to be used in
|
||||
* iframe instances of CKEditor through an entry in their .info file:
|
||||
* iframe instances of CKEditor through an entry in their .info.yml file:
|
||||
*
|
||||
* @code
|
||||
* ckeditor_stylesheets[] = css/ckeditor-iframe.css
|
||||
* ckeditor_stylesheets:
|
||||
* - css/ckeditor-iframe.css
|
||||
* @endcode
|
||||
*
|
||||
* @param array &$css
|
||||
|
|
|
@ -19,6 +19,7 @@ use Drupal\Component\Annotation\Plugin;
|
|||
* @see \Drupal\ckeditor\CKEditorPluginInterface
|
||||
* @see \Drupal\ckeditor\CKEditorPluginBase
|
||||
* @see \Drupal\ckeditor\CKEditorPluginManager
|
||||
* @see hook_ckeditor_plugin_info_alter()
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
|
|
|
@ -17,11 +17,11 @@ use Drupal\editor\Entity\Editor;
|
|||
* than the one provided by Drupal core is used. Most CKEditor plugins don't
|
||||
* need to provide additional settings forms.
|
||||
*
|
||||
* This base assumes that your plugin has buttons that you want to be enabled
|
||||
* through the toolbar builder UI. It is still possible to also implement the
|
||||
* CKEditorPluginContextualInterface (for contextual enabling) and
|
||||
* This base class assumes that your plugin has buttons that you want to be
|
||||
* enabled through the toolbar builder UI. It is still possible to also
|
||||
* implement the CKEditorPluginContextualInterface (for contextual enabling) and
|
||||
* CKEditorPluginConfigurableInterface interfaces (for configuring plugin
|
||||
* settings) though.
|
||||
* settings).
|
||||
*
|
||||
* NOTE: the Drupal plugin ID should correspond to the CKEditor plugin name.
|
||||
*
|
||||
|
|
|
@ -298,8 +298,11 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
|
|||
);
|
||||
|
||||
// Finally, set Drupal-specific CKEditor settings.
|
||||
$root_relative_file_url = function ($uri) {
|
||||
return file_url_transform_relative(file_create_url($uri));
|
||||
};
|
||||
$settings += array(
|
||||
'drupalExternalPlugins' => array_map('file_create_url', $external_plugin_files),
|
||||
'drupalExternalPlugins' => array_map($root_relative_file_url, $external_plugin_files),
|
||||
);
|
||||
|
||||
// Parse all CKEditor plugin JavaScript files for translations.
|
||||
|
@ -421,6 +424,7 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
|
|||
$this->moduleHandler->alter('ckeditor_css', $css, $editor);
|
||||
$css = array_merge($css, _ckeditor_theme_css());
|
||||
$css = array_map('file_create_url', $css);
|
||||
$css = array_map('file_url_transform_relative', $css);
|
||||
|
||||
return array_values($css);
|
||||
}
|
||||
|
|
|
@ -90,8 +90,8 @@ class CKEditorTest extends KernelTestBase {
|
|||
'language' => 'en',
|
||||
'stylesSet' => FALSE,
|
||||
'drupalExternalPlugins' => array(
|
||||
'drupalimage' => file_create_url('core/modules/ckeditor/js/plugins/drupalimage/plugin.js'),
|
||||
'drupallink' => file_create_url('core/modules/ckeditor/js/plugins/drupallink/plugin.js'),
|
||||
'drupalimage' => file_url_transform_relative(file_create_url('core/modules/ckeditor/js/plugins/drupalimage/plugin.js')),
|
||||
'drupallink' => file_url_transform_relative(file_create_url('core/modules/ckeditor/js/plugins/drupallink/plugin.js')),
|
||||
),
|
||||
);
|
||||
$expected_config = $this->castSafeStrings($expected_config);
|
||||
|
@ -114,9 +114,9 @@ class CKEditorTest extends KernelTestBase {
|
|||
$expected_config['toolbar'][0]['items'][] = 'Format';
|
||||
$expected_config['format_tags'] = 'p;h2;h3;h4;h5;h6';
|
||||
$expected_config['extraPlugins'] .= ',llama_contextual,llama_contextual_and_button';
|
||||
$expected_config['drupalExternalPlugins']['llama_contextual'] = file_create_url('core/modules/ckeditor/tests/modules/js/llama_contextual.js');
|
||||
$expected_config['drupalExternalPlugins']['llama_contextual_and_button'] = file_create_url('core/modules/ckeditor/tests/modules/js/llama_contextual_and_button.js');
|
||||
$expected_config['contentsCss'][] = file_create_url('core/modules/ckeditor/tests/modules/ckeditor_test.css');
|
||||
$expected_config['drupalExternalPlugins']['llama_contextual'] = file_url_transform_relative(file_create_url('core/modules/ckeditor/tests/modules/js/llama_contextual.js'));
|
||||
$expected_config['drupalExternalPlugins']['llama_contextual_and_button'] = file_url_transform_relative(file_create_url('core/modules/ckeditor/tests/modules/js/llama_contextual_and_button.js'));
|
||||
$expected_config['contentsCss'][] = file_url_transform_relative(file_create_url('core/modules/ckeditor/tests/modules/ckeditor_test.css'));
|
||||
ksort($expected_config);
|
||||
$this->assertIdentical($expected_config, $this->castSafeStrings($this->ckeditor->getJSSettings($editor)), 'Generated JS settings are correct for customized configuration.');
|
||||
|
||||
|
@ -261,15 +261,15 @@ class CKEditorTest extends KernelTestBase {
|
|||
|
||||
// Enable the editor_test module, which implements hook_ckeditor_css_alter().
|
||||
$this->enableModules(array('ckeditor_test'));
|
||||
$expected[] = file_create_url('core/modules/ckeditor/tests/modules/ckeditor_test.css');
|
||||
$expected[] = file_url_transform_relative(file_create_url('core/modules/ckeditor/tests/modules/ckeditor_test.css'));
|
||||
$this->assertIdentical($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a hook_ckeditor_css_alter() implementation exists.');
|
||||
|
||||
// Enable the Bartik theme, which specifies a CKEditor stylesheet.
|
||||
\Drupal::service('theme_handler')->install(['bartik']);
|
||||
$this->config('system.theme')->set('default', 'bartik')->save();
|
||||
$expected[] = file_create_url('core/themes/bartik/css/base/elements.css');
|
||||
$expected[] = file_create_url('core/themes/bartik/css/components/captions.css');
|
||||
$expected[] = file_create_url('core/themes/bartik/css/components/table.css');
|
||||
$expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/base/elements.css'));
|
||||
$expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/components/captions.css'));
|
||||
$expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/components/table.css'));
|
||||
$this->assertIdentical($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a theme providing a CKEditor stylesheet exists.');
|
||||
}
|
||||
|
||||
|
@ -478,8 +478,8 @@ class CKEditorTest extends KernelTestBase {
|
|||
|
||||
protected function getDefaultContentsCssConfig() {
|
||||
return array(
|
||||
file_create_url('core/modules/ckeditor/css/ckeditor-iframe.css'),
|
||||
file_create_url('core/modules/system/css/components/align.module.css'),
|
||||
file_url_transform_relative(file_create_url('core/modules/ckeditor/css/ckeditor-iframe.css')),
|
||||
file_url_transform_relative(file_create_url('core/modules/system/css/components/align.module.css')),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,6 @@ class CKEditorToolbarButtonTest extends WebTestBase {
|
|||
* Method tests CKEditor image buttons.
|
||||
*/
|
||||
public function testImageButtonDisplay() {
|
||||
global $base_url;
|
||||
$this->drupalLogin($this->admin_user);
|
||||
|
||||
// Install the Arabic language (which is RTL) and configure as the default.
|
||||
|
@ -75,7 +74,7 @@ class CKEditorToolbarButtonTest extends WebTestBase {
|
|||
$json_encode = function($html) {
|
||||
return trim(Json::encode($html), '"');
|
||||
};
|
||||
$markup = $json_encode($base_url . '/core/modules/ckeditor/js/plugins/drupalimage/image.png');
|
||||
$markup = $json_encode(file_url_transform_relative(file_create_url('core/modules/ckeditor/js/plugins/drupalimage/image.png')));
|
||||
$this->assertRaw($markup);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ function color_block_view_pre_render(array $build) {
|
|||
// Override logo.
|
||||
$logo = $config->get('logo');
|
||||
if ($logo && $build['content']['site_logo'] && preg_match('!' . $theme_key . '/logo.svg$!', $build['content']['site_logo']['#uri'])) {
|
||||
$build['content']['site_logo']['#uri'] = file_create_url($logo);
|
||||
$build['content']['site_logo']['#uri'] = file_url_transform_relative(file_create_url($logo));
|
||||
}
|
||||
|
||||
return $build;
|
||||
|
|
|
@ -121,7 +121,7 @@ class ColorTest extends WebTestBase {
|
|||
$this->drupalGet('<front>');
|
||||
$stylesheets = $this->config('color.theme.' . $theme)->get('stylesheets');
|
||||
foreach ($stylesheets as $stylesheet) {
|
||||
$this->assertPattern('|' . file_create_url($stylesheet) . '|', 'Make sure the color stylesheet is included in the content. (' . $theme . ')');
|
||||
$this->assertPattern('|' . file_url_transform_relative(file_create_url($stylesheet)) . '|', 'Make sure the color stylesheet is included in the content. (' . $theme . ')');
|
||||
$stylesheet_content = join("\n", file($stylesheet));
|
||||
$this->assertTrue(strpos($stylesheet_content, 'color: #123456') !== FALSE, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')');
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ class ColorTest extends WebTestBase {
|
|||
|
||||
// Ensure that the overridden logo is present in Bartik, which is colorable.
|
||||
$this->drupalGet('admin/appearance/settings/bartik');
|
||||
$this->assertIdentical($GLOBALS['base_url'] . '/' . 'core/misc/druplicon.png', $this->getDrupalSettings()['color']['logo']);
|
||||
$this->assertIdentical($GLOBALS['base_path'] . 'core/misc/druplicon.png', $this->getDrupalSettings()['color']['logo']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide test color module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_system_theme_info().
|
||||
*/
|
||||
function color_test_system_theme_info() {
|
||||
$themes['color_test_theme'] = drupal_get_path('module', 'color_test') . '/themes/color_test_theme/color_test_theme.info.yml';
|
||||
return $themes;
|
||||
}
|
|
@ -563,7 +563,7 @@ function comment_preview(CommentInterface $comment, FormStateInterface $form_sta
|
|||
|
||||
if (!$form_state->getErrors()) {
|
||||
$comment->in_preview = TRUE;
|
||||
$comment_build = comment_view($comment);
|
||||
$comment_build = \Drupal::entityTypeManager()->getViewBuilder('comment')->view($comment);
|
||||
$comment_build['#weight'] = -100;
|
||||
|
||||
$preview_build['comment_preview'] = $comment_build;
|
||||
|
@ -573,7 +573,7 @@ function comment_preview(CommentInterface $comment, FormStateInterface $form_sta
|
|||
$build = array();
|
||||
$parent = $comment->getParentComment();
|
||||
if ($parent && $parent->isPublished()) {
|
||||
$build = comment_view($parent);
|
||||
$build = \Drupal::entityTypeManager()->getViewBuilder('comment')->view($parent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -104,7 +104,7 @@ class Rss extends RssPluginBase {
|
|||
|
||||
// The comment gets built and modules add to or modify
|
||||
// $comment->rss_elements and $comment->rss_namespaces.
|
||||
$build = comment_view($comment, 'rss');
|
||||
$build = $this->entityManager->getViewBuilder('comment')->view($comment, 'rss');
|
||||
unset($build['#theme']);
|
||||
|
||||
if (!empty($comment->rss_namespaces)) {
|
||||
|
|
|
@ -164,7 +164,7 @@ class CommentTranslationUITest extends ContentTranslationUITestBase {
|
|||
'created' => REQUEST_TIME - mt_rand(0, 1000),
|
||||
);
|
||||
$edit = array(
|
||||
'uid' => $user->getUsername() . '(' . $user->id() . ')',
|
||||
'uid' => $user->getUsername() . ' (' . $user->id() . ')',
|
||||
'date[date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'),
|
||||
'date[time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'),
|
||||
);
|
||||
|
|
|
@ -33,7 +33,6 @@ class ConfigEventsTest extends KernelTestBase {
|
|||
|
||||
$config = new Config($name, \Drupal::service('config.storage'), \Drupal::service('event_dispatcher'), \Drupal::service('config.typed'));
|
||||
$config->set('key', 'initial');
|
||||
\Drupal::state()->get('config_events_test.event', FALSE);
|
||||
$this->assertIdentical(\Drupal::state()->get('config_events_test.event', array()), array(), 'No events fired by creating a new configuration object');
|
||||
$config->save();
|
||||
|
||||
|
|
|
@ -506,4 +506,70 @@ class ConfigSchemaTest extends KernelTestBase {
|
|||
$this->assertEqual($definitions['config_schema_test.hook']['additional_metadata'], 'new schema info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests saving config when the type is wrapped by a dynamic type.
|
||||
*/
|
||||
public function testConfigSaveWithWrappingSchema() {
|
||||
$untyped_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'plugin_id' => 'wrapper:foo',
|
||||
'internal_value' => 100,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$typed_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'plugin_id' => 'wrapper:foo',
|
||||
'internal_value' => '100',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Save config which has a schema that enforces types.
|
||||
\Drupal::configFactory()->getEditable('wrapping.config_schema_test.plugin_types')
|
||||
->setData($untyped_values)
|
||||
->save();
|
||||
$this->assertIdentical(\Drupal::config('wrapping.config_schema_test.plugin_types')
|
||||
->get(), $typed_values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests dynamic config schema type with multiple sub-key references.
|
||||
*/
|
||||
public function testConfigSaveWithWrappingSchemaDoubleBrackets() {
|
||||
$untyped_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'foo' => 'cat',
|
||||
'bar' => 'dog',
|
||||
'another_key' => 100,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$typed_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'foo' => 'cat',
|
||||
'bar' => 'dog',
|
||||
'another_key' => '100',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Save config which has a schema that enforces types.
|
||||
\Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets')
|
||||
->setData($untyped_values)
|
||||
->save();
|
||||
$this->assertIdentical(\Drupal::config('wrapping.config_schema_test.double_brackets')
|
||||
->get(), $typed_values);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\config\Tests\Storage;
|
|||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\UnsupportedDataTypeConfigException;
|
||||
|
||||
/**
|
||||
* Tests FileStorage operations.
|
||||
|
@ -76,4 +77,19 @@ class FileStorageTest extends ConfigStorageTestBase {
|
|||
$this->assertIdentical($config_files, $expected_files, 'Absolute path, two config files found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test UnsupportedDataTypeConfigException displays path of
|
||||
* erroneous file during read.
|
||||
*/
|
||||
public function testReadUnsupportedDataTypeConfigException() {
|
||||
file_put_contents($this->storage->getFilePath('core.extension'), PHP_EOL . 'foo : [bar}', FILE_APPEND);
|
||||
try {
|
||||
$config_parsed = $this->storage->read('core.extension');
|
||||
}
|
||||
catch (UnsupportedDataTypeConfigException $e) {
|
||||
$this->pass('Exception thrown when trying to read a field containing invalid data type.');
|
||||
$this->assertTrue((strpos($e->getMessage(), $this->storage->getFilePath('core.extension')) !== FALSE), 'Erroneous file path is displayed.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,10 +32,10 @@ class EventSubscriber implements EventSubscriberInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Reacts to the ConfigEvents::COLLECTION_NAMES event.
|
||||
* Reacts to the ConfigEvents::COLLECTION_INFO event.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigCollectionInfo $collection_info
|
||||
* The configuration collection names event.
|
||||
* The configuration collection info event.
|
||||
*/
|
||||
public function addCollections(ConfigCollectionInfo $collection_info) {
|
||||
$collections = $this->state->get('config_collection_install_test.collection_names', array());
|
||||
|
|
|
@ -213,3 +213,50 @@ config_test.dynamic.*.third_party.config_schema_test:
|
|||
type: integer
|
||||
string:
|
||||
type: string
|
||||
|
||||
wrapping.config_schema_test.plugin_types:
|
||||
type: config_object
|
||||
mapping:
|
||||
tests:
|
||||
type: sequence
|
||||
sequence:
|
||||
- type: wrapping.test.plugin_types.[plugin_id]
|
||||
|
||||
wrapping.test.plugin_types.*:
|
||||
type: test.plugin_types.[plugin_id]
|
||||
mapping:
|
||||
wrapper_value:
|
||||
type: string
|
||||
|
||||
test.plugin_types.wrapper:*:
|
||||
type: test.plugin_types
|
||||
mapping:
|
||||
internal_value:
|
||||
type: string
|
||||
|
||||
wrapping.config_schema_test.double_brackets:
|
||||
type: config_object
|
||||
mapping:
|
||||
tests:
|
||||
type: sequence
|
||||
sequence:
|
||||
- type: wrapping.test.double_brackets.[another_key]
|
||||
|
||||
wrapping.test.double_brackets.*:
|
||||
type: test.double_brackets.[foo].[bar]
|
||||
mapping:
|
||||
wrapper_value:
|
||||
type: string
|
||||
|
||||
test.double_brackets.cat.dog:
|
||||
type: test.double_brackets
|
||||
mapping:
|
||||
another_key:
|
||||
type: string
|
||||
foo:
|
||||
type: string
|
||||
bar:
|
||||
type: string
|
||||
|
||||
test.double_brackets.*:
|
||||
type: mapping
|
||||
|
|
|
@ -35,9 +35,6 @@ function dblog_views_data() {
|
|||
'sort' => array(
|
||||
'id' => 'standard',
|
||||
),
|
||||
'search' => array(
|
||||
'id' => 'standard',
|
||||
),
|
||||
);
|
||||
|
||||
$data['watchdog']['uid'] = array(
|
||||
|
@ -52,9 +49,6 @@ function dblog_views_data() {
|
|||
'argument' => array(
|
||||
'id' => 'numeric',
|
||||
),
|
||||
'search' => array(
|
||||
'id' => 'standard',
|
||||
),
|
||||
'relationship' => array(
|
||||
'title' => t('User'),
|
||||
'help' => t('The user on which the log entry as written.'),
|
||||
|
|
|
@ -308,7 +308,7 @@ class EntityReferenceAdminTest extends WebTestBase {
|
|||
|
||||
$edit = array(
|
||||
'title[0][value]' => 'Test',
|
||||
'field_test_entity_ref_field[0][target_id]' => $node1->getTitle() . '(' . $node1->id() . ')'
|
||||
'field_test_entity_ref_field[0][target_id]' => $node1->getTitle() . ' (' . $node1->id() . ')'
|
||||
);
|
||||
$this->drupalPostForm('node/add/' . $this->type, $edit, t('Save'));
|
||||
$this->assertLink($node1->getTitle());
|
||||
|
|
|
@ -12,6 +12,7 @@ use Drupal\Component\Render\FormattableMarkup;
|
|||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
|
@ -76,6 +77,7 @@ class EntityReferenceItemTest extends FieldUnitTestBase {
|
|||
$this->installEntitySchema('file');
|
||||
|
||||
$this->installSchema('comment', ['comment_entity_statistics']);
|
||||
$this->installSchema('node', ['node_access']);
|
||||
|
||||
$this->vocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomMachineName(),
|
||||
|
@ -100,7 +102,7 @@ class EntityReferenceItemTest extends FieldUnitTestBase {
|
|||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_taxonomy_term', 'Test content entity reference', 'taxonomy_term');
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_entity_test_string_id', 'Test content entity reference with string ID', 'entity_test_string_id');
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_taxonomy_vocabulary', 'Test config entity reference', 'taxonomy_vocabulary');
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_node', 'Test node entity reference', 'node');
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_node', 'Test node entity reference', 'node', 'default', [], FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_user', 'Test user entity reference', 'user');
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_comment', 'Test comment entity reference', 'comment');
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_file', 'Test file entity reference', 'file');
|
||||
|
@ -406,6 +408,64 @@ class EntityReferenceItemTest extends FieldUnitTestBase {
|
|||
$errors = $entity->validate();
|
||||
$this->assertEqual(0, count($errors));
|
||||
|
||||
// Test with a mix of valid and invalid nodes.
|
||||
$unsaved_unpublished_node_title = $this->randomString();
|
||||
$unsaved_unpublished_node = Node::create([
|
||||
'title' => $unsaved_unpublished_node_title,
|
||||
'type' => 'node',
|
||||
'status' => NODE_NOT_PUBLISHED,
|
||||
]);
|
||||
|
||||
$saved_unpublished_node_title = $this->randomString();
|
||||
$saved_unpublished_node = Node::create([
|
||||
'title' => $saved_unpublished_node_title,
|
||||
'type' => 'node',
|
||||
'status' => NODE_NOT_PUBLISHED,
|
||||
]);
|
||||
$saved_unpublished_node->save();
|
||||
|
||||
$saved_published_node_title = $this->randomString();
|
||||
$saved_published_node = Node::create([
|
||||
'title' => $saved_published_node_title,
|
||||
'type' => 'node',
|
||||
'status' => NODE_PUBLISHED,
|
||||
]);
|
||||
$saved_published_node->save();
|
||||
|
||||
$entity = EntityTest::create([
|
||||
'field_test_node' => [
|
||||
[
|
||||
'entity' => $unsaved_unpublished_node,
|
||||
],
|
||||
[
|
||||
'target_id' => $saved_unpublished_node->id(),
|
||||
],
|
||||
[
|
||||
'target_id' => $saved_published_node->id(),
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$errors = $entity->validate();
|
||||
$this->assertEqual(2, count($errors));
|
||||
$this->assertEqual($errors[0]->getMessage(), new FormattableMarkup('This entity (%type: %label) cannot be referenced.', ['%type' => 'node', '%label' => $unsaved_unpublished_node_title]));
|
||||
$this->assertEqual($errors[0]->getPropertyPath(), 'field_test_node.0.entity');
|
||||
$this->assertEqual($errors[1]->getMessage(), new FormattableMarkup('This entity (%type: %label) cannot be referenced.', ['%type' => 'node', '%label' => $saved_unpublished_node->id()]));
|
||||
$this->assertEqual($errors[1]->getPropertyPath(), 'field_test_node.1.target_id');
|
||||
|
||||
// Publish one of the nodes and try again.
|
||||
$saved_unpublished_node->setPublished(TRUE);
|
||||
$saved_unpublished_node->save();
|
||||
$errors = $entity->validate();
|
||||
$this->assertEqual(1, count($errors));
|
||||
$this->assertEqual($errors[0]->getMessage(), new FormattableMarkup('This entity (%type: %label) cannot be referenced.', ['%type' => 'node', '%label' => $unsaved_unpublished_node_title]));
|
||||
$this->assertEqual($errors[0]->getPropertyPath(), 'field_test_node.0.entity');
|
||||
|
||||
// Publish the last invalid node and try again.
|
||||
$unsaved_unpublished_node->setPublished(TRUE);
|
||||
$errors = $entity->validate();
|
||||
$this->assertEqual(0, count($errors));
|
||||
|
||||
// Test with an unpublished and unsaved comment.
|
||||
$title = $this->randomString();
|
||||
$comment = Comment::create([
|
||||
|
|
|
@ -977,7 +977,12 @@ function file_tokens($type, $tokens, array $data, array $options, BubbleableMeta
|
|||
break;
|
||||
|
||||
case 'url':
|
||||
// Ideally, this would use file_url_transform_relative(), but because
|
||||
// tokens are also often used in e-mails, it's better to keep absolute
|
||||
// file URLs. The 'url.site' cache context is associated to ensure the
|
||||
// correct absolute URL is used in case of a multisite setup.
|
||||
$replacements[$original] = file_create_url($file->getFileUri());
|
||||
$bubbleable_metadata->addCacheContexts(['url.site']);
|
||||
break;
|
||||
|
||||
// These tokens are default variations on the chained tokens handled below.
|
||||
|
@ -1228,7 +1233,13 @@ function template_preprocess_file_link(&$variables) {
|
|||
$options = array();
|
||||
|
||||
$file_entity = ($file instanceof File) ? $file : File::load($file->fid);
|
||||
// @todo Wrap in file_url_transform_relative(). This is currently
|
||||
// impossible. As a work-around, we currently add the 'url.site' cache context
|
||||
// to ensure different file URLs are generated for different sites in a
|
||||
// multisite setup, including HTTP and HTTPS versions of the same site.
|
||||
// Fix in https://www.drupal.org/node/2646744.
|
||||
$url = file_create_url($file_entity->getFileUri());
|
||||
$variables['#cache']['contexts'][] = 'url.site';
|
||||
|
||||
$mime_type = $file->getMimeType();
|
||||
// Set options as per anchor format described at
|
||||
|
|
|
@ -70,6 +70,8 @@ class File extends ContentEntityBase implements FileInterface {
|
|||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see file_url_transform_relative()
|
||||
*/
|
||||
public function url($rel = 'canonical', $options = array()) {
|
||||
return file_create_url($this->getFileUri());
|
||||
|
|
|
@ -52,6 +52,8 @@ abstract class BaseFieldFileFormatterBase extends FormatterBase {
|
|||
$url = NULL;
|
||||
// Add support to link to the entity itself.
|
||||
if ($this->getSetting('link_to_file')) {
|
||||
// @todo Wrap in file_url_transform_relative(). This is currently
|
||||
// impossible. See below.
|
||||
$url = file_create_url($items->getEntity()->uri->value);
|
||||
}
|
||||
|
||||
|
@ -63,6 +65,16 @@ abstract class BaseFieldFileFormatterBase extends FormatterBase {
|
|||
'#type' => 'link',
|
||||
'#title' => $view_value,
|
||||
'#url' => Url::fromUri($url),
|
||||
// @todo Remove the 'url.site' cache context by using a relative file
|
||||
// URL (file_url_transform_relative()). This is currently impossible
|
||||
// because #type => link requires a Url object, and Url objects do not
|
||||
// support relative URLs: they require fully qualified URLs. Fix in
|
||||
// https://www.drupal.org/node/2646744.
|
||||
'#cache' => [
|
||||
'contexts' => [
|
||||
'url.site',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -55,6 +55,9 @@ class FileUriFormatter extends BaseFieldFileFormatterBase {
|
|||
protected function viewValue(FieldItemInterface $item) {
|
||||
$value = $item->value;
|
||||
if ($this->getSetting('file_download_path')) {
|
||||
// @todo Wrap in file_url_transform_relative(). This is currently
|
||||
// impossible. See BaseFieldFileFormatterBase::viewElements(). Fix in
|
||||
// https://www.drupal.org/node/2646744.
|
||||
$value = file_create_url($value);
|
||||
}
|
||||
return $value;
|
||||
|
|
|
@ -33,6 +33,9 @@ class RSSEnclosureFormatter extends FileFormatterBase {
|
|||
$entity->rss_elements[] = array(
|
||||
'key' => 'enclosure',
|
||||
'attributes' => array(
|
||||
// In RSS feeds, it is necessary to use absolute URLs. The 'url.site'
|
||||
// cache context is already associated with RSS feed responses, so it
|
||||
// does not need to be specified here.
|
||||
'url' => file_create_url($file->getFileUri()),
|
||||
'length' => $file->getSize(),
|
||||
'type' => $file->getMimeType(),
|
||||
|
|
|
@ -30,7 +30,7 @@ class UrlPlainFormatter extends FileFormatterBase {
|
|||
|
||||
foreach ($this->getEntitiesToView($items, $langcode) as $delta => $file) {
|
||||
$elements[$delta] = array(
|
||||
'#markup' => file_create_url($file->getFileUri()),
|
||||
'#markup' => file_url_transform_relative(file_create_url($file->getFileUri())),
|
||||
'#cache' => array(
|
||||
'tags' => $file->getCacheTags(),
|
||||
),
|
||||
|
|
|
@ -56,9 +56,11 @@ class FileFieldItemList extends EntityReferenceFieldItemList {
|
|||
$original_ids = array();
|
||||
$langcode = $this->getLangcode();
|
||||
$original = $entity->original;
|
||||
$original_items = $original->hasTranslation($langcode) ? $original->getTranslation($langcode)->{$field_name} : $original->{$field_name};
|
||||
foreach ($original_items as $item) {
|
||||
$original_ids[] = $item->target_id;
|
||||
if ($original->hasTranslation($langcode)) {
|
||||
$original_items = $original->getTranslation($langcode)->{$field_name};
|
||||
foreach ($original_items as $item) {
|
||||
$original_ids[] = $item->target_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement file usage by 1 for files that were removed from the field.
|
||||
|
|
|
@ -186,7 +186,6 @@ class FileWidget extends WidgetBase implements ContainerFactoryPluginInterface {
|
|||
$elements['#description'] = $description;
|
||||
$elements['#field_name'] = $field_name;
|
||||
$elements['#language'] = $items->getLangcode();
|
||||
$elements['#display_field'] = (bool) $this->getFieldSetting('display_field');
|
||||
// The field settings include defaults for the field type. However, this
|
||||
// widget is a base class for other widgets (e.g., ImageWidget) that may
|
||||
// act on field types without these expected settings.
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\file\Plugin\migrate\cckfield\FileField.
|
||||
* Contains \Drupal\file\Plugin\migrate\cckfield\d6\FileField.
|
||||
*/
|
||||
|
||||
namespace Drupal\file\Plugin\migrate\cckfield;
|
||||
namespace Drupal\file\Plugin\migrate\cckfield\d6;
|
||||
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\file\Plugin\migrate\cckfield\d7\FileField.
|
||||
*/
|
||||
|
||||
namespace Drupal\file\Plugin\migrate\cckfield\d7;
|
||||
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\cckfield\CckFieldPluginBase;
|
||||
|
||||
/**
|
||||
* @MigrateCckField(
|
||||
* id = "file",
|
||||
* )
|
||||
*/
|
||||
class FileField extends CckFieldPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldWidgetMap() {
|
||||
return [
|
||||
'filefield_widget' => 'file_generic',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldFormatterMap() {
|
||||
return [
|
||||
'default' => 'file_default',
|
||||
'url_plain' => 'file_url_plain',
|
||||
'path_plain' => 'file_url_plain',
|
||||
'image_plain' => 'image',
|
||||
'image_nodelink' => 'image',
|
||||
'image_imagelink' => 'image',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) {
|
||||
$process = [
|
||||
'plugin' => 'iterator',
|
||||
'source' => $field_name,
|
||||
'process' => [
|
||||
'target_id' => 'fid',
|
||||
'display' => 'display',
|
||||
'description' => 'description',
|
||||
],
|
||||
];
|
||||
$migration->mergeProcessOfProperty($field_name, $process);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldType(Row $row) {
|
||||
return $row->getSourceProperty('widget_type') == 'imagefield_widget' ? 'image' : 'file';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\file\Plugin\migrate\cckfield\d7\ImageField.
|
||||
*/
|
||||
|
||||
namespace Drupal\file\Plugin\migrate\cckfield\d7;
|
||||
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\cckfield\CckFieldPluginBase;
|
||||
|
||||
/**
|
||||
* @MigrateCckField(
|
||||
* id = "image"
|
||||
* )
|
||||
*/
|
||||
class ImageField extends CckFieldPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldFormatterMap() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) {
|
||||
$process = [
|
||||
'plugin' => 'iterator',
|
||||
'source' => $field_name,
|
||||
'process' => [
|
||||
'target_id' => 'fid',
|
||||
'alt' => 'alt',
|
||||
'title' => 'title',
|
||||
'width' => 'width',
|
||||
'height' => 'height',
|
||||
],
|
||||
];
|
||||
$migration->mergeProcessOfProperty($field_name, $process);
|
||||
}
|
||||
|
||||
}
|
|
@ -69,6 +69,12 @@ class File extends FieldPluginBase {
|
|||
protected function renderLink($data, ResultRow $values) {
|
||||
if (!empty($this->options['link_to_file']) && $data !== NULL && $data !== '') {
|
||||
$this->options['alter']['make_link'] = TRUE;
|
||||
// @todo Wrap in file_url_transform_relative(). This is currently
|
||||
// impossible. As a work-around, we could add the 'url.site' cache context
|
||||
// to ensure different file URLs are generated for different sites in a
|
||||
// multisite setup, including HTTP and HTTPS versions of the same site.
|
||||
// But unfortunately it's impossible to bubble a cache context here.
|
||||
// Fix in https://www.drupal.org/node/2646744.
|
||||
$this->options['alter']['path'] = file_create_url($this->getValue($values, 'uri'));
|
||||
}
|
||||
|
||||
|
|
208
core/modules/file/src/Tests/FileOnTranslatedEntityTest.php
Normal file
208
core/modules/file/src/Tests/FileOnTranslatedEntityTest.php
Normal file
|
@ -0,0 +1,208 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\file\Tests\FileOnTranslatedEntityTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\file\Tests;
|
||||
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Uploads files to translated nodes.
|
||||
*
|
||||
* @group file
|
||||
*/
|
||||
class FileOnTranslatedEntityTest extends FileFieldTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('language', 'content_translation');
|
||||
|
||||
/**
|
||||
* The name of the file field used in the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create the "Basic page" node type.
|
||||
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
|
||||
|
||||
// Create a file field on the "Basic page" node type.
|
||||
$this->fieldName = strtolower($this->randomMachineName());
|
||||
$this->createFileField($this->fieldName, 'node', 'page');
|
||||
|
||||
// Create and login user.
|
||||
$permissions = array(
|
||||
'access administration pages',
|
||||
'administer content translation',
|
||||
'administer content types',
|
||||
'administer languages',
|
||||
'create content translations',
|
||||
'create page content',
|
||||
'edit any page content',
|
||||
'translate any entity',
|
||||
'delete any page content',
|
||||
);
|
||||
$admin_user = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Add a second and third language.
|
||||
$edit = array();
|
||||
$edit['predefined_langcode'] = 'fr';
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
|
||||
|
||||
$edit = array();
|
||||
$edit['predefined_langcode'] = 'nl';
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
|
||||
|
||||
// Enable translation for "Basic page" nodes.
|
||||
$edit = array(
|
||||
'entity_types[node]' => 1,
|
||||
'settings[node][page][translatable]' => 1,
|
||||
"settings[node][page][fields][$this->fieldName]" => 1,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests synced file fields on translated nodes.
|
||||
*/
|
||||
public function testSyncedFiles() {
|
||||
// Verify that the file field on the "Basic page" node type is translatable.
|
||||
$definitions = \Drupal::entityManager()->getFieldDefinitions('node', 'page');
|
||||
$this->assertTrue($definitions[$this->fieldName]->isTranslatable(), 'Node file field is translatable.');
|
||||
|
||||
// Create a default language node.
|
||||
$default_language_node = $this->drupalCreateNode(array('type' => 'page', 'title' => 'Lost in translation'));
|
||||
|
||||
// Edit the node to upload a file.
|
||||
$edit = array();
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('text')[0]->uri);
|
||||
$this->drupalPostForm('node/' . $default_language_node->id() . '/edit', $edit, t('Save'));
|
||||
$first_fid = $this->getLastFileId();
|
||||
|
||||
// Translate the node into French: remove the existing file.
|
||||
$this->drupalPostForm('node/' . $default_language_node->id() . '/translations/add/en/fr', array(), t('Remove'));
|
||||
|
||||
// Upload a different file.
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = 'Bill Murray';
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('text')[1]->uri);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
// This inspects the HTML after the post of the translation, the file
|
||||
// should be displayed on the original node.
|
||||
$this->assertRaw('file--mime-text-plain');
|
||||
$second_fid = $this->getLastFileId();
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
|
||||
|
||||
/* @var $file \Drupal\file\FileInterface */
|
||||
|
||||
// Ensure the file status of the first file permanent.
|
||||
$file = File::load($first_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Ensure the file status of the second file is permanent.
|
||||
$file = File::load($second_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Translate the node into dutch: remove the existing file.
|
||||
$this->drupalPostForm('node/' . $default_language_node->id() . '/translations/add/en/nl', array(), t('Remove'));
|
||||
|
||||
// Upload a different file.
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = 'Scarlett Johansson';
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('text')[2]->uri);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
$third_fid = $this->getLastFileId();
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
|
||||
|
||||
// Ensure the first file is untouched.
|
||||
$file = File::load($first_fid);
|
||||
$this->assertTrue($file->isPermanent(), 'First file still exists and is permanent.');
|
||||
// This inspects the HTML after the post of the translation, the file
|
||||
// should be displayed on the original node.
|
||||
$this->assertRaw('file--mime-text-plain');
|
||||
|
||||
// Ensure the file status of the second file is permanent.
|
||||
$file = File::load($second_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Ensure the file status of the third file is permanent.
|
||||
$file = File::load($third_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Edit the second translation: remove the existing file.
|
||||
$this->drupalPostForm('fr/node/' . $default_language_node->id() . '/edit', array(), t('Remove'));
|
||||
|
||||
// Upload a different file.
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = 'David Bowie';
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('text')[3]->uri);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
$replaced_second_fid = $this->getLastFileId();
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
|
||||
|
||||
// Ensure the first and third files are untouched.
|
||||
$file = File::load($first_fid);
|
||||
$this->assertTrue($file->isPermanent(), 'First file still exists and is permanent.');
|
||||
|
||||
$file = File::load($third_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Ensure the file status of the replaced second file is permanent.
|
||||
$file = File::load($replaced_second_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Ensure the file status of the old second file is now temporary.
|
||||
$file = File::load($second_fid);
|
||||
$this->assertTrue($file->isTemporary());
|
||||
|
||||
// Delete the third translation.
|
||||
$this->drupalPostForm('nl/node/' . $default_language_node->id() . '/delete', array(), t('Delete Dutch translation'));
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
|
||||
|
||||
// Ensure the first and replaced second files are untouched.
|
||||
$file = File::load($first_fid);
|
||||
$this->assertTrue($file->isPermanent(), 'First file still exists and is permanent.');
|
||||
|
||||
$file = File::load($replaced_second_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Ensure the file status of the third file is now temporary.
|
||||
$file = File::load($third_fid);
|
||||
$this->assertTrue($file->isTemporary());
|
||||
|
||||
// Delete the all translations.
|
||||
$this->drupalPostForm('node/' . $default_language_node->id() . '/delete', array(), t('Delete all translations'));
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
|
||||
|
||||
// Ensure the file status of the all files are now temporary.
|
||||
$file = File::load($first_fid);
|
||||
$this->assertTrue($file->isTemporary(), 'First file still exists and is temporary.');
|
||||
|
||||
$file = File::load($replaced_second_fid);
|
||||
$this->assertTrue($file->isTemporary());
|
||||
}
|
||||
|
||||
}
|
|
@ -66,7 +66,8 @@ class FileTokenReplaceTest extends FileFieldTestBase {
|
|||
$metadata_tests['[file:path]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[file:mime]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[file:size]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[file:url]'] = $base_bubbleable_metadata;
|
||||
$bubbleable_metadata = clone $base_bubbleable_metadata;
|
||||
$metadata_tests['[file:url]'] = $bubbleable_metadata->addCacheContexts(['url.site']);
|
||||
$bubbleable_metadata = clone $base_bubbleable_metadata;
|
||||
$metadata_tests['[file:created]'] = $bubbleable_metadata->addCacheTags(['rendered']);
|
||||
$metadata_tests['[file:created:short]'] = $bubbleable_metadata;
|
||||
|
|
|
@ -72,16 +72,18 @@ class FilterCaption extends FilterBase {
|
|||
$altered_html = drupal_render($filter_caption);
|
||||
|
||||
// Load the altered HTML into a new DOMDocument and retrieve the element.
|
||||
$updated_node = Html::load($altered_html)->getElementsByTagName('body')
|
||||
$updated_nodes = Html::load($altered_html)->getElementsByTagName('body')
|
||||
->item(0)
|
||||
->childNodes
|
||||
->item(0);
|
||||
->childNodes;
|
||||
|
||||
// Import the updated node from the new DOMDocument into the original
|
||||
// one, importing also the child nodes of the updated node.
|
||||
$updated_node = $dom->importNode($updated_node, TRUE);
|
||||
// Finally, replace the original node with the new node.
|
||||
$node->parentNode->replaceChild($updated_node, $node);
|
||||
foreach ($updated_nodes as $updated_node) {
|
||||
// Import the updated node from the new DOMDocument into the original
|
||||
// one, importing also the child nodes of the updated node.
|
||||
$updated_node = $dom->importNode($updated_node, TRUE);
|
||||
$node->parentNode->insertBefore($updated_node, $node);
|
||||
}
|
||||
// Finally, remove the original data-caption node.
|
||||
$node->parentNode->removeChild($node);
|
||||
}
|
||||
|
||||
$result->setProcessedText(Html::serialize($dom))
|
||||
|
|
|
@ -49,7 +49,7 @@ class FilterHtml extends FilterBase {
|
|||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Allowed HTML tags'),
|
||||
'#default_value' => $this->settings['allowed_html'],
|
||||
'#maxlength' => 1024,
|
||||
'#maxlength' => 2048,
|
||||
'#description' => $this->t('A list of HTML tags that can be used. By default only the <em>lang</em> and <em>dir</em> attributes are allowed for all HTML tags. Each HTML tag may have attributes which are treated as allowed attribute names for that HTML tag. Each attribute may allow all values, or only allow specific values. Attribute names or values may be written as a prefix and wildcard like <em>jump-*</em>. JavaScript event attributes, JavaScript URLs, and CSS are always stripped.'),
|
||||
'#size' => 250,
|
||||
'#attached' => array(
|
||||
|
|
109
core/modules/filter/src/Tests/FilterCaptionTwigDebugTest.php
Normal file
109
core/modules/filter/src/Tests/FilterCaptionTwigDebugTest.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\filter\Tests\FilterCaptionTwigDebugTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\filter\Tests;
|
||||
|
||||
use Drupal\Core\Render\RenderContext;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\filter\FilterPluginCollection;
|
||||
|
||||
/**
|
||||
* Tests the caption filter with Twig debugging on.
|
||||
*
|
||||
* @group filter
|
||||
*/
|
||||
class FilterCaptionTwigDebugTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['system', 'filter'];
|
||||
|
||||
/**
|
||||
* @var \Drupal\filter\Plugin\FilterInterface[]
|
||||
*/
|
||||
protected $filters;
|
||||
|
||||
/**
|
||||
* Enables Twig debugging.
|
||||
*/
|
||||
protected function debugOn() {
|
||||
// Enable debug, rebuild the service container, and clear all caches.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
if (!$parameters['debug']) {
|
||||
$parameters['debug'] = TRUE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
$this->resetAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables Twig debugging.
|
||||
*/
|
||||
protected function debugOff() {
|
||||
// Disable debug, rebuild the service container, and clear all caches.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
if ($parameters['debug']) {
|
||||
$parameters['debug'] = FALSE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
$this->resetAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->debugOn();
|
||||
|
||||
$manager = $this->container->get('plugin.manager.filter');
|
||||
$bag = new FilterPluginCollection($manager, []);
|
||||
$this->filters = $bag->getAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function tearDown() {
|
||||
$this->debugOff();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the caption filter with Twig debugging on.
|
||||
*/
|
||||
function testCaptionFilter() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = \Drupal::service('renderer');
|
||||
$filter = $this->filters['filter_caption'];
|
||||
|
||||
$test = function ($input) use ($filter, $renderer) {
|
||||
return $renderer->executeInRenderContext(new RenderContext(), function () use ($input, $filter) {
|
||||
return $filter->process($input, 'und');
|
||||
});
|
||||
};
|
||||
|
||||
// No data-caption attribute.
|
||||
$input = '<img src="llama.jpg" />';
|
||||
$expected = $input;
|
||||
$this->assertIdentical($expected, $test($input)->getProcessedText());
|
||||
|
||||
// Data-caption attribute.
|
||||
$input = '<img src="llama.jpg" data-caption="Loquacious llama!" />';
|
||||
$expected = '<img src="llama.jpg" /><figcaption>Loquacious llama!</figcaption>';
|
||||
$output = $test($input);
|
||||
$output = $output->getProcessedText();
|
||||
$this->assertTrue(strpos($output, $expected) !== FALSE, "\"$output\" contains \"$expected\"");
|
||||
$this->assertTrue(strpos($output, '<!-- THEME HOOK: \'filter_caption\' -->') !== FALSE, 'filter_caption theme hook debug comment is present.');
|
||||
}
|
||||
|
||||
}
|
|
@ -501,7 +501,12 @@ function template_preprocess_forums(&$variables) {
|
|||
}
|
||||
|
||||
$row[] = array(
|
||||
'data' => $topic->comment_count . $new_replies,
|
||||
'data' => [
|
||||
[
|
||||
'#prefix' => $topic->comment_count,
|
||||
'#markup' => $new_replies,
|
||||
],
|
||||
],
|
||||
'class' => array('forum__replies'),
|
||||
);
|
||||
$row[] = array(
|
||||
|
|
|
@ -514,6 +514,9 @@ class ForumTest extends WebTestBase {
|
|||
// Check that forum renders properly.
|
||||
$this->drupalGet("forum/{$this->forum['tid']}");
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Verify there is no unintentional HTML tag escaping.
|
||||
$this->assertNoEscaped('<', '');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,7 +34,7 @@ function template_preprocess_image_style_preview(&$variables) {
|
|||
$original_path = \Drupal::config('image.settings')->get('preview_image');
|
||||
$original_image = $image_factory->get($original_path);
|
||||
$variables['original'] = array(
|
||||
'url' => file_create_url($original_path),
|
||||
'url' => file_url_transform_relative(file_create_url($original_path)),
|
||||
'width' => $original_image->getWidth(),
|
||||
'height' => $original_image->getHeight(),
|
||||
);
|
||||
|
@ -55,7 +55,7 @@ function template_preprocess_image_style_preview(&$variables) {
|
|||
}
|
||||
$preview_image = $image_factory->get($preview_file);
|
||||
$variables['derivative'] = array(
|
||||
'url' => file_create_url($preview_file),
|
||||
'url' => file_url_transform_relative(file_create_url($preview_file)),
|
||||
'width' => $preview_image->getWidth(),
|
||||
'height' => $preview_image->getHeight(),
|
||||
);
|
||||
|
|
22
core/modules/image/image.post_update.php
Normal file
22
core/modules/image/image.post_update.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Post-update functions for Image.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
|
||||
/**
|
||||
* Saves the image style dependencies into form and view display entities.
|
||||
*/
|
||||
function image_post_update_image_style_dependencies() {
|
||||
// Merge view and form displays. Use array_values() to avoid key collisions.
|
||||
$displays = array_merge(array_values(EntityViewDisplay::loadMultiple()), array_values(EntityFormDisplay::loadMultiple()));
|
||||
/** @var \Drupal\Core\Entity\Display\EntityDisplayInterface[] $displays */
|
||||
foreach ($displays as $display) {
|
||||
// Re-save each config entity to add missed dependencies.
|
||||
$display->save();
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
|||
* "flush" = "Drupal\image\Form\ImageStyleFlushForm"
|
||||
* },
|
||||
* "list_builder" = "Drupal\image\ImageStyleListBuilder",
|
||||
* "storage" = "Drupal\image\ImageStyleStorage",
|
||||
* },
|
||||
* admin_permission = "administer image styles",
|
||||
* config_prefix = "style",
|
||||
|
@ -58,13 +59,6 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
|||
*/
|
||||
class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, EntityWithPluginCollectionInterface {
|
||||
|
||||
/**
|
||||
* The name of the image style to use as replacement upon delete.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $replacementID;
|
||||
|
||||
/**
|
||||
* The name of the image style.
|
||||
*
|
||||
|
@ -128,17 +122,13 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
|
|||
public static function postDelete(EntityStorageInterface $storage, array $entities) {
|
||||
parent::postDelete($storage, $entities);
|
||||
|
||||
/** @var \Drupal\image\ImageStyleInterface[] $entities */
|
||||
foreach ($entities as $style) {
|
||||
// Flush cached media for the deleted style.
|
||||
$style->flush();
|
||||
// Check whether field settings need to be updated.
|
||||
// In case no replacement style was specified, all image fields that are
|
||||
// using the deleted style are left in a broken state.
|
||||
if (!$style->isSyncing() && $new_id = $style->getReplacementID()) {
|
||||
// The deleted ID is still set as originalID.
|
||||
$style->setName($new_id);
|
||||
static::replaceImageStyle($style);
|
||||
}
|
||||
// Clear the replacement ID, if one has been previously stored.
|
||||
/** @var \Drupal\image\ImageStyleStorageInterface $storage */
|
||||
$storage->clearReplacementId($style->id());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,7 +370,9 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getReplacementID() {
|
||||
return $this->get('replacementID');
|
||||
/** @var \Drupal\image\ImageStyleStorageInterface $storage */
|
||||
$storage = $this->entityTypeManager()->getStorage($this->getEntityTypeId());
|
||||
return $storage->getReplacementId($this->id());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,13 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
*/
|
||||
class ImageStyleDeleteForm extends EntityDeleteForm {
|
||||
|
||||
/**
|
||||
* Replacement options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $replacementOptions;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -25,20 +32,28 @@ class ImageStyleDeleteForm extends EntityDeleteForm {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->t('If this style is in use on the site, you may select another style to replace it. All images that have been generated for this style will be permanently deleted.');
|
||||
if (count($this->getReplacementOptions()) > 1) {
|
||||
return $this->t('If this style is in use on the site, you may select another style to replace it. All images that have been generated for this style will be permanently deleted. If no replacement style is selected, the dependent configurations might need manual reconfiguration.');
|
||||
}
|
||||
return $this->t('All images that have been generated for this style will be permanently deleted. The dependent configurations might need manual reconfiguration.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$replacement_styles = array_diff_key(image_style_options(), array($this->entity->id() => ''));
|
||||
$form['replacement'] = array(
|
||||
'#title' => $this->t('Replacement style'),
|
||||
'#type' => 'select',
|
||||
'#options' => $replacement_styles,
|
||||
'#empty_option' => $this->t('No replacement, just delete'),
|
||||
);
|
||||
$replacement_styles = $this->getReplacementOptions();
|
||||
// If there are non-empty options in the list, allow the user to optionally
|
||||
// pick up a replacement.
|
||||
if (count($replacement_styles) > 1) {
|
||||
$form['replacement'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Replacement style'),
|
||||
'#options' => $replacement_styles,
|
||||
'#empty_option' => $this->t('- No replacement -'),
|
||||
'#weight' => -5,
|
||||
];
|
||||
}
|
||||
|
||||
return parent::form($form, $form_state);
|
||||
}
|
||||
|
@ -47,9 +62,27 @@ class ImageStyleDeleteForm extends EntityDeleteForm {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->entity->set('replacementID', $form_state->getValue('replacement'));
|
||||
|
||||
// Save a selected replacement in the image style storage. It will be used
|
||||
// later, in the same request, when resolving dependencies.
|
||||
if ($replacement = $form_state->getValue('replacement')) {
|
||||
/** @var \Drupal\image\ImageStyleStorageInterface $storage */
|
||||
$storage = $this->entityTypeManager->getStorage($this->entity->getEntityTypeId());
|
||||
$storage->setReplacementId($this->entity->id(), $replacement);
|
||||
}
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of image style replacement options.
|
||||
*
|
||||
* @return array
|
||||
* An option list suitable for the form select '#options'.
|
||||
*/
|
||||
protected function getReplacementOptions() {
|
||||
if (!isset($this->replacementOptions)) {
|
||||
$this->replacementOptions = array_diff_key(image_style_options(), [$this->getEntity()->id() => '']);
|
||||
}
|
||||
return $this->replacementOptions;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,8 +17,14 @@ interface ImageStyleInterface extends ConfigEntityInterface {
|
|||
/**
|
||||
* Returns the replacement ID.
|
||||
*
|
||||
* @return string
|
||||
* The name of the image style to use as replacement upon delete.
|
||||
* @return string|null
|
||||
* The replacement image style ID or NULL if no replacement has been
|
||||
* selected.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x, will be removed before Drupal 9.0.x. Use
|
||||
* \Drupal\image\ImageStyleStorageInterface::getReplacementId() instead.
|
||||
*
|
||||
* @see \Drupal\image\ImageStyleStorageInterface::getReplacementId()
|
||||
*/
|
||||
public function getReplacementID();
|
||||
|
||||
|
@ -70,6 +76,7 @@ interface ImageStyleInterface extends ConfigEntityInterface {
|
|||
* in an <img> tag. Requesting the URL will cause the image to be created.
|
||||
*
|
||||
* @see \Drupal\image\Controller\ImageStyleDownloadController::deliver()
|
||||
* @see file_url_transform_relative()
|
||||
*/
|
||||
public function buildUrl($path, $clean_urls = NULL);
|
||||
|
||||
|
|
|
@ -91,9 +91,9 @@ class ImageStyleListBuilder extends ConfigEntityListBuilder {
|
|||
*/
|
||||
public function render() {
|
||||
$build = parent::render();
|
||||
$build['#empty'] = $this->t('There are currently no styles. <a href=":url">Add a new one</a>.', array(
|
||||
$build['table']['#empty'] = $this->t('There are currently no styles. <a href=":url">Add a new one</a>.', [
|
||||
':url' => $this->urlGenerator->generateFromRoute('image.style_add'),
|
||||
));
|
||||
]);
|
||||
return $build;
|
||||
}
|
||||
|
||||
|
|
51
core/modules/image/src/ImageStyleStorage.php
Normal file
51
core/modules/image/src/ImageStyleStorage.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\image\ImageStyleStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\image;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
||||
|
||||
/**
|
||||
* Storage controller class for "image style" configuration entities.
|
||||
*/
|
||||
class ImageStyleStorage extends ConfigEntityStorage implements ImageStyleStorageInterface {
|
||||
|
||||
/**
|
||||
* Image style replacement memory storage.
|
||||
*
|
||||
* This value is not stored in the backend. It's used during the deletion of
|
||||
* an image style to save the replacement image style in the same request. The
|
||||
* value is used later, when resolving dependencies.
|
||||
*
|
||||
* @var string[]
|
||||
*
|
||||
* @see \Drupal\image\Form\ImageStyleDeleteForm::submitForm()
|
||||
*/
|
||||
protected $replacement = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setReplacementId($name, $replacement) {
|
||||
$this->replacement[$name] = $replacement;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getReplacementId($name) {
|
||||
return isset($this->replacement[$name]) ? $this->replacement[$name] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearReplacementId($name) {
|
||||
unset($this->replacement[$name]);
|
||||
}
|
||||
|
||||
}
|
57
core/modules/image/src/ImageStyleStorageInterface.php
Normal file
57
core/modules/image/src/ImageStyleStorageInterface.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\image\ImageStyleStorageInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\image;
|
||||
|
||||
/**
|
||||
* Interface for storage controller for "image style" configuration entities.
|
||||
*/
|
||||
interface ImageStyleStorageInterface {
|
||||
|
||||
/**
|
||||
* Stores a replacement ID for an image style being deleted.
|
||||
*
|
||||
* The method stores a replacement style to be used by the configuration
|
||||
* dependency system when a image style is deleted. The replacement style is
|
||||
* replacing the deleted style in other configuration entities that are
|
||||
* depending on the image style being deleted.
|
||||
*
|
||||
* @param string $name
|
||||
* The ID of the image style to be deleted.
|
||||
* @param string $replacement
|
||||
* The ID of the image style used as replacement.
|
||||
*/
|
||||
public function setReplacementId($name, $replacement);
|
||||
|
||||
/**
|
||||
* Retrieves the replacement ID of a deleted image style.
|
||||
*
|
||||
* The method is retrieving the value stored by ::setReplacementId().
|
||||
*
|
||||
* @param string $name
|
||||
* The ID of the image style to be replaced.
|
||||
*
|
||||
* @return string|null
|
||||
* The ID of the image style used as replacement, if there's any, or NULL.
|
||||
*
|
||||
* @see \Drupal\image\ImageStyleStorageInterface::setReplacementId()
|
||||
*/
|
||||
public function getReplacementId($name);
|
||||
|
||||
/**
|
||||
* Clears a replacement ID from the storage.
|
||||
*
|
||||
* The method clears the value previously stored with ::setReplacementId().
|
||||
*
|
||||
* @param string $name
|
||||
* The ID of the image style to be replaced.
|
||||
*
|
||||
* @see \Drupal\image\ImageStyleStorageInterface::setReplacementId()
|
||||
*/
|
||||
public function clearReplacementId($name);
|
||||
|
||||
}
|
|
@ -14,6 +14,7 @@ use Drupal\Core\Link;
|
|||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
|
@ -41,7 +42,7 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi
|
|||
/**
|
||||
* The image style entity storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
* @var \Drupal\image\ImageStyleStorageInterface
|
||||
*/
|
||||
protected $imageStyleStorage;
|
||||
|
||||
|
@ -199,9 +200,16 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi
|
|||
}
|
||||
|
||||
foreach ($files as $delta => $file) {
|
||||
$cache_contexts = array();
|
||||
if (isset($link_file)) {
|
||||
$image_uri = $file->getFileUri();
|
||||
// @todo Wrap in file_url_transform_relative(). This is currently
|
||||
// impossible. As a work-around, we currently add the 'url.site' cache
|
||||
// context to ensure different file URLs are generated for different
|
||||
// sites in a multisite setup, including HTTP and HTTPS versions of the
|
||||
// same site. Fix in https://www.drupal.org/node/2646744.
|
||||
$url = Url::fromUri(file_create_url($image_uri));
|
||||
$cache_contexts[] = 'url.site';
|
||||
}
|
||||
$cache_tags = Cache::mergeTags($cache_tags, $file->getCacheTags());
|
||||
|
||||
|
@ -219,6 +227,7 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi
|
|||
'#url' => $url,
|
||||
'#cache' => array(
|
||||
'tags' => $cache_tags,
|
||||
'contexts' => $cache_contexts,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -226,4 +235,41 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi
|
|||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$dependencies = parent::calculateDependencies();
|
||||
$style_id = $this->getSetting('image_style');
|
||||
/** @var \Drupal\image\ImageStyleInterface $style */
|
||||
if ($style_id && $style = ImageStyle::load($style_id)) {
|
||||
// If this formatter uses a valid image style to display the image, add
|
||||
// the image style configuration entity as dependency of this formatter.
|
||||
$dependencies[$style->getConfigDependencyKey()][] = $style->getConfigDependencyName();
|
||||
}
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onDependencyRemoval(array $dependencies) {
|
||||
$changed = parent::onDependencyRemoval($dependencies);
|
||||
$style_id = $this->getSetting('image_style');
|
||||
/** @var \Drupal\image\ImageStyleInterface $style */
|
||||
if ($style_id && $style = ImageStyle::load($style_id)) {
|
||||
if (!empty($dependencies[$style->getConfigDependencyKey()][$style->getConfigDependencyName()])) {
|
||||
$replacement_id = $this->imageStyleStorage->getReplacementId($style_id);
|
||||
// If a valid replacement has been provided in the storage, replace the
|
||||
// image style with the replacement and signal that the formatter plugin
|
||||
// settings were updated.
|
||||
if ($replacement_id && ImageStyle::load($replacement_id)) {
|
||||
$this->setSetting('image_style', $replacement_id);
|
||||
$changed = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $changed;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use Drupal\Component\Utility\NestedArray;
|
|||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\file\Plugin\Field\FieldWidget\FileWidget;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'image_image' widget.
|
||||
|
@ -273,4 +274,49 @@ class ImageWidget extends FileWidget {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$dependencies = parent::calculateDependencies();
|
||||
$style_id = $this->getSetting('preview_image_style');
|
||||
/** @var \Drupal\image\ImageStyleInterface $style */
|
||||
if ($style_id && $style = ImageStyle::load($style_id)) {
|
||||
// If this widget uses a valid image style to display the preview of the
|
||||
// uploaded image, add that image style configuration entity as dependency
|
||||
// of this widget.
|
||||
$dependencies[$style->getConfigDependencyKey()][] = $style->getConfigDependencyName();
|
||||
}
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onDependencyRemoval(array $dependencies) {
|
||||
$changed = parent::onDependencyRemoval($dependencies);
|
||||
$style_id = $this->getSetting('preview_image_style');
|
||||
/** @var \Drupal\image\ImageStyleInterface $style */
|
||||
if ($style_id && $style = ImageStyle::load($style_id)) {
|
||||
if (!empty($dependencies[$style->getConfigDependencyKey()][$style->getConfigDependencyName()])) {
|
||||
/** @var \Drupal\image\ImageStyleStorageInterface $storage */
|
||||
$storage = \Drupal::entityManager()->getStorage($style->getEntityTypeId());
|
||||
$replacement_id = $storage->getReplacementId($style_id);
|
||||
// If a valid replacement has been provided in the storage, replace the
|
||||
// preview image style with the replacement.
|
||||
if ($replacement_id && ImageStyle::load($replacement_id)) {
|
||||
$this->setSetting('preview_image_style', $replacement_id);
|
||||
}
|
||||
// If there's no replacement or the replacement is invalid, disable the
|
||||
// image preview.
|
||||
else {
|
||||
$this->setSetting('preview_image_style', '');
|
||||
}
|
||||
// Signal that the formatter plugin settings were updated.
|
||||
$changed = TRUE;
|
||||
}
|
||||
}
|
||||
return $changed;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\image\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\image\ImageStyleInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
@ -273,6 +274,19 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
|||
|
||||
$this->assertFalse(ImageStyle::load($style_name), format_string('Image style %style successfully deleted.', array('%style' => $style->label())));
|
||||
|
||||
// Test empty text when there are no image styles.
|
||||
|
||||
// Delete all image styles.
|
||||
foreach (ImageStyle::loadMultiple() as $image_style) {
|
||||
$image_style->delete();
|
||||
}
|
||||
|
||||
// Confirm that the empty text is correct on the image styles page.
|
||||
$this->drupalGet($admin_path);
|
||||
$this->assertRaw(t('There are currently no styles. <a href=":url">Add a new one</a>.', [
|
||||
':url' => \Drupal::url('image.style_add'),
|
||||
]));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -307,7 +321,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
|||
|
||||
// Test that image is displayed using newly created style.
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertRaw($style->buildUrl($original_uri), format_string('Image displayed using style @style.', array('@style' => $style_name)));
|
||||
$this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri)), format_string('Image displayed using style @style.', array('@style' => $style_name)));
|
||||
|
||||
// Rename the style and make sure the image field is updated.
|
||||
$new_style_name = strtolower($this->randomMachineName(10));
|
||||
|
@ -322,7 +336,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
|||
|
||||
// Reload the image style using the new name.
|
||||
$style = ImageStyle::load($new_style_name);
|
||||
$this->assertRaw($style->buildUrl($original_uri), 'Image displayed using style replacement style.');
|
||||
$this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri)), 'Image displayed using style replacement style.');
|
||||
|
||||
// Delete the style and choose a replacement style.
|
||||
$edit = array(
|
||||
|
@ -334,7 +348,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
|||
|
||||
$replacement_style = ImageStyle::load('thumbnail');
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertRaw($replacement_style->buildUrl($original_uri), 'Image displayed using style replacement style.');
|
||||
$this->assertRaw(file_url_transform_relative($replacement_style->buildUrl($original_uri)), 'Image displayed using style replacement style.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -441,11 +455,16 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
|
|||
|
||||
// Test that image is displayed using newly created style.
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertRaw($style->buildUrl($original_uri), format_string('Image displayed using style @style.', array('@style' => $style_name)));
|
||||
$this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri)), format_string('Image displayed using style @style.', array('@style' => $style_name)));
|
||||
|
||||
// Copy config to sync, and delete the image style.
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$active = $this->container->get('config.storage');
|
||||
// Remove the image field from the display, to avoid a dependency error
|
||||
// during import.
|
||||
EntityViewDisplay::load('node.article.default')
|
||||
->removeComponent($field_name)
|
||||
->save();
|
||||
$this->copyConfig($active, $sync);
|
||||
$sync->delete('image.style.' . $style_name);
|
||||
$this->configImporter()->import();
|
||||
|
|
|
@ -41,7 +41,7 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$style = entity_create('image_style', array('name' => 'test', 'label' => 'Test'));
|
||||
$style->save();
|
||||
$generated_uri = 'public://styles/test/public/'. \Drupal::service('file_system')->basename($original_uri);
|
||||
$url = $style->buildUrl($original_uri);
|
||||
$url = file_url_transform_relative($style->buildUrl($original_uri));
|
||||
|
||||
$variables = array(
|
||||
'#theme' => 'image_style',
|
||||
|
@ -70,7 +70,7 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="120" height="60" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($url);
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
|
@ -91,7 +91,7 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="60" height="120" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($url);
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
|
@ -113,7 +113,7 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="45" height="90" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($url);
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
|
@ -135,7 +135,7 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="45" height="90" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($url);
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
|
@ -153,7 +153,7 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="45" height="90" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($url);
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
|
@ -174,7 +174,7 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($url);
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
|
||||
|
@ -194,7 +194,7 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="30" height="30" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($url);
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
|
@ -215,7 +215,7 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($url);
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
|
||||
|
@ -251,10 +251,10 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
];
|
||||
// PNG original image. Should be resized to 100x100.
|
||||
$generated_uri = 'public://styles/test_uri/public/'. \Drupal::service('file_system')->basename($original_uri);
|
||||
$url = $style->buildUrl($original_uri);
|
||||
$url = file_url_transform_relative($style->buildUrl($original_uri));
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="100" height="100" alt="" class="image-style-test-uri" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($url);
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
|
@ -264,11 +264,11 @@ class ImageDimensionsTest extends WebTestBase {
|
|||
$file = $files[1];
|
||||
$original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME);
|
||||
$generated_uri = 'public://styles/test_uri/public/'. \Drupal::service('file_system')->basename($original_uri);
|
||||
$url = $style->buildUrl($original_uri);
|
||||
$url = file_url_transform_relative($style->buildUrl($original_uri));
|
||||
$variables['#uri'] = $original_uri;
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="50" height="50" alt="" class="image-style-test-uri" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($url);
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
|
|
|
@ -127,6 +127,8 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
$default_output = '<a href="' . file_create_url($image_uri) . '">' . $renderer->renderRoot($image) . '</a>';
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertCacheTag($file->getCacheTags()[0]);
|
||||
// @todo Remove in https://www.drupal.org/node/2646744.
|
||||
$this->assertCacheContext('url.site');
|
||||
$cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertTrue(!preg_match('/ image_style\:/', $cache_tags_header), 'No image style cache tag found.');
|
||||
$this->assertRaw($default_output, 'Image linked to file formatter displaying correctly on full node view.');
|
||||
|
@ -165,7 +167,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
'//a[@href=:path]/img[@src=:url and @alt=:alt and @width=:width and @height=:height]',
|
||||
array(
|
||||
':path' => $node->url(),
|
||||
':url' => file_create_url($image['#uri']),
|
||||
':url' => file_url_transform_relative(file_create_url($image['#uri'])),
|
||||
':width' => $image['#width'],
|
||||
':height' => $image['#height'],
|
||||
':alt' => $alt,
|
||||
|
@ -256,7 +258,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
$node = $node_storage->load($nid);
|
||||
$file = $node->{$field_name}->entity;
|
||||
|
||||
$url = file_create_url(ImageStyle::load('medium')->buildUrl($file->getFileUri()));
|
||||
$url = file_url_transform_relative(file_create_url(ImageStyle::load('medium')->buildUrl($file->getFileUri())));
|
||||
$this->assertTrue($this->cssSelect('img[width=40][height=20][class=image-style-medium][src="' . $url . '"]'));
|
||||
|
||||
// Add alt/title fields to the image and verify that they are displayed.
|
||||
|
|
|
@ -66,9 +66,11 @@ abstract class ImageFieldTestBase extends WebTestBase {
|
|||
* @param array $field_settings
|
||||
* A list of instance settings that will be added to the instance defaults.
|
||||
* @param array $widget_settings
|
||||
* A list of widget settings that will be added to the widget defaults.
|
||||
* Widget settings to be added to the widget defaults.
|
||||
* @param array $formatter_settings
|
||||
* Formatter settings to be added to the formatter defaults.
|
||||
*/
|
||||
function createImageField($name, $type_name, $storage_settings = array(), $field_settings = array(), $widget_settings = array()) {
|
||||
function createImageField($name, $type_name, $storage_settings = array(), $field_settings = array(), $widget_settings = array(), $formatter_settings = array()) {
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $name,
|
||||
'entity_type' => 'node',
|
||||
|
@ -95,7 +97,10 @@ abstract class ImageFieldTestBase extends WebTestBase {
|
|||
->save();
|
||||
|
||||
entity_get_display('node', $type_name, 'default')
|
||||
->setComponent($name)
|
||||
->setComponent($name, array(
|
||||
'type' => 'image',
|
||||
'settings' => $formatter_settings,
|
||||
))
|
||||
->save();
|
||||
|
||||
return $field_config;
|
||||
|
|
86
core/modules/image/src/Tests/ImageStyleDeleteTest.php
Normal file
86
core/modules/image/src/Tests/ImageStyleDeleteTest.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\image\Tests\ImageStyleDeleteTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\image\Tests;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
|
||||
/**
|
||||
* Tests image style deletion using the UI.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageStyleDeleteTest extends ImageFieldTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create an image field 'foo' having the image style 'medium' as widget
|
||||
// preview and as formatter.
|
||||
$this->createImageField('foo', 'page', [], [], ['preview_image_style' => 'medium'], ['image_style' => 'medium']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image style deletion.
|
||||
*/
|
||||
public function testDelete() {
|
||||
$this->drupalGet('admin/config/media/image-styles/manage/medium/delete');
|
||||
// Checks that the 'replacement' select element is displayed.
|
||||
$this->assertFieldByName('replacement');
|
||||
// Checks that UI messages are correct.
|
||||
$this->assertRaw(t('If this style is in use on the site, you may select another style to replace it. All images that have been generated for this style will be permanently deleted. If no replacement style is selected, the dependent configurations might need manual reconfiguration.'));
|
||||
$this->assertNoRaw(t('All images that have been generated for this style will be permanently deleted. The dependent configurations might need manual reconfiguration.'));
|
||||
|
||||
// Delete 'medium' image style but replace it with 'thumbnail'. This style
|
||||
// is involved in 'node.page.default' display view and form.
|
||||
$this->drupalPostForm(NULL, ['replacement' => 'thumbnail'], t('Delete'));
|
||||
|
||||
/** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $view_display */
|
||||
$view_display = EntityViewDisplay::load('node.page.default');
|
||||
// Checks that the formatter setting is replaced.
|
||||
if ($this->assertNotNull($component = $view_display->getComponent('foo'))) {
|
||||
$this->assertIdentical($component['settings']['image_style'], 'thumbnail');
|
||||
}
|
||||
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
|
||||
$form_display = EntityFormDisplay::load('node.page.default');
|
||||
// Check that the widget setting is replaced.
|
||||
if ($this->assertNotNull($component = $form_display->getComponent('foo'))) {
|
||||
$this->assertIdentical($component['settings']['preview_image_style'], 'thumbnail');
|
||||
}
|
||||
|
||||
$this->drupalGet('admin/config/media/image-styles/manage/thumbnail/delete');
|
||||
// Checks that the 'replacement' select element is displayed.
|
||||
$this->assertFieldByName('replacement');
|
||||
// Checks that UI messages are correct.
|
||||
$this->assertRaw(t('If this style is in use on the site, you may select another style to replace it. All images that have been generated for this style will be permanently deleted. If no replacement style is selected, the dependent configurations might need manual reconfiguration.'));
|
||||
$this->assertNoRaw(t('All images that have been generated for this style will be permanently deleted. The dependent configurations might need manual reconfiguration.'));
|
||||
|
||||
// Delete 'thumbnail' image style. Provide no replacement.
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
|
||||
$view_display = EntityViewDisplay::load('node.page.default');
|
||||
// Checks that the formatter setting is disabled.
|
||||
$this->assertNull($view_display->getComponent('foo'));
|
||||
$this->assertNotNull($view_display->get('hidden')['foo']);
|
||||
// Checks that widget setting is preserved with the image preview disabled.
|
||||
$form_display = EntityFormDisplay::load('node.page.default');
|
||||
$this->assertNotNull($widget = $form_display->getComponent('foo'));
|
||||
$this->assertIdentical($widget['settings']['preview_image_style'], '');
|
||||
|
||||
// Now, there's only one image style configured on the system: 'large'.
|
||||
$this->drupalGet('admin/config/media/image-styles/manage/large/delete');
|
||||
// Checks that the 'replacement' select element is not displayed.
|
||||
$this->assertNoFieldByName('replacement');
|
||||
// Checks that UI messages are correct.
|
||||
$this->assertNoRaw(t('If this style is in use on the site, you may select another style to replace it. All images that have been generated for this style will be permanently deleted. If no replacement style is selected, the dependent configurations might need manual reconfiguration.'));
|
||||
$this->assertRaw(t('All images that have been generated for this style will be permanently deleted. The dependent configurations might need manual reconfiguration.'));
|
||||
}
|
||||
|
||||
}
|
|
@ -74,7 +74,7 @@ class ImageThemeFunctionTest extends WebTestBase {
|
|||
// Create a style.
|
||||
$style = entity_create('image_style', array('name' => 'test', 'label' => 'Test'));
|
||||
$style->save();
|
||||
$url = $style->buildUrl($original_uri);
|
||||
$url = file_url_transform_relative($style->buildUrl($original_uri));
|
||||
|
||||
// Create a test entity with the image field set.
|
||||
$entity = entity_create('entity_test');
|
||||
|
@ -136,7 +136,7 @@ class ImageThemeFunctionTest extends WebTestBase {
|
|||
// Create a style.
|
||||
$style = entity_create('image_style', array('name' => 'image_test', 'label' => 'Test'));
|
||||
$style->save();
|
||||
$url = $style->buildUrl($original_uri);
|
||||
$url = file_url_transform_relative($style->buildUrl($original_uri));
|
||||
|
||||
// Create the base element that we'll use in the tests below.
|
||||
$base_element = array(
|
||||
|
|
59
core/modules/image/src/Tests/Update/ImageUpdateTest.php
Normal file
59
core/modules/image/src/Tests/Update/ImageUpdateTest.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\image\Tests\Update\ImageUpdateTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\image\Tests\Update;
|
||||
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests Image update path.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image_post_update_image_style_dependencies().
|
||||
*
|
||||
* @see image_post_update_image_style_dependencies()
|
||||
*/
|
||||
public function testPostUpdateImageStylesDependencies() {
|
||||
$view = 'core.entity_view_display.node.article.default';
|
||||
$form = 'core.entity_form_display.node.article.default';
|
||||
|
||||
// Check that view display 'node.article.default' doesn't depend on image
|
||||
// style 'image.style.large'.
|
||||
$dependencies = $this->config($view)->get('dependencies.config');
|
||||
$this->assertFalse(in_array('image.style.large', $dependencies));
|
||||
// Check that form display 'node.article.default' doesn't depend on image
|
||||
// style 'image.style.thumbnail'.
|
||||
$dependencies = $this->config($form)->get('dependencies.config');
|
||||
$this->assertFalse(in_array('image.style.thumbnail', $dependencies));
|
||||
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
||||
// Check that view display 'node.article.default' depend on image style
|
||||
// 'image.style.large'.
|
||||
$dependencies = $this->config($view)->get('dependencies.config');
|
||||
$this->assertTrue(in_array('image.style.large', $dependencies));
|
||||
// Check that form display 'node.article.default' depend on image style
|
||||
// 'image.style.thumbnail'.
|
||||
$dependencies = $this->config($view)->get('dependencies.config');
|
||||
$this->assertTrue(in_array('image.style.large', $dependencies));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\image\Kernel\ImageStyleIntegrationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests the integration of ImageStyle with the core.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageStyleIntegrationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['image', 'file', 'field', 'system', 'user', 'node'];
|
||||
|
||||
/**
|
||||
* Tests the dependency between ImageStyle and entity display components.
|
||||
*/
|
||||
public function testEntityDisplayDependency() {
|
||||
// Create two image styles.
|
||||
/** @var \Drupal\image\ImageStyleInterface $style */
|
||||
$style = ImageStyle::create(['name' => 'main_style']);
|
||||
$style->save();
|
||||
/** @var \Drupal\image\ImageStyleInterface $replacement */
|
||||
$replacement = ImageStyle::create(['name' => 'replacement_style']);
|
||||
$replacement->save();
|
||||
|
||||
// Create a node-type, named 'note'.
|
||||
$node_type = NodeType::create(['type' => 'note']);
|
||||
$node_type->save();
|
||||
|
||||
// Create an image field and attach it to the 'note' node-type.
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'sticker',
|
||||
'type' => 'image',
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'sticker',
|
||||
'bundle' => 'note',
|
||||
])->save();
|
||||
|
||||
// Create the default entity view display and set the 'sticker' field to use
|
||||
// the 'main_style' images style in formatter.
|
||||
/** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $view_display */
|
||||
$view_display = EntityViewDisplay::create([
|
||||
'targetEntityType' => 'node',
|
||||
'bundle' => 'note',
|
||||
'mode' => 'default',
|
||||
'status' => TRUE,
|
||||
])->setComponent('sticker', ['settings' => ['image_style' => 'main_style']]);
|
||||
$view_display->save();
|
||||
|
||||
// Create the default entity form display and set the 'sticker' field to use
|
||||
// the 'main_style' images style in the widget.
|
||||
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
|
||||
$form_display = EntityFormDisplay::create([
|
||||
'targetEntityType' => 'node',
|
||||
'bundle' => 'note',
|
||||
'mode' => 'default',
|
||||
'status' => TRUE,
|
||||
])->setComponent('sticker', ['settings' => ['preview_image_style' => 'main_style']]);
|
||||
$form_display->save();
|
||||
|
||||
// Check that the entity displays exists before dependency removal.
|
||||
$this->assertNotNull(EntityViewDisplay::load($view_display->id()));
|
||||
$this->assertNotNull(EntityFormDisplay::load($form_display->id()));
|
||||
|
||||
// Delete the 'main_style' image style. Before that, emulate the UI process
|
||||
// of selecting a replacement style by setting the replacement image style
|
||||
// ID in the image style storage.
|
||||
/** @var \Drupal\image\ImageStyleStorageInterface $storage */
|
||||
$storage = $this->container->get('entity.manager')->getStorage($style->getEntityTypeId());
|
||||
$storage->setReplacementId('main_style', 'replacement_style');
|
||||
$style->delete();
|
||||
|
||||
// Check that the entity displays exists after dependency removal.
|
||||
$this->assertNotNull($view_display = EntityViewDisplay::load($view_display->id()));
|
||||
$this->assertNotNull($form_display = EntityFormDisplay::load($form_display->id()));
|
||||
// Check that the 'sticker' formatter component exists in both displays.
|
||||
$this->assertNotNull($formatter = $view_display->getComponent('sticker'));
|
||||
$this->assertNotNull($widget = $form_display->getComponent('sticker'));
|
||||
// Check that both displays are using now 'replacement_style' for images.
|
||||
$this->assertSame('replacement_style', $formatter['settings']['image_style']);
|
||||
$this->assertSame('replacement_style', $widget['settings']['preview_image_style']);
|
||||
|
||||
// Delete the 'replacement_style' without setting a replacement image style.
|
||||
$replacement->delete();
|
||||
|
||||
// The entity view and form displays exists after dependency removal.
|
||||
$this->assertNotNull($view_display = EntityViewDisplay::load($view_display->id()));
|
||||
$this->assertNotNull($form_display = EntityFormDisplay::load($form_display->id()));
|
||||
// The 'sticker' formatter component should be hidden in view display.
|
||||
$this->assertNull($view_display->getComponent('sticker'));
|
||||
$this->assertTrue($view_display->get('hidden')['sticker']);
|
||||
// The 'sticker' widget component should be active in form displays, but the
|
||||
// image preview should be disabled.
|
||||
$this->assertNotNull($widget = $form_display->getComponent('sticker'));
|
||||
$this->assertSame('', $widget['settings']['preview_image_style']);
|
||||
}
|
||||
|
||||
}
|
|
@ -101,6 +101,10 @@ class LinkFieldTest extends WebTestBase {
|
|||
// strings displayed to the user).
|
||||
$valid_external_entries = array(
|
||||
'http://www.example.com/' => 'http://www.example.com/',
|
||||
// Strings within parenthesis without leading space char.
|
||||
'http://www.example.com/strings_(string_within_parenthesis)' => 'http://www.example.com/strings_(string_within_parenthesis)',
|
||||
// Numbers within parenthesis without leading space char.
|
||||
'http://www.example.com/numbers_(9999)' => 'http://www.example.com/numbers_(9999)',
|
||||
);
|
||||
$valid_internal_entries = array(
|
||||
'/entity_test/add' => '/entity_test/add',
|
||||
|
|
102
core/modules/link/src/Tests/Views/LinkViewsTokensTest.php
Normal file
102
core/modules/link/src/Tests/Views/LinkViewsTokensTest.php
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\link\Tests\Views\LinkViewsTokensTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\link\Tests\Views;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests the views integration for link tokens.
|
||||
*
|
||||
* @group link
|
||||
*/
|
||||
class LinkViewsTokensTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['link_test_views'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_link_tokens'];
|
||||
|
||||
/**
|
||||
* The field name used for the link field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'field_link';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
ViewTestData::createTestViews(get_class($this), array('link_test_views'));
|
||||
|
||||
// Create Basic page node type.
|
||||
$this->drupalCreateContentType(array(
|
||||
'type' => 'page',
|
||||
'name' => 'Basic page'
|
||||
));
|
||||
|
||||
// Create a field.
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $this->fieldName,
|
||||
'type' => 'link',
|
||||
'entity_type' => 'node',
|
||||
'cardinality' => 1,
|
||||
))->save();
|
||||
FieldConfig::create(array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'page',
|
||||
'label' => 'link field',
|
||||
))->save();
|
||||
|
||||
}
|
||||
|
||||
public function testLinkViewsTokens() {
|
||||
// Array of URI's to test.
|
||||
$uris = [
|
||||
'http://www.drupal.org' => 'Drupal.org',
|
||||
];
|
||||
|
||||
// Add nodes with the URI's and titles.
|
||||
foreach ($uris as $uri => $title) {
|
||||
$values = array('type' => 'page');
|
||||
$values[$this->fieldName][] = ['uri' => $uri, 'title' => $title, 'options' => ['attributes' => ['class' => 'test-link-class']]];
|
||||
$this->drupalCreateNode($values);
|
||||
}
|
||||
|
||||
$this->drupalGet('test_link_tokens');
|
||||
|
||||
foreach ($uris as $uri => $title) {
|
||||
// Formatted link: {{ field_link }}<br />
|
||||
$this->assertRaw("Formated: <a href=\"$uri\" class=\"test-link-class\">$title</a>");
|
||||
|
||||
// Raw uri: {{ field_link__uri }}<br />
|
||||
$this->assertRaw("Raw uri: $uri");
|
||||
|
||||
// Raw title: {{ field_link__title }}<br />
|
||||
$this->assertRaw("Raw title: $title");
|
||||
|
||||
// Raw options: {{ field_link__options }}<br />
|
||||
// Options is an array and should return empty after token replace.
|
||||
$this->assertRaw("Raw options: .");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
name: 'Link test views'
|
||||
type: module
|
||||
description: 'Provides default views for views link tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- node
|
||||
- views
|
||||
- link
|
|
@ -0,0 +1,206 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.field_link
|
||||
module:
|
||||
- link
|
||||
- node
|
||||
- user
|
||||
id: test_link_tokens
|
||||
label: link
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
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: full
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: '‹ previous'
|
||||
next: 'next ›'
|
||||
first: '« first'
|
||||
last: 'last »'
|
||||
quantity: 9
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
default_field_elements: true
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
fields:
|
||||
field_link:
|
||||
id: field_link
|
||||
table: node__field_link
|
||||
field: field_link
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: true
|
||||
text: "Formated: {{ field_link }}<br />\nRaw uri: {{ field_link__uri }}<br />\nRaw title: {{ field_link__title }}<br />\nRaw options: {{ field_link__options }}."
|
||||
make_link: false
|
||||
path: '{{ field_link__uri }}'
|
||||
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
|
||||
click_sort_column: uri
|
||||
type: link
|
||||
settings:
|
||||
trim_length: 80
|
||||
url_only: false
|
||||
url_plain: false
|
||||
rel: '0'
|
||||
target: '0'
|
||||
group_column: ''
|
||||
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
|
||||
plugin_id: field
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
table: node_field_data
|
||||
field: status
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
id: status
|
||||
expose:
|
||||
operator: ''
|
||||
group: 1
|
||||
sorts:
|
||||
created:
|
||||
id: created
|
||||
table: node_field_data
|
||||
field: created
|
||||
order: DESC
|
||||
entity_type: node
|
||||
entity_field: created
|
||||
plugin_id: date
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
granularity: second
|
||||
title: link
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
cacheable: false
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: test_link_tokens
|
||||
cache_metadata:
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
cacheable: false
|
|
@ -292,8 +292,6 @@ class MenuForm extends EntityForm {
|
|||
$form['links'][$id]['#attributes'] = $element['#attributes'];
|
||||
$form['links'][$id]['#attributes']['class'][] = 'draggable';
|
||||
|
||||
$form['links'][$id]['#item'] = $element['#item'];
|
||||
|
||||
// TableDrag: Sort the table row according to its existing/configured weight.
|
||||
$form['links'][$id]['#weight'] = $element['#item']->link->getWeight();
|
||||
|
||||
|
|
|
@ -34054,7 +34054,7 @@ $connection->insert('vocabulary')
|
|||
'hierarchy' => '1',
|
||||
'multiple' => '1',
|
||||
'required' => '0',
|
||||
'tags' => '0',
|
||||
'tags' => '1',
|
||||
'module' => 'taxonomy',
|
||||
'weight' => '5',
|
||||
))
|
||||
|
@ -34079,7 +34079,7 @@ $connection->insert('vocabulary')
|
|||
'relations' => '1',
|
||||
'hierarchy' => '0',
|
||||
'multiple' => '0',
|
||||
'required' => '0',
|
||||
'required' => '1',
|
||||
'tags' => '0',
|
||||
'module' => 'taxonomy',
|
||||
'weight' => '0',
|
||||
|
|
|
@ -4330,6 +4330,33 @@ $connection->schema()->createTable('field_data_field_file', array(
|
|||
'mysql_character_set' => 'utf8',
|
||||
));
|
||||
|
||||
$connection->insert('field_data_field_file')
|
||||
->fields(array(
|
||||
'entity_type',
|
||||
'bundle',
|
||||
'deleted',
|
||||
'entity_id',
|
||||
'revision_id',
|
||||
'language',
|
||||
'delta',
|
||||
'field_file_fid',
|
||||
'field_file_display',
|
||||
'field_file_description',
|
||||
))
|
||||
->values(array(
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'test_content_type',
|
||||
'deleted' => '0',
|
||||
'entity_id' => '1',
|
||||
'revision_id' => '1',
|
||||
'language' => 'und',
|
||||
'delta' => '0',
|
||||
'field_file_fid' => '2',
|
||||
'field_file_display' => '1',
|
||||
'field_file_description' => 'file desc',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$connection->schema()->createTable('field_data_field_float', array(
|
||||
'fields' => array(
|
||||
'entity_type' => array(
|
||||
|
@ -6065,6 +6092,33 @@ $connection->schema()->createTable('field_revision_field_file', array(
|
|||
'mysql_character_set' => 'utf8',
|
||||
));
|
||||
|
||||
$connection->insert('field_revision_field_file')
|
||||
->fields(array(
|
||||
'entity_type',
|
||||
'bundle',
|
||||
'deleted',
|
||||
'entity_id',
|
||||
'revision_id',
|
||||
'language',
|
||||
'delta',
|
||||
'field_file_fid',
|
||||
'field_file_display',
|
||||
'field_file_description',
|
||||
))
|
||||
->values(array(
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'test_content_type',
|
||||
'deleted' => '0',
|
||||
'entity_id' => '1',
|
||||
'revision_id' => '1',
|
||||
'language' => 'und',
|
||||
'delta' => '0',
|
||||
'field_file_fid' => '2',
|
||||
'field_file_display' => '1',
|
||||
'field_file_description' => 'file desc',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$connection->schema()->createTable('field_revision_field_float', array(
|
||||
'fields' => array(
|
||||
'entity_type' => array(
|
||||
|
@ -7337,6 +7391,13 @@ $connection->insert('file_usage')
|
|||
'module' => 'file',
|
||||
'type' => 'node',
|
||||
'id' => '1',
|
||||
'count' => '2',
|
||||
))
|
||||
->values(array(
|
||||
'fid' => '2',
|
||||
'module' => 'file',
|
||||
'type' => 'node',
|
||||
'id' => '1',
|
||||
'count' => '1',
|
||||
))
|
||||
->execute();
|
||||
|
|
|
@ -883,7 +883,9 @@ function node_form_system_themes_admin_form_submit($form, FormStateInterface $fo
|
|||
* with node access to ensure only nodes to which the user has access are
|
||||
* retrieved, through the use of hook_query_TAG_alter(). See the
|
||||
* @link entity_api Entity API topic @endlink for more information on entity
|
||||
* queries.
|
||||
* queries. Tagging a query with "node_access" does not check the
|
||||
* published/unpublished status of nodes, so the base query is responsible
|
||||
* for ensuring that unpublished nodes are not displayed to inappropriate users.
|
||||
*
|
||||
* Note: Even a single module returning an AccessResultInterface object from
|
||||
* hook_node_access() whose isForbidden() method equals TRUE will block access
|
||||
|
|
|
@ -65,6 +65,11 @@ class NodeGrantDatabaseStorage implements NodeGrantDatabaseStorageInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function access(NodeInterface $node, $operation, AccountInterface $account) {
|
||||
// Grants only support these operations.
|
||||
if (!in_array($operation, ['view', 'update', 'delete'])) {
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
// If no module implements the hook or the node does not have an id there is
|
||||
// no point in querying the database for access grants.
|
||||
if (!$this->moduleHandler->getImplementations('node_grants') || !$node->id()) {
|
||||
|
|
|
@ -39,6 +39,7 @@ class MigrateNodeTest extends MigrateDrupal7TestBase {
|
|||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('comment');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installEntitySchema('file');
|
||||
$this->installConfig(static::$modules);
|
||||
$this->installSchema('node', ['node_access']);
|
||||
$this->installSchema('system', ['sequences']);
|
||||
|
@ -51,6 +52,7 @@ class MigrateNodeTest extends MigrateDrupal7TestBase {
|
|||
'd7_field',
|
||||
'd7_field_instance',
|
||||
'd7_node__test_content_type',
|
||||
'd7_node__article',
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -134,6 +136,17 @@ class MigrateNodeTest extends MigrateDrupal7TestBase {
|
|||
$this->assertIdentical('Some more text', $node->field_text_list[0]->value);
|
||||
$this->assertIdentical('7', $node->field_integer_list[0]->value);
|
||||
$this->assertIdentical('qwerty', $node->field_text->value);
|
||||
$this->assertIdentical('2', $node->field_file->target_id);
|
||||
$this->assertIdentical('file desc', $node->field_file->description);
|
||||
$this->assertTrue($node->field_file->display);
|
||||
$this->assertIdentical('1', $node->field_images->target_id);
|
||||
$this->assertIdentical('alt text', $node->field_images->alt);
|
||||
$this->assertIdentical('title text', $node->field_images->title);
|
||||
$this->assertIdentical('93', $node->field_images->width);
|
||||
$this->assertIdentical('93', $node->field_images->height);
|
||||
|
||||
$node = Node::load(2);
|
||||
$this->assertIdentical("...is that it's the absolute best show ever. Trust me, I would know.", $node->body->value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,4 +26,13 @@ class NodeAccessGrantsTest extends NodeAccessTest {
|
|||
*/
|
||||
public static $modules = array('node_access_test_empty');
|
||||
|
||||
/**
|
||||
* Test operations not supported by node grants.
|
||||
*/
|
||||
function testUnsupportedOperation() {
|
||||
$web_user = $this->drupalCreateUser(['access content']);
|
||||
$node = $this->drupalCreateNode();
|
||||
$this->assertNodeAccess(['random_operation' => FALSE], $node, $web_user);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -80,17 +80,17 @@ class NodeAttributesTest extends NodeTestBase {
|
|||
'lang' => 'en',
|
||||
);
|
||||
$this->assertTrue($graph->hasProperty($node_uri, 'http://purl.org/dc/terms/title', $expected_value), 'Node title found in RDF output (dc:title).');
|
||||
// Node date.
|
||||
// Node date (date format must be UTC).
|
||||
$expected_value = array(
|
||||
'type' => 'literal',
|
||||
'value' => date('c', $node->getCreatedTime()),
|
||||
'value' => \Drupal::service('date.formatter')->format($node->getCreatedTime(), 'custom', 'c', 'UTC'),
|
||||
'datatype' => 'http://www.w3.org/2001/XMLSchema#dateTime',
|
||||
);
|
||||
$this->assertTrue($graph->hasProperty($node_uri, 'http://purl.org/dc/terms/date', $expected_value), 'Node date found in RDF output (dc:date).');
|
||||
// Node date.
|
||||
// Node date (date format must be UTC).
|
||||
$expected_value = array(
|
||||
'type' => 'literal',
|
||||
'value' => date('c', $node->getCreatedTime()),
|
||||
'value' => \Drupal::service('date.formatter')->format($node->getCreatedTime(), 'custom', 'c', 'UTC'),
|
||||
'datatype' => 'http://www.w3.org/2001/XMLSchema#dateTime',
|
||||
);
|
||||
$this->assertTrue($graph->hasProperty($node_uri, 'http://purl.org/dc/terms/created', $expected_value), 'Node date found in RDF output (dc:created).');
|
||||
|
|
|
@ -391,7 +391,7 @@ function responsive_image_build_source_attributes(ImageInterface $image, array $
|
|||
// Use the image width as key so we can sort the array later on.
|
||||
// Images within a srcset should be sorted from small to large, since
|
||||
// the first matching source will be used.
|
||||
$srcset[intval($dimensions['width'])] = file_create_url(_responsive_image_image_style_url($image_style_name, $image->getSource())) . ' ' . $dimensions['width'] . 'w';
|
||||
$srcset[intval($dimensions['width'])] = _responsive_image_image_style_url($image_style_name, $image->getSource()) . ' ' . $dimensions['width'] . 'w';
|
||||
$sizes = array_merge(explode(',', $image_style_mapping['image_mapping']['sizes']), $sizes);
|
||||
}
|
||||
break;
|
||||
|
@ -405,7 +405,7 @@ function responsive_image_build_source_attributes(ImageInterface $image, array $
|
|||
// be sorted from small to large, since the first matching source will
|
||||
// be used. We multiply it by 100 so multipliers with up to two decimals
|
||||
// can be used.
|
||||
$srcset[intval(Unicode::substr($multiplier, 0, -1) * 100)] = file_create_url(_responsive_image_image_style_url($image_style_mapping['image_mapping'], $image->getSource())) . ' ' . $multiplier;
|
||||
$srcset[intval(Unicode::substr($multiplier, 0, -1) * 100)] = _responsive_image_image_style_url($image_style_mapping['image_mapping'], $image->getSource()) . ' ' . $multiplier;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -495,9 +495,9 @@ function _responsive_image_image_style_url($style_name, $path) {
|
|||
}
|
||||
$entity = ImageStyle::load($style_name);
|
||||
if ($entity instanceof Drupal\image\Entity\ImageStyle) {
|
||||
return $entity->buildUrl($path);
|
||||
return file_url_transform_relative($entity->buildUrl($path));
|
||||
}
|
||||
return file_create_url($path);
|
||||
return file_url_transform_relative(file_create_url($path));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -230,7 +230,7 @@ class ResponsiveImageFormatter extends ImageFormatterBase implements ContainerFa
|
|||
foreach ($files as $delta => $file) {
|
||||
// Link the <picture> element to the original file.
|
||||
if (isset($link_file)) {
|
||||
$url = Url::fromUri(file_create_url($file->getFileUri()));
|
||||
$url = file_url_transform_relative(file_create_url($file->getFileUri()));
|
||||
}
|
||||
// Extract field item attributes for the theme function, and unset them
|
||||
// from the $item so that the field template does not re-render them.
|
||||
|
|
|
@ -244,7 +244,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
$display->setComponent($field_name, $display_options)
|
||||
->save();
|
||||
|
||||
$default_output = '<a href="' . file_create_url($image_uri) . '"><picture';
|
||||
$default_output = '<a href="' . file_url_transform_relative(file_create_url($image_uri)) . '"><picture';
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertTrue(!preg_match('/ image_style\:/', $cache_tags_header), 'No image style cache tag found.');
|
||||
|
@ -289,10 +289,10 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
$this->assertRaw('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
|
||||
$thumbnail_style = ImageStyle::load('thumbnail');
|
||||
// Assert the output of the 'srcset' attribute (small multipliers first).
|
||||
$this->assertRaw('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== 1x, ' . $thumbnail_style->buildUrl($image_uri) . ' 1.5x');
|
||||
$this->assertRaw('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== 1x, ' . file_url_transform_relative($thumbnail_style->buildUrl($image_uri)) . ' 1.5x');
|
||||
$this->assertRaw('/styles/medium/');
|
||||
// Assert the output of the original image.
|
||||
$this->assertRaw(file_create_url($image_uri) . ' 3x');
|
||||
$this->assertRaw(file_url_transform_relative(file_create_url($image_uri)) . ' 3x');
|
||||
// Assert the output of the breakpoints.
|
||||
$this->assertRaw('media="(min-width: 0px)"');
|
||||
$this->assertRaw('media="(min-width: 560px)"');
|
||||
|
@ -301,7 +301,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
$this->assertPattern('/media="\(min-width: 560px\)".+?sizes="\(min-width: 700px\) 700px, 100vw"/');
|
||||
// Assert the output of the 'srcset' attribute (small images first).
|
||||
$medium_style = ImageStyle::load('medium');
|
||||
$this->assertRaw($medium_style->buildUrl($image_uri) . ' 220w, ' . $large_style->buildUrl($image_uri) . ' 360w');
|
||||
$this->assertRaw(file_url_transform_relative($medium_style->buildUrl($image_uri)) . ' 220w, ' . file_url_transform_relative($large_style->buildUrl($image_uri)) . ' 360w');
|
||||
$this->assertRaw('media="(min-width: 851px)"');
|
||||
}
|
||||
$this->assertRaw('/styles/large/');
|
||||
|
@ -321,7 +321,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
'#alt' => $alt,
|
||||
'#srcset' => array(
|
||||
array(
|
||||
'uri' => $large_style->buildUrl($image->getSource()),
|
||||
'uri' => file_url_transform_relative($large_style->buildUrl($image->getSource())),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -403,7 +403,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
$thumbnail_style = ImageStyle::load('thumbnail');
|
||||
$node = $node_storage->load($nid);
|
||||
$image_uri = File::load($node->{$field_name}->target_id)->getFileUri();
|
||||
$this->assertPattern('/srcset="' . preg_quote($thumbnail_style->buildUrl($image_uri), '/') . ' 1x".+?media="\(min-width: 0px\)"/');
|
||||
$this->assertPattern('/srcset="' . preg_quote(file_url_transform_relative($thumbnail_style->buildUrl($image_uri)), '/') . ' 1x".+?media="\(min-width: 0px\)"/');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -449,7 +449,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
$medium_style = ImageStyle::load('medium');
|
||||
$node = $node_storage->load($nid);
|
||||
$image_uri = File::load($node->{$field_name}->target_id)->getFileUri();
|
||||
$this->assertRaw('<img srcset="' . $medium_style->buildUrl($image_uri) . ' 1x, ' . $large_style->buildUrl($image_uri) . ' 2x"');
|
||||
$this->assertRaw('<img srcset="' . file_url_transform_relative($medium_style->buildUrl($image_uri)) . ' 1x, ' . file_url_transform_relative($large_style->buildUrl($image_uri)) . ' 2x"');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -509,7 +509,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
|
|||
switch ($link_type) {
|
||||
case 'file':
|
||||
// Make sure the link to the file is present.
|
||||
$this->assertPattern('/<a(.*?)href="' . preg_quote(file_create_url($image_uri), '/') . '"(.*?)><picture/');
|
||||
$this->assertPattern('/<a(.*?)href="' . preg_quote(file_url_transform_relative(file_create_url($image_uri)), '/') . '"(.*?)><picture/');
|
||||
break;
|
||||
|
||||
case 'content':
|
||||
|
|
|
@ -33,7 +33,7 @@ class EntityDeriver implements ContainerDeriverInterface {
|
|||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs an EntityDerivative object.
|
||||
* Constructs an EntityDeriver object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
|
@ -86,7 +86,7 @@ class EntityDeriver implements ContainerDeriverInterface {
|
|||
// Check if there are link templates defined for the entity type and
|
||||
// use the path from the route instead of the default.
|
||||
if ($link_template = $entity_type->getLinkTemplate($link_relation)) {
|
||||
$this->derivatives[$entity_type_id]['uri_paths'][$link_relation] = '/' . $link_template;
|
||||
$this->derivatives[$entity_type_id]['uri_paths'][$link_relation] = $link_template;
|
||||
}
|
||||
else {
|
||||
$this->derivatives[$entity_type_id]['uri_paths'][$link_relation] = $default_uri;
|
||||
|
|
|
@ -29,7 +29,7 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
|
|||
* }
|
||||
* )
|
||||
*
|
||||
* @see \Drupal\rest\Plugin\Derivative\EntityDerivative
|
||||
* @see \Drupal\rest\Plugin\Deriver\EntityDeriver
|
||||
*/
|
||||
class EntityResource extends ResourceBase {
|
||||
|
||||
|
|
|
@ -88,6 +88,10 @@ class DataFieldRow extends RowPluginBase {
|
|||
|
||||
if ($fields = $this->view->display_handler->getOption('fields')) {
|
||||
foreach ($fields as $id => $field) {
|
||||
// Don't show the field if it has been excluded.
|
||||
if (!empty($field['exclude'])) {
|
||||
continue;
|
||||
}
|
||||
$form['field_options'][$id]['field'] = array(
|
||||
'#markup' => $id,
|
||||
);
|
||||
|
@ -138,6 +142,10 @@ class DataFieldRow extends RowPluginBase {
|
|||
$output = array();
|
||||
|
||||
foreach ($this->view->field as $id => $field) {
|
||||
// Don't render anything if this field is excluded.
|
||||
if (!empty($field->options['exclude'])) {
|
||||
continue;
|
||||
}
|
||||
// If the raw output option has been set, just get the raw value.
|
||||
if (!empty($this->rawOutputOptions[$id])) {
|
||||
$value = $field->getValue($row);
|
||||
|
|
|
@ -106,4 +106,19 @@ class ResourceTest extends RESTTestBase {
|
|||
$this->curlClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that resource URI paths are formatted properly.
|
||||
*/
|
||||
public function testUriPaths() {
|
||||
$this->enableService('entity:entity_test');
|
||||
/** @var \Drupal\rest\Plugin\Type\ResourcePluginManager $manager */
|
||||
$manager = \Drupal::service('plugin.manager.rest');
|
||||
|
||||
foreach ($manager->getDefinitions() as $resource => $definition) {
|
||||
foreach ($definition['uri_paths'] as $key => $uri_path) {
|
||||
$this->assertFalse(strpos($uri_path, '//'), 'The resource URI path does not have duplicate slashes.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -470,6 +470,32 @@ class StyleSerializerTest extends PluginTestBase {
|
|||
$this->assertIdentical($values['created'], $view->result[$index]->views_test_data_created, 'Expected raw created value found.');
|
||||
$this->assertIdentical($values['name'], $view->result[$index]->views_test_data_name, 'Expected raw name value found.');
|
||||
}
|
||||
|
||||
// Test result with an excluded field.
|
||||
$view->setDisplay('rest_export_1');
|
||||
$view->displayHandlers->get('rest_export_1')->overrideOption('fields', [
|
||||
'name' => [
|
||||
'id' => 'name',
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
],
|
||||
'created' => [
|
||||
'id' => 'created',
|
||||
'exclude' => TRUE,
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'created',
|
||||
'relationship' => 'none',
|
||||
],
|
||||
]);
|
||||
$view->save();
|
||||
$this->executeView($view);
|
||||
foreach ($this->drupalGetJSON('test/serialize/field') as $index => $values) {
|
||||
$this->assertTrue(!isset($values['created']), 'Excluded value not found.');
|
||||
}
|
||||
// Test that the excluded field is not shown in the row options.
|
||||
$this->drupalGet('admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options');
|
||||
$this->assertNoText('created');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
72
core/modules/simpletest/src/BlockCreationTrait.php
Normal file
72
core/modules/simpletest/src/BlockCreationTrait.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\simpletest\BlockCreationTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\simpletest;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
|
||||
/**
|
||||
* Provides methods to create and place block with default settings.
|
||||
*
|
||||
* This trait is meant to be used only by test classes.
|
||||
*/
|
||||
trait BlockCreationTrait {
|
||||
|
||||
/**
|
||||
* Creates a block instance based on default settings.
|
||||
*
|
||||
* @param string $plugin_id
|
||||
* The plugin ID of the block type for this block instance.
|
||||
* @param array $settings
|
||||
* (optional) An associative array of settings for the block entity.
|
||||
* Override the defaults by specifying the key and value in the array, for
|
||||
* example:
|
||||
* @code
|
||||
* $this->drupalPlaceBlock('system_powered_by_block', array(
|
||||
* 'label' => t('Hello, world!'),
|
||||
* ));
|
||||
* @endcode
|
||||
* The following defaults are provided:
|
||||
* - label: Random string.
|
||||
* - ID: Random string.
|
||||
* - region: 'sidebar_first'.
|
||||
* - theme: The default theme.
|
||||
* - visibility: Empty array.
|
||||
*
|
||||
* @return \Drupal\block\Entity\Block
|
||||
* The block entity.
|
||||
*
|
||||
* @todo
|
||||
* Add support for creating custom block instances.
|
||||
*/
|
||||
protected function placeBlock($plugin_id, array $settings = array()) {
|
||||
$config = \Drupal::configFactory();
|
||||
$settings += array(
|
||||
'plugin' => $plugin_id,
|
||||
'region' => 'sidebar_first',
|
||||
'id' => strtolower($this->randomMachineName(8)),
|
||||
'theme' => $config->get('system.theme')->get('default'),
|
||||
'label' => $this->randomMachineName(8),
|
||||
'visibility' => array(),
|
||||
'weight' => 0,
|
||||
);
|
||||
$values = [];
|
||||
foreach (array('region', 'id', 'theme', 'plugin', 'weight', 'visibility') as $key) {
|
||||
$values[$key] = $settings[$key];
|
||||
// Remove extra values that do not belong in the settings array.
|
||||
unset($settings[$key]);
|
||||
}
|
||||
foreach ($values['visibility'] as $id => $visibility) {
|
||||
$values['visibility'][$id]['id'] = $id;
|
||||
}
|
||||
$values['settings'] = $settings;
|
||||
$block = Block::create($values);
|
||||
$block->save();
|
||||
return $block;
|
||||
}
|
||||
|
||||
}
|
58
core/modules/simpletest/src/ContentTypeCreationTrait.php
Normal file
58
core/modules/simpletest/src/ContentTypeCreationTrait.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\simpletest\ContentTypeCreationTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\simpletest;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Provides methods to create content type from given values.
|
||||
*
|
||||
* This trait is meant to be used only by test classes.
|
||||
*/
|
||||
trait ContentTypeCreationTrait {
|
||||
|
||||
/**
|
||||
* Creates a custom content type based on default settings.
|
||||
*
|
||||
* @param array $values
|
||||
* An array of settings to change from the defaults.
|
||||
* Example: 'type' => 'foo'.
|
||||
*
|
||||
* @return \Drupal\node\Entity\NodeType
|
||||
* Created content type.
|
||||
*/
|
||||
protected function createContentType(array $values = array()) {
|
||||
// Find a non-existent random type name.
|
||||
if (!isset($values['type'])) {
|
||||
do {
|
||||
$id = strtolower($this->randomMachineName(8));
|
||||
} while (NodeType::load($id));
|
||||
}
|
||||
else {
|
||||
$id = $values['type'];
|
||||
}
|
||||
$values += array(
|
||||
'type' => $id,
|
||||
'name' => $id,
|
||||
);
|
||||
$type = NodeType::create($values);
|
||||
$status = $type->save();
|
||||
node_add_body_field($type);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
if ($this instanceof \PHPUnit_Framework_TestCase) {
|
||||
$this->assertSame($status, SAVED_NEW, (new FormattableMarkup('Created content type %type.', array('%type' => $type->id())))->__toString());
|
||||
}
|
||||
else {
|
||||
$this->assertEqual($status, SAVED_NEW, (new FormattableMarkup('Created content type %type.', array('%type' => $type->id())))->__toString());
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
}
|
88
core/modules/simpletest/src/NodeCreationTrait.php
Normal file
88
core/modules/simpletest/src/NodeCreationTrait.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\simpletest\NodeCreationTrait
|
||||
*/
|
||||
|
||||
namespace Drupal\simpletest;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Provides methods to create node based on default settings.
|
||||
*
|
||||
* This trait is meant to be used only by test classes.
|
||||
*/
|
||||
trait NodeCreationTrait {
|
||||
|
||||
/**
|
||||
* Get a node from the database based on its title.
|
||||
*
|
||||
* @param string|\Drupal\Component\Render\MarkupInterface $title
|
||||
* A node title, usually generated by $this->randomMachineName().
|
||||
* @param $reset
|
||||
* (optional) Whether to reset the entity cache.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* A node entity matching $title.
|
||||
*/
|
||||
function getNodeByTitle($title, $reset = FALSE) {
|
||||
if ($reset) {
|
||||
\Drupal::entityTypeManager()->getStorage('node')->resetCache();
|
||||
}
|
||||
// Cast MarkupInterface objects to string.
|
||||
$title = (string) $title;
|
||||
$nodes = \Drupal::entityTypeManager()
|
||||
->getStorage('node')
|
||||
->loadByProperties(['title' => $title]);
|
||||
// Load the first node returned from the database.
|
||||
$returned_node = reset($nodes);
|
||||
return $returned_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a node based on default settings.
|
||||
*
|
||||
* @param array $settings
|
||||
* (optional) An associative array of settings for the node, as used in
|
||||
* entity_create(). Override the defaults by specifying the key and value
|
||||
* in the array, for example:
|
||||
* @code
|
||||
* $this->drupalCreateNode(array(
|
||||
* 'title' => t('Hello, world!'),
|
||||
* 'type' => 'article',
|
||||
* ));
|
||||
* @endcode
|
||||
* The following defaults are provided:
|
||||
* - body: Random string using the default filter format:
|
||||
* @code
|
||||
* $settings['body'][0] = array(
|
||||
* 'value' => $this->randomMachineName(32),
|
||||
* 'format' => filter_default_format(),
|
||||
* );
|
||||
* @endcode
|
||||
* - title: Random string.
|
||||
* - type: 'page'.
|
||||
* - uid: The currently logged in user, or anonymous.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The created node entity.
|
||||
*/
|
||||
protected function createNode(array $settings = array()) {
|
||||
// Populate defaults array.
|
||||
$settings += array(
|
||||
'body' => array(array(
|
||||
'value' => $this->randomMachineName(32),
|
||||
'format' => filter_default_format(),
|
||||
)),
|
||||
'title' => $this->randomMachineName(8),
|
||||
'type' => 'page',
|
||||
'uid' => \Drupal::currentUser()->id(),
|
||||
);
|
||||
$node = Node::create($settings);
|
||||
$node->save();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
}
|
|
@ -333,7 +333,7 @@ EOD;
|
|||
$assertion['file'] = $this->asText($row->td[2]);
|
||||
$assertion['line'] = $this->asText($row->td[3]);
|
||||
$assertion['function'] = $this->asText($row->td[4]);
|
||||
$ok_url = file_create_url('core/misc/icons/73b355/check.svg');
|
||||
$ok_url = file_url_transform_relative(file_create_url('core/misc/icons/73b355/check.svg'));
|
||||
$assertion['status'] = ($row->td[5]->img['src'] == $ok_url) ? 'Pass' : 'Fail';
|
||||
$results['assertions'][] = $assertion;
|
||||
}
|
||||
|
|
42
core/modules/simpletest/src/Tests/TimeZoneTest.php
Normal file
42
core/modules/simpletest/src/Tests/TimeZoneTest.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\simpletest\Tests\TimeZoneTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\simpletest\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* This test will check SimpleTest's default time zone handling.
|
||||
*
|
||||
* @group simpletest
|
||||
*/
|
||||
class TimeZoneTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* A user with administrative privileges.
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer site configuration']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user accounts have the default time zone set.
|
||||
*/
|
||||
function testAccountTimeZones() {
|
||||
$expected = 'Australia/Sydney';
|
||||
$this->assertEqual($this->rootUser->getTimeZone(), $expected, 'Root user has correct time zone.');
|
||||
$this->assertEqual($this->adminUser->getTimeZone(), $expected, 'Admin user has correct time zone.');
|
||||
}
|
||||
|
||||
}
|
|
@ -29,7 +29,6 @@ use Drupal\Core\Session\UserSession;
|
|||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Zend\Diactoros\Uri;
|
||||
|
@ -42,7 +41,16 @@ use Zend\Diactoros\Uri;
|
|||
abstract class WebTestBase extends TestBase {
|
||||
|
||||
use AssertContentTrait;
|
||||
|
||||
use BlockCreationTrait {
|
||||
placeBlock as drupalPlaceBlock;
|
||||
}
|
||||
use ContentTypeCreationTrait {
|
||||
createContentType as drupalCreateContentType;
|
||||
}
|
||||
use NodeCreationTrait {
|
||||
getNodeByTitle as drupalGetNodeByTitle;
|
||||
createNode as drupalCreateNode;
|
||||
}
|
||||
use UserCreationTrait {
|
||||
createUser as drupalCreateUser;
|
||||
createRole as drupalCreateRole;
|
||||
|
@ -230,108 +238,6 @@ abstract class WebTestBase extends TestBase {
|
|||
$this->classLoader = require DRUPAL_ROOT . '/autoload.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a node from the database based on its title.
|
||||
*
|
||||
* @param string|\Drupal\Component\Render\MarkupInterface $title
|
||||
* A node title, usually generated by $this->randomMachineName().
|
||||
* @param $reset
|
||||
* (optional) Whether to reset the entity cache.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* A node entity matching $title.
|
||||
*/
|
||||
function drupalGetNodeByTitle($title, $reset = FALSE) {
|
||||
if ($reset) {
|
||||
\Drupal::entityManager()->getStorage('node')->resetCache();
|
||||
}
|
||||
// Cast MarkupInterface objects to string.
|
||||
$title = (string) $title;
|
||||
$nodes = entity_load_multiple_by_properties('node', array('title' => $title));
|
||||
// Load the first node returned from the database.
|
||||
$returned_node = reset($nodes);
|
||||
return $returned_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a node based on default settings.
|
||||
*
|
||||
* @param array $settings
|
||||
* (optional) An associative array of settings for the node, as used in
|
||||
* entity_create(). Override the defaults by specifying the key and value
|
||||
* in the array, for example:
|
||||
* @code
|
||||
* $this->drupalCreateNode(array(
|
||||
* 'title' => t('Hello, world!'),
|
||||
* 'type' => 'article',
|
||||
* ));
|
||||
* @endcode
|
||||
* The following defaults are provided:
|
||||
* - body: Random string using the default filter format:
|
||||
* @code
|
||||
* $settings['body'][0] = array(
|
||||
* 'value' => $this->randomMachineName(32),
|
||||
* 'format' => filter_default_format(),
|
||||
* );
|
||||
* @endcode
|
||||
* - title: Random string.
|
||||
* - type: 'page'.
|
||||
* - uid: The currently logged in user, or anonymous.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The created node entity.
|
||||
*/
|
||||
protected function drupalCreateNode(array $settings = array()) {
|
||||
// Populate defaults array.
|
||||
$settings += array(
|
||||
'body' => array(array(
|
||||
'value' => $this->randomMachineName(32),
|
||||
'format' => filter_default_format(),
|
||||
)),
|
||||
'title' => $this->randomMachineName(8),
|
||||
'type' => 'page',
|
||||
'uid' => \Drupal::currentUser()->id(),
|
||||
);
|
||||
$node = entity_create('node', $settings);
|
||||
$node->save();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom content type based on default settings.
|
||||
*
|
||||
* @param array $values
|
||||
* An array of settings to change from the defaults.
|
||||
* Example: 'type' => 'foo'.
|
||||
*
|
||||
* @return \Drupal\node\Entity\NodeType
|
||||
* Created content type.
|
||||
*/
|
||||
protected function drupalCreateContentType(array $values = array()) {
|
||||
// Find a non-existent random type name.
|
||||
if (!isset($values['type'])) {
|
||||
do {
|
||||
$id = strtolower($this->randomMachineName(8));
|
||||
} while (NodeType::load($id));
|
||||
}
|
||||
else {
|
||||
$id = $values['type'];
|
||||
}
|
||||
$values += array(
|
||||
'type' => $id,
|
||||
'name' => $id,
|
||||
);
|
||||
$type = entity_create('node_type', $values);
|
||||
$status = $type->save();
|
||||
node_add_body_field($type);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
$this->assertEqual($status, SAVED_NEW, SafeMarkup::format('Created content type %type.', array('%type' => $type->id())));
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the renderable view of an entity.
|
||||
*
|
||||
|
@ -390,58 +296,6 @@ abstract class WebTestBase extends TestBase {
|
|||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a block instance based on default settings.
|
||||
*
|
||||
* @param string $plugin_id
|
||||
* The plugin ID of the block type for this block instance.
|
||||
* @param array $settings
|
||||
* (optional) An associative array of settings for the block entity.
|
||||
* Override the defaults by specifying the key and value in the array, for
|
||||
* example:
|
||||
* @code
|
||||
* $this->drupalPlaceBlock('system_powered_by_block', array(
|
||||
* 'label' => t('Hello, world!'),
|
||||
* ));
|
||||
* @endcode
|
||||
* The following defaults are provided:
|
||||
* - label: Random string.
|
||||
* - ID: Random string.
|
||||
* - region: 'sidebar_first'.
|
||||
* - theme: The default theme.
|
||||
* - visibility: Empty array.
|
||||
*
|
||||
* @return \Drupal\block\Entity\Block
|
||||
* The block entity.
|
||||
*
|
||||
* @todo
|
||||
* Add support for creating custom block instances.
|
||||
*/
|
||||
protected function drupalPlaceBlock($plugin_id, array $settings = array()) {
|
||||
$settings += array(
|
||||
'plugin' => $plugin_id,
|
||||
'region' => 'sidebar_first',
|
||||
'id' => strtolower($this->randomMachineName(8)),
|
||||
'theme' => $this->config('system.theme')->get('default'),
|
||||
'label' => $this->randomMachineName(8),
|
||||
'visibility' => array(),
|
||||
'weight' => 0,
|
||||
);
|
||||
$values = [];
|
||||
foreach (array('region', 'id', 'theme', 'plugin', 'weight', 'visibility') as $key) {
|
||||
$values[$key] = $settings[$key];
|
||||
// Remove extra values that do not belong in the settings array.
|
||||
unset($settings[$key]);
|
||||
}
|
||||
foreach ($values['visibility'] as $id => $visibility) {
|
||||
$values['visibility'][$id]['id'] = $id;
|
||||
}
|
||||
$values['settings'] = $settings;
|
||||
$block = entity_create('block', $values);
|
||||
$block->save();
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see whether a block appears on the page.
|
||||
*
|
||||
|
@ -674,6 +528,14 @@ abstract class WebTestBase extends TestBase {
|
|||
* being executed.
|
||||
*/
|
||||
protected function setUp() {
|
||||
// Set an explicit time zone to not rely on the system one, which may vary
|
||||
// from setup to setup. The Australia/Sydney time zone is chosen so all
|
||||
// tests are run using an edge case scenario (UTC+10 and DST). This choice
|
||||
// is made to prevent time zone related regressions and reduce the
|
||||
// fragility of the testing system in general. This is also set in config in
|
||||
// \Drupal\simpletest\WebTestBase::initConfig().
|
||||
date_default_timezone_set('Australia/Sydney');
|
||||
|
||||
// Preserve original batch for later restoration.
|
||||
$this->setBatch();
|
||||
|
||||
|
@ -983,6 +845,7 @@ abstract class WebTestBase extends TestBase {
|
|||
'name' => 'admin',
|
||||
'mail' => 'admin@example.com',
|
||||
'pass_raw' => $this->randomMachineName(),
|
||||
'timezone' => date_default_timezone_get(),
|
||||
));
|
||||
|
||||
// The child site derives its session name from the database prefix when
|
||||
|
@ -2005,8 +1868,7 @@ abstract class WebTestBase extends TestBase {
|
|||
}
|
||||
// @todo Ajax commands can target any jQuery selector, but these are
|
||||
// hard to fully emulate with XPath. For now, just handle 'head'
|
||||
// and 'body', since these are used by
|
||||
// \Drupal\Core\Ajax\AjaxResponse::ajaxRender().
|
||||
// and 'body', since these are used by the Ajax renderer.
|
||||
elseif (in_array($command['selector'], array('head', 'body'))) {
|
||||
$wrapperNode = $xpath->query('//' . $command['selector'])->item(0);
|
||||
}
|
||||
|
|
|
@ -133,7 +133,6 @@ class CronForm extends FormBase {
|
|||
drupal_set_message(t('Cron run failed.'), 'error');
|
||||
}
|
||||
|
||||
return new RedirectResponse($this->url('system.cron_settings', array(), array('absolute' => TRUE)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ class SystemConfigSubscriber implements EventSubscriberInterface {
|
|||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[ConfigEvents::SAVE][] = array('onConfigSave', 0);
|
||||
// The empty check has a high priority so that is can stop propagation if
|
||||
// The empty check has a high priority so that it can stop propagation if
|
||||
// there is no configuration to import.
|
||||
$events[ConfigEvents::IMPORT_VALIDATE][] = array('onConfigImporterValidateNotEmpty', 512);
|
||||
$events[ConfigEvents::IMPORT_VALIDATE][] = array('onConfigImporterValidateSiteUUID', 256);
|
||||
|
|
|
@ -22,13 +22,13 @@ use Drupal\Core\Asset\AttachedAssets;
|
|||
*/
|
||||
class FrameworkTest extends AjaxTestBase {
|
||||
/**
|
||||
* Ensures \Drupal\Core\Ajax\AjaxResponse::ajaxRender() returns JavaScript settings from the page request.
|
||||
* Verifies the Ajax rendering of a command in the settings.
|
||||
*/
|
||||
public function testAJAXRender() {
|
||||
// Verify that settings command is generated if JavaScript settings exist.
|
||||
$commands = $this->drupalGetAjax('ajax-test/render');
|
||||
$expected = new SettingsCommand(array('ajax' => 'test'), TRUE);
|
||||
$this->assertCommand($commands, $expected->render(), '\Drupal\Core\Ajax\AjaxResponse::ajaxRender() loads JavaScript settings.');
|
||||
$this->assertCommand($commands, $expected->render(), 'JavaScript settings command is present.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,8 +59,8 @@ class FrameworkTest extends AjaxTestBase {
|
|||
|
||||
// Load any page with at least one CSS file, at least one JavaScript file
|
||||
// and at least one #ajax-powered element. The latter is an assumption of
|
||||
// drupalPostAjaxForm(), the two former are assumptions of
|
||||
// AjaxResponse::ajaxRender().
|
||||
// drupalPostAjaxForm(), the two former are assumptions of the Ajax
|
||||
// renderer.
|
||||
// @todo refactor AJAX Framework + tests to make less assumptions.
|
||||
$this->drupalGet('ajax_forms_test_lazy_load_form');
|
||||
|
||||
|
|
|
@ -96,8 +96,8 @@ class AttachedAssetsTest extends KernelTestBase {
|
|||
$rendered_css = $this->renderer->renderPlain($css_render_array);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
|
||||
$this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="' . file_create_url('core/modules/system/tests/modules/common_test/bar.css') . '?' . $query_string . '" media="all" />'), FALSE, 'Rendering an external CSS file.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/foo.js') . '?' . $query_string . '"></script>'), FALSE, 'Rendering an external JavaScript file.');
|
||||
$this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/bar.css')) . '?' . $query_string . '" media="all" />'), FALSE, 'Rendering an external CSS file.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/foo.js')) . '?' . $query_string . '"></script>'), FALSE, 'Rendering an external JavaScript file.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,7 +150,7 @@ class AttachedAssetsTest extends KernelTestBase {
|
|||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>';
|
||||
$expected_2 = '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js') . '?v=1" defer bar="foo"></script>';
|
||||
$expected_2 = '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js')) . '?v=1" defer bar="foo"></script>';
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered external JavaScript with correct defer and random attributes.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_2), FALSE, 'Rendered internal JavaScript with correct defer and random attributes.');
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ class AttachedAssetsTest extends KernelTestBase {
|
|||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>';
|
||||
$expected_2 = '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js') . '?v=1" defer bar="foo"></script>';
|
||||
$expected_2 = '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js')) . '?v=1" defer bar="foo"></script>';
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered external JavaScript with correct defer and random attributes.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_2), FALSE, 'Rendered internal JavaScript with correct defer and random attributes.');
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ class AttachedAssetsTest extends KernelTestBase {
|
|||
$rendered_footer_js = \Drupal::service('asset.js.collection_renderer')->render($footer_js);
|
||||
$this->assertEqual(2, count($rendered_footer_js), 'There are 2 JavaScript assets in the footer.');
|
||||
$this->assertEqual('drupal-settings-json', $rendered_footer_js[0]['#attributes']['data-drupal-selector'], 'The first of the two JavaScript assets in the footer has drupal settings.');
|
||||
$this->assertEqual('http://', substr($rendered_footer_js[1]['#attributes']['src'], 0, 7), 'The second of the two JavaScript assets in the footer has the sole aggregated JavaScript asset.');
|
||||
$this->assertEqual(0, strpos($rendered_footer_js[1]['#attributes']['src'], base_path()), 'The second of the two JavaScript assets in the footer has the sole aggregated JavaScript asset.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -237,9 +237,9 @@ class AttachedAssetsTest extends KernelTestBase {
|
|||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/header.js') . '?' . $query_string . '"></script>'), FALSE, 'The JS asset in common_test/js-header appears in the header.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_create_url('core/misc/drupal.js')), FALSE, 'The JS asset of the direct dependency (core/drupal) of common_test/js-header appears in the header.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_create_url('core/assets/vendor/domready/ready.min.js')), FALSE, 'The JS asset of the indirect dependency (core/domready) of common_test/js-header appears in the header.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/header.js')) . '?' . $query_string . '"></script>'), FALSE, 'The JS asset in common_test/js-header appears in the header.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/misc/drupal.js'))), FALSE, 'The JS asset of the direct dependency (core/drupal) of common_test/js-header appears in the header.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . file_url_transform_relative(file_create_url('core/assets/vendor/domready/ready.min.js'))), FALSE, 'The JS asset of the indirect dependency (core/domready) of common_test/js-header appears in the header.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,8 +267,8 @@ 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);
|
||||
$expected_1 = "<!--[if lte IE 8]>\n" . '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/old-ie.js') . '?' . $default_query_string . '"></script>' . "\n<![endif]-->";
|
||||
$expected_2 = "<!--[if !IE]><!-->\n" . '<script src="' . file_create_url('core/modules/system/tests/modules/common_test/no-ie.js') . '?' . $default_query_string . '"></script>' . "\n<!--<![endif]-->";
|
||||
$expected_1 = "<!--[if lte IE 8]>\n" . '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/old-ie.js')) . '?' . $default_query_string . '"></script>' . "\n<![endif]-->";
|
||||
$expected_2 = "<!--[if !IE]><!-->\n" . '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/no-ie.js')) . '?' . $default_query_string . '"></script>' . "\n<!--<![endif]-->";
|
||||
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_1), FALSE, 'Rendered JavaScript within downlevel-hidden conditional comments.');
|
||||
$this->assertNotIdentical(strpos($rendered_js, $expected_2), FALSE, 'Rendered JavaScript within downlevel-revealed conditional comments.');
|
||||
|
@ -476,8 +476,8 @@ class AttachedAssetsTest extends KernelTestBase {
|
|||
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
|
||||
$rendered_js = $this->renderer->renderPlain($js_render_array);
|
||||
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
|
||||
$this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="' . str_replace('&', '&', file_create_url('core/modules/system/tests/modules/common_test/querystring.css?arg1=value1&arg2=value2')) . '&' . $query_string . '" media="all" />'), FALSE, 'CSS file with query string gets version query string correctly appended..');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . str_replace('&', '&', file_create_url('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2')) . '&' . $query_string . '"></script>'), FALSE, 'JavaScript file with query string gets version query string correctly appended.');
|
||||
$this->assertNotIdentical(strpos($rendered_css, '<link rel="stylesheet" href="' . str_replace('&', '&', file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/querystring.css?arg1=value1&arg2=value2'))) . '&' . $query_string . '" media="all" />'), FALSE, 'CSS file with query string gets version query string correctly appended..');
|
||||
$this->assertNotIdentical(strpos($rendered_js, '<script src="' . str_replace('&', '&', file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2'))) . '&' . $query_string . '"></script>'), FALSE, 'JavaScript file with query string gets version query string correctly appended.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ namespace Drupal\system\Tests\Entity;
|
|||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
|
@ -810,4 +812,97 @@ class EntityTranslationTest extends EntityLanguageTestBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if entity translation statuses are correct after removing two
|
||||
* translation.
|
||||
*/
|
||||
public function testDeleteEntityTranslation() {
|
||||
$entity_type = 'entity_test_mul';
|
||||
$controller = $this->entityManager->getStorage($entity_type);
|
||||
|
||||
// Create a translatable test field.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'entity_type' => $entity_type,
|
||||
'field_name' => 'translatable_test_field',
|
||||
'type' => 'field_test',
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'label' => $this->randomMachineName(),
|
||||
'bundle' => $entity_type,
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
// Create an untranslatable test field.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'entity_type' => $entity_type,
|
||||
'field_name' => 'untranslatable_test_field',
|
||||
'type' => 'field_test',
|
||||
'translatable' => FALSE,
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'label' => $this->randomMachineName(),
|
||||
'bundle' => $entity_type,
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
// Create an entity with both translatable and untranslatable test fields.
|
||||
$values = array(
|
||||
'name' => $this->randomString(),
|
||||
'translatable_test_field' => $this->randomString(),
|
||||
'untranslatable_test_field' => $this->randomString(),
|
||||
);
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $controller->create($values);
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$entity->addTranslation($langcode, $values);
|
||||
}
|
||||
$entity->save();
|
||||
|
||||
// Assert there are no deleted languages in the lists yet.
|
||||
$this->assertNull(\Drupal::state()->get('entity_test.delete.translatable_test_field'));
|
||||
$this->assertNull(\Drupal::state()->get('entity_test.delete.untranslatable_test_field'));
|
||||
|
||||
// Remove the second and third langcodes from the entity.
|
||||
$entity->removeTranslation('l1');
|
||||
$entity->removeTranslation('l2');
|
||||
$entity->save();
|
||||
|
||||
// Ensure that for the translatable test field the second and third
|
||||
// langcodes are in the deleted languages list.
|
||||
$actual = \Drupal::state()->get('entity_test.delete.translatable_test_field');
|
||||
$expected_translatable = ['l1', 'l2'];
|
||||
sort($actual);
|
||||
sort($expected_translatable);
|
||||
$this->assertEqual($actual, $expected_translatable);
|
||||
// Ensure that the untranslatable test field is untouched.
|
||||
$this->assertNull(\Drupal::state()->get('entity_test.delete.untranslatable_test_field'));
|
||||
|
||||
// Delete the entity, which removes all remaining translations.
|
||||
$entity->delete();
|
||||
|
||||
// All languages have been deleted now.
|
||||
$actual = \Drupal::state()->get('entity_test.delete.translatable_test_field');
|
||||
$expected_translatable[] = 'en';
|
||||
$expected_translatable[] = 'l0';
|
||||
sort($actual);
|
||||
sort($expected_translatable);
|
||||
$this->assertEqual($actual, $expected_translatable);
|
||||
|
||||
// The untranslatable field is shared and only deleted once, for the
|
||||
// default langcode.
|
||||
$actual = \Drupal::state()->get('entity_test.delete.untranslatable_test_field');
|
||||
$expected_untranslatable = ['en'];
|
||||
sort($actual);
|
||||
sort($expected_untranslatable);
|
||||
$this->assertEqual($actual, $expected_untranslatable);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -48,6 +48,22 @@ class LocalActionTest extends WebTestBase {
|
|||
[Url::fromRoute('menu_test.local_action3'), 'My YAML discovery action'],
|
||||
[Url::fromRoute('menu_test.local_action5'), 'Title override'],
|
||||
]);
|
||||
// Test a local action title that changes based on a config value.
|
||||
$this->drupalGet(Url::fromRoute('menu_test.local_action6'));
|
||||
$this->assertLocalAction([
|
||||
[Url::fromRoute('menu_test.local_action5'), 'Original title'],
|
||||
]);
|
||||
// Verify the expected cache tag in the response headers.
|
||||
$header_values = explode(' ', $this->drupalGetHeader('x-drupal-cache-tags'));
|
||||
$this->assertTrue(in_array('config:menu_test.links.action', $header_values), "Found 'config:menu_test.links.action' cache tag in header");
|
||||
/** @var \Drupal\Core\Config\Config $config */
|
||||
$config = $this->container->get('config.factory')->getEditable('menu_test.links.action');
|
||||
$config->set('title', 'New title');
|
||||
$config->save();
|
||||
$this->drupalGet(Url::fromRoute('menu_test.local_action6'));
|
||||
$this->assertLocalAction([
|
||||
[Url::fromRoute('menu_test.local_action5'), 'New title'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,7 +48,7 @@ class AjaxPageStateTest extends WebTestBase {
|
|||
);
|
||||
$this->assertRaw(
|
||||
'/core/misc/drupalSettingsLoader.js',
|
||||
'The Dupalsettings library from core should be loaded.'
|
||||
'The drupalSettings library from core should be loaded.'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ class AjaxPageStateTest extends WebTestBase {
|
|||
|
||||
$this->assertRaw(
|
||||
'/core/misc/drupalSettingsLoader.js',
|
||||
'The Dupalsettings library from core should be loaded.'
|
||||
'The drupalSettings library from core should be loaded.'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ class AjaxPageStateTest extends WebTestBase {
|
|||
|
||||
$this->assertNoRaw(
|
||||
'/core/misc/drupalSettingsLoader.js',
|
||||
'The Dupalsettings library from core should be excluded from loading.'
|
||||
'The drupalSettings library from core should be excluded from loading.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,6 +109,10 @@ class CronRunTest extends WebTestBase {
|
|||
// fail randomly. Look for the word 'years', because without a timestamp,
|
||||
// the time will start at 1 January 1970.
|
||||
$this->assertNoText('years');
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
$this->assertUrl('admin/config/system/cron');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -86,6 +86,11 @@ class HtaccessTest extends WebTestBase {
|
|||
foreach ($file_exts_to_allow as $file_ext) {
|
||||
$file_paths["$path/access_test.$file_ext"] = 200;
|
||||
}
|
||||
|
||||
// Ensure composer.json and composer.lock cannot be accessed.
|
||||
$file_paths["$path/composer.json"] = 403;
|
||||
$file_paths["$path/composer.lock"] = 403;
|
||||
|
||||
return $file_paths;
|
||||
}
|
||||
|
||||
|
|
|
@ -65,27 +65,27 @@ class ThemeTest extends WebTestBase {
|
|||
// Raw stream wrapper URI.
|
||||
$file->uri => array(
|
||||
'form' => file_uri_target($file->uri),
|
||||
'src' => file_create_url($file->uri),
|
||||
'src' => file_url_transform_relative(file_create_url($file->uri)),
|
||||
),
|
||||
// Relative path within the public filesystem.
|
||||
file_uri_target($file->uri) => array(
|
||||
'form' => file_uri_target($file->uri),
|
||||
'src' => file_create_url($file->uri),
|
||||
'src' => file_url_transform_relative(file_create_url($file->uri)),
|
||||
),
|
||||
// Relative path to a public file.
|
||||
$file_relative => array(
|
||||
'form' => $file_relative,
|
||||
'src' => file_create_url($file->uri),
|
||||
'src' => file_url_transform_relative(file_create_url($file->uri)),
|
||||
),
|
||||
// Relative path to an arbitrary file.
|
||||
'core/misc/druplicon.png' => array(
|
||||
'form' => 'core/misc/druplicon.png',
|
||||
'src' => $GLOBALS['base_url'] . '/' . 'core/misc/druplicon.png',
|
||||
'src' => base_path() . 'core/misc/druplicon.png',
|
||||
),
|
||||
// Relative path to a file in a theme.
|
||||
$default_theme_path . '/logo.svg' => array(
|
||||
'form' => $default_theme_path . '/logo.svg',
|
||||
'src' => $GLOBALS['base_url'] . '/' . $default_theme_path . '/logo.svg',
|
||||
'src' => base_path() . $default_theme_path . '/logo.svg',
|
||||
),
|
||||
);
|
||||
foreach ($supported_paths as $input => $expected) {
|
||||
|
@ -186,7 +186,7 @@ class ThemeTest extends WebTestBase {
|
|||
':rel' => 'home',
|
||||
)
|
||||
);
|
||||
$this->assertEqual($elements[0]['src'], file_create_url($uploaded_filename));
|
||||
$this->assertEqual($elements[0]['src'], file_url_transform_relative(file_create_url($uploaded_filename)));
|
||||
|
||||
$this->container->get('theme_handler')->install(array('bartik'));
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ class EngineTwigTest extends WebTestBase {
|
|||
*/
|
||||
public function testTwigFileUrls() {
|
||||
$this->drupalGet('/twig-theme-test/file-url');
|
||||
$filepath = file_create_url('core/modules/system/tests/modules/twig_theme_test/twig_theme_test.js');
|
||||
$filepath = file_url_transform_relative(file_create_url('core/modules/system/tests/modules/twig_theme_test/twig_theme_test.js'));
|
||||
$this->assertRaw('<div>file_url: ' . $filepath . '</div>');
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Tests built-in image theme functions.
|
||||
|
@ -32,9 +33,17 @@ class ImageTest extends KernelTestBase {
|
|||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// The code under test uses file_url_transform_relative(), which relies on
|
||||
// the Request containing the correct hostname. KernelTestBase doesn't set
|
||||
// it, so push another request onto the stack to ensure it's correct.
|
||||
$request = Request::create('/', 'GET', [], [], [], $_SERVER);
|
||||
$this->container = $this->kernel->getContainer();
|
||||
$this->container->get('request_stack')->push($request);
|
||||
|
||||
$this->testImages = array(
|
||||
'/core/misc/druplicon.png',
|
||||
'/core/misc/loading.gif',
|
||||
'core/misc/druplicon.png',
|
||||
'core/misc/loading.gif',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -75,7 +84,7 @@ class ImageTest extends KernelTestBase {
|
|||
$this->render($image);
|
||||
|
||||
// Make sure the src attribute has the correct value.
|
||||
$this->assertRaw(file_create_url($image['#uri']), 'Correct output for an image with the src attribute.');
|
||||
$this->assertRaw(file_url_transform_relative(file_create_url($image['#uri'])), 'Correct output for an image with the src attribute.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,7 +112,7 @@ class ImageTest extends KernelTestBase {
|
|||
$this->render($image);
|
||||
|
||||
// Make sure the srcset attribute has the correct value.
|
||||
$this->assertRaw(file_create_url($this->testImages[0]) . ' 1x, ' . file_create_url($this->testImages[1]) . ' 2x', 'Correct output for image with srcset attribute and multipliers.');
|
||||
$this->assertRaw(file_url_transform_relative(file_create_url($this->testImages[0])) . ' 1x, ' . file_url_transform_relative(file_create_url($this->testImages[1])) . ' 2x', 'Correct output for image with srcset attribute and multipliers.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -135,7 +144,7 @@ class ImageTest extends KernelTestBase {
|
|||
$this->render($image);
|
||||
|
||||
// Make sure the srcset attribute has the correct value.
|
||||
$this->assertRaw(file_create_url($this->testImages[0]) . ' ' . $widths[0] . ', ' . file_create_url($this->testImages[1]) . ' ' . $widths[1], 'Correct output for image with srcset attribute and width descriptors.');
|
||||
$this->assertRaw(file_url_transform_relative(file_create_url($this->testImages[0])) . ' ' . $widths[0] . ', ' . file_url_transform_relative(file_create_url($this->testImages[1])) . ' ' . $widths[1], 'Correct output for image with srcset attribute and width descriptors.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,7 +43,9 @@ class UpdatePathTestJavaScriptTest extends UpdatePathTestBase {
|
|||
if (!isset($script['src'])) {
|
||||
continue;
|
||||
}
|
||||
$src = (string) $script['src'];
|
||||
// Source is a root-relative URL. Transform it to an absolute URL to allow
|
||||
// file_get_contents() to access the file.
|
||||
$src = preg_replace('#^' . $GLOBALS['base_path'] . '(.*)#i', $GLOBALS['base_url'] . '/' . '${1}', (string) $script['src']);
|
||||
$file_content = file_get_contents($src);
|
||||
|
||||
if (strpos($file_content, 'window.drupalSettings =') !== FALSE) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue