updated core and modules
This commit is contained in:
@@ -27,10 +27,6 @@ Devel Generate
|
||||
=================
|
||||
Bulk creates nodes, users, comment, terms for development. Has Drush integration.
|
||||
|
||||
Devel Node Access
|
||||
=================
|
||||
Prints the node_access records for a given node. Also offers hook_node_access_explain for all node access modules to implement.
|
||||
|
||||
Devel Generate Extensions
|
||||
=========================
|
||||
Devel Images Provider [http://drupal.org/project/devel_image_provider] allows to configure external providers for images.
|
||||
|
||||
@@ -9,6 +9,26 @@
|
||||
"email": "weitzman@tejasa.com",
|
||||
"homepage": "https://github.com/weitzman",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Hans Salvisberg",
|
||||
"email": "drupal@salvisberg.com",
|
||||
"homepage": "https://www.drupal.org/u/salvis",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Luca Lusso",
|
||||
"homepage": "https://www.drupal.org/u/lussoluca",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Marco (willzyx)",
|
||||
"homepage": "https://www.drupal.org/u/willzyx",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "See contributors",
|
||||
"homepage": "https://www.drupal.org/node/3236/committers"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
@@ -18,7 +38,7 @@
|
||||
},
|
||||
"license": "GPL-2.0+",
|
||||
"minimum-stability": "dev",
|
||||
"require": { },
|
||||
"require": {},
|
||||
"suggest": {
|
||||
"symfony/var-dumper": "Pretty print complex values better with var-dumper available"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
toolbar_items:
|
||||
- 'devel.admin_settings_link'
|
||||
- 'devel.cache_clear'
|
||||
- 'devel.container_info.service'
|
||||
- 'devel.execute_php'
|
||||
- 'devel.menu_rebuild'
|
||||
- 'devel.reinstall'
|
||||
- 'devel.route_info'
|
||||
- 'devel.run_cron'
|
||||
@@ -28,6 +28,16 @@ devel.settings:
|
||||
type: string
|
||||
label: 'Devel variable dumper'
|
||||
|
||||
devel.toolbar.settings:
|
||||
type: config_object
|
||||
label: 'Devel Toolbar settings'
|
||||
mapping:
|
||||
toolbar_items:
|
||||
type: sequence
|
||||
label: 'Toolbar items'
|
||||
sequence:
|
||||
type: string
|
||||
|
||||
block.settings.devel_switch_user:
|
||||
type: block_settings
|
||||
label: 'Switch user block'
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @file
|
||||
* Styling for devel toolbar module.
|
||||
*/
|
||||
|
||||
.toolbar .toolbar-tray-vertical .edit-devel-toolbar {
|
||||
text-align: right; /* LTR */
|
||||
padding: 1em;
|
||||
}
|
||||
[dir="rtl"] .toolbar .toolbar-tray-vertical .edit-devel-toolbar {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.toolbar .toolbar-tray-horizontal .edit-devel-toolbar {
|
||||
float: right; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .toolbar .toolbar-tray-horizontal .edit-devel-toolbar {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.toolbar .toolbar-tray-horizontal .toolbar-menu {
|
||||
float: left; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .toolbar .toolbar-tray-horizontal .toolbar-menu {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.toolbar .toolbar-bar .toolbar-icon-devel:before{
|
||||
background-image: url(../icons/bebebe/cog.svg);
|
||||
}
|
||||
.toolbar-bar .toolbar-icon-devel:active:before,
|
||||
.toolbar-bar .toolbar-icon-devel.is-active:before {
|
||||
background-image: url(../icons/ffffff/cog.svg);
|
||||
}
|
||||
|
||||
.toolbar-horizontal .toolbar-horizontal-item-hidden {
|
||||
display: none;
|
||||
}
|
||||
@@ -7,8 +7,8 @@ configure: devel.admin_settings
|
||||
tags:
|
||||
- developer
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-09-03
|
||||
version: '8.x-1.0-alpha1+23-dev'
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1472897643
|
||||
datestamp: 1502732047
|
||||
|
||||
@@ -5,6 +5,25 @@
|
||||
* Install, update and uninstall functions for the devel module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
function devel_requirements($phase) {
|
||||
$requirements = [];
|
||||
|
||||
if ($phase == 'runtime') {
|
||||
// To understand the reasons why this message is marked as info see
|
||||
// https://www.drupal.org/node/2834400.
|
||||
$requirements['devel'] = [
|
||||
'title' => t('Devel module enabled'),
|
||||
'description' => t('The Devel module provides access to internal debugging information; therefore it\'s recommended to disable this module on sites in production.'),
|
||||
'severity' => REQUIREMENT_INFO,
|
||||
];
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default devel dumper plugin.
|
||||
*/
|
||||
|
||||
@@ -3,3 +3,9 @@ devel:
|
||||
css:
|
||||
theme:
|
||||
css/devel.css: {}
|
||||
|
||||
devel-toolbar:
|
||||
version: VERSION
|
||||
css:
|
||||
component:
|
||||
css/devel.toolbar.css: {}
|
||||
|
||||
@@ -23,11 +23,6 @@ devel.menu_rebuild:
|
||||
route_name: devel.menu_rebuild
|
||||
menu_name: devel
|
||||
class: \Drupal\devel\Plugin\Menu\DestinationMenuLink
|
||||
devel.menu_item:
|
||||
title: 'Menu Item'
|
||||
route_name: devel.menu_item
|
||||
menu_name: devel
|
||||
class: \Drupal\devel\Plugin\Menu\MenuItemMenuLink
|
||||
devel.state_system_page:
|
||||
title: 'State editor'
|
||||
description: 'Edit state system values.'
|
||||
@@ -73,3 +68,26 @@ devel.run_cron:
|
||||
route_name: devel.run_cron
|
||||
menu_name: devel
|
||||
class: \Drupal\devel\Plugin\Menu\DestinationMenuLink
|
||||
|
||||
# Container info
|
||||
devel.container_info.service:
|
||||
title: 'Container Info'
|
||||
route_name: devel.container_info.service
|
||||
menu_name: devel
|
||||
|
||||
# Routes info
|
||||
devel.route_info:
|
||||
title: 'Routes Info'
|
||||
route_name: devel.route_info
|
||||
menu_name: devel
|
||||
devel.route_info.item:
|
||||
title: 'Current route info'
|
||||
route_name: devel.route_info.item
|
||||
menu_name: devel
|
||||
class: \Drupal\devel\Plugin\Menu\RouteDetailMenuLink
|
||||
|
||||
# Event info
|
||||
devel.event_info:
|
||||
title: 'Events Info'
|
||||
route_name: devel.event_info
|
||||
menu_name: devel
|
||||
|
||||
@@ -6,3 +6,13 @@ devel.admin_settings:
|
||||
route_name: devel.admin_settings
|
||||
base_route: devel.admin_settings
|
||||
weight: 0
|
||||
|
||||
# Container info
|
||||
devel.container_info.service:
|
||||
title: 'Services'
|
||||
route_name: devel.container_info.service
|
||||
base_route: devel.container_info.service
|
||||
devel.container_info.parameter:
|
||||
title: 'Parameters'
|
||||
route_name: devel.container_info.parameter
|
||||
base_route: devel.container_info.service
|
||||
|
||||
@@ -19,14 +19,51 @@ use Drupal\Core\Database\Query\AlterableInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Logger\RfcLogLevel;
|
||||
use Drupal\Core\Menu\LocalTaskDefault;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Utility\Error;
|
||||
use Drupal\devel\EntityTypeInfo;
|
||||
use Drupal\devel\ToolbarHandler;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function devel_help($route_name) {
|
||||
function devel_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.devel':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Devel module provides a suite of modules containing fun for module developers and themers. For more information, see the <a href=":url">online documentation for the Devel module</a>.', [':url' => 'https://www.drupal.org/docs/8/modules/devel']) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Inspecting Service Container') . '</dt>';
|
||||
$output .= '<dd>' . t('The module allows you to inspect Services and Parameters registered in the Service Container. You can see those informations on <a href=":url">Container info</a> page.', [':url' => Url::fromRoute('devel.container_info.service')->toString()]) . '</dd>';
|
||||
$output .= '<dt>' . t('Inspecting Routes') . '</dt>';
|
||||
$output .= '<dd>' . t('The module allows you to inspect routes information, gathering all routing data from <em>.routing.yml</em> files and from classes which subscribe to the route build/alter events. You can see those informations on <a href=":url">Routes info</a> page.', [':url' => Url::fromRoute('devel.route_info')->toString()]) . '</dd>';
|
||||
$output .= '<dt>' . t('Inspecting Events') . '</dt>';
|
||||
$output .= '<dd>' . t('The module allow you to inspect listeners registered in the event dispatcher. You can see those informations on <a href=":url">Events info</a> page.', [':url' => Url::fromRoute('devel.event_info')->toString()]) . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
|
||||
case 'devel.container_info.service':
|
||||
case 'devel.container_info.parameter':
|
||||
$output = '';
|
||||
$output .= '<p>' . t('Displays Services and Parameters registered in the Service Container. For more informations on the Service Container, see the <a href=":url">Symfony online documentation</a>.', [':url' => 'http://symfony.com/doc/current/service_container.html']) . '</p>';
|
||||
return $output;
|
||||
|
||||
case 'devel.route_info':
|
||||
$output = '';
|
||||
$output .= '<p>' . t('Displays registered routes for the site. For a complete overview of the routing system, see the <a href=":url">online documentation</a>.', [':url' => 'https://www.drupal.org/docs/8/api/routing-system']) . '</p>';
|
||||
return $output;
|
||||
|
||||
case 'devel.event_info':
|
||||
$output = '';
|
||||
$output .= '<p>' . t('Displays events and listeners registered in the event dispatcher. For a complete overview of the event system, see the <a href=":url">Symfony online documentation</a>.', [':url' => 'http://symfony.com/doc/current/components/event_dispatcher.html']) . '</p>';
|
||||
return $output;
|
||||
|
||||
case 'devel.reinstall':
|
||||
$output = '<p>' . t('<strong>Warning</strong> - will delete your module tables and configuration.') . '</p>';
|
||||
$output .= '<p>' . t('Uninstall and then install the selected modules. <code>hook_uninstall()</code> and <code>hook_install()</code> will be executed and the schema version number will be set to the most recent update number.') . '</p>';
|
||||
@@ -38,6 +75,11 @@ function devel_help($route_name) {
|
||||
case 'devel.state_system_page':
|
||||
return '<p>' . t('This is a list of state variables and their values. For more information read online documentation of <a href=":documentation">State API in Drupal 8</a>.', array(':documentation' => "https://www.drupal.org/developing/api/8/state")) . '</p>';
|
||||
|
||||
case 'devel.layout_info':
|
||||
$output = '';
|
||||
$output .= '<p>' . t('Displays layouts available to the site. For a complete overview of the layout system, see the <a href=":url">Layout API documentation</a>.', [':url' => 'https://www.drupal.org/docs/8/api/layout-api']) . '</p>';
|
||||
return $output;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,39 +87,57 @@ function devel_help($route_name) {
|
||||
* Implements hook_entity_type_alter().
|
||||
*/
|
||||
function devel_entity_type_alter(array &$entity_types) {
|
||||
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
|
||||
foreach ($entity_types as $entity_type_id => $entity_type) {
|
||||
if ($entity_type->hasViewBuilderClass() && $entity_type->hasLinkTemplate('canonical')) {
|
||||
$entity_type->setLinkTemplate('devel-render', "/devel/$entity_type_id/{{$entity_type_id}}/render");
|
||||
}
|
||||
if (($entity_type->getFormClass('default') || $entity_type->getFormClass('edit')) && $entity_type->hasLinkTemplate('edit-form')) {
|
||||
$entity_type->setLinkTemplate('devel-load', "/devel/$entity_type_id/{{$entity_type_id}}");
|
||||
}
|
||||
}
|
||||
return \Drupal::service('class_resolver')
|
||||
->getInstanceFromDefinition(EntityTypeInfo::class)
|
||||
->entityTypeAlter($entity_types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_operation().
|
||||
*/
|
||||
function devel_entity_operation(EntityInterface $entity) {
|
||||
$operations = array();
|
||||
if (\Drupal::currentUser()->hasPermission('access devel information')) {
|
||||
if ($entity->hasLinkTemplate('devel-load')) {
|
||||
$operations['devel'] = array(
|
||||
'title' => t('Devel'),
|
||||
'weight' => 100,
|
||||
'url' => $entity->toUrl('devel-load'),
|
||||
);
|
||||
}
|
||||
elseif ($entity->hasLinkTemplate('devel-render')) {
|
||||
$operations['devel'] = array(
|
||||
'title' => t('Devel'),
|
||||
'weight' => 100,
|
||||
'url' => $entity->toUrl('devel-render'),
|
||||
);
|
||||
}
|
||||
return \Drupal::service('class_resolver')
|
||||
->getInstanceFromDefinition(EntityTypeInfo::class)
|
||||
->entityOperation($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_toolbar().
|
||||
*/
|
||||
function devel_toolbar() {
|
||||
return \Drupal::service('class_resolver')
|
||||
->getInstanceFromDefinition(ToolbarHandler::class)
|
||||
->toolbar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_links_discovered_alter().
|
||||
*/
|
||||
function devel_menu_links_discovered_alter(&$links) {
|
||||
// Conditionally add the Layouts info menu link.
|
||||
if (\Drupal::moduleHandler()->moduleExists('layout_discovery')) {
|
||||
$links['devel.layout_info'] = [
|
||||
'title' => new TranslatableMarkup('Layouts Info'),
|
||||
'route_name' => 'devel.layout_info',
|
||||
'description' => new TranslatableMarkup('Overview of layouts available to the site.'),
|
||||
'menu_name' => 'devel',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_local_tasks_alter().
|
||||
*/
|
||||
function devel_local_tasks_alter(&$local_tasks) {
|
||||
if (\Drupal::moduleHandler()->moduleExists('toolbar')) {
|
||||
$local_tasks['devel.toolbar.settings_form'] = [
|
||||
'title' => 'Toolbar Settings',
|
||||
'base_route' => 'devel.admin_settings',
|
||||
'route_name' => 'devel.toolbar.settings_form',
|
||||
'class' => LocalTaskDefault::class,
|
||||
'options' => [],
|
||||
];
|
||||
}
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,7 +265,7 @@ function backtrace_error_handler($error_level, $message, $filename, $line, $cont
|
||||
drupal_set_message($msg, ($severity_level <= RfcLogLevel::NOTICE ? 'error' : 'warning'), TRUE);
|
||||
}
|
||||
if (!empty($error_handlers[DEVEL_ERROR_HANDLER_BACKTRACE_KINT])) {
|
||||
print kprint_r(ddebug_backtrace(TRUE, 1), $return = TRUE, $msg);
|
||||
print kpr(ddebug_backtrace(TRUE, 1), TRUE, $msg);
|
||||
}
|
||||
if (!empty($error_handlers[DEVEL_ERROR_HANDLER_BACKTRACE_DPM])) {
|
||||
dpm(ddebug_backtrace(TRUE, 1), $msg, 'warning');
|
||||
@@ -225,24 +285,6 @@ function devel_page_attachments_alter(&$page) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an object using either Kint (if enabled) or devel_print_object().
|
||||
*
|
||||
* @param array|object $object
|
||||
* An array or object to print.
|
||||
* @param string $prefix
|
||||
* @todo: this parameter is not needed with Kint.
|
||||
* Prefix for output items.
|
||||
*
|
||||
* @deprecated in Devel 8.x-dev, will be removed before Devel 8.0.
|
||||
* Use kpr() or devel.dumper service instead.
|
||||
*
|
||||
* @TODO remove in https://www.drupal.org/node/2703343
|
||||
*/
|
||||
function kdevel_print_object($object, $prefix = NULL) {
|
||||
return kpr($object, TRUE, $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for DevelDumperManager::dump().
|
||||
*
|
||||
@@ -364,18 +406,6 @@ function kpr($input, $export = FALSE, $name = NULL) {
|
||||
return \Drupal::service('devel.dumper')->dumpOrExport($input, $name, $export);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kint print.
|
||||
*
|
||||
* @deprecated in Devel 8.x-dev, will be removed before Devel 8.0.
|
||||
* Use kpr() or devel.dumper service instead.
|
||||
*
|
||||
* @TODO remove in https://www.drupal.org/node/2703343
|
||||
*/
|
||||
function kprint_r($input, $export = FALSE, $name = NULL, $function = 'print_r') {
|
||||
return kpr($input, $export, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for DevelDumperManager::dumpOrExport().
|
||||
*
|
||||
@@ -394,7 +424,7 @@ function dargs($always = TRUE) {
|
||||
static $printed;
|
||||
if ($always || !$printed) {
|
||||
$bt = debug_backtrace();
|
||||
print kdevel_print_object($bt[1]['args']);
|
||||
print kpr($bt[1]['args'], TRUE);
|
||||
$printed = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -444,7 +474,7 @@ function dpq($query, $return = FALSE, $name = NULL) {
|
||||
function devel_render() {
|
||||
$args = func_get_args();
|
||||
// #pre_render and #post_render pass the rendered $element as last argument.
|
||||
kprint_r(end($args));
|
||||
kpr(end($args));
|
||||
// #pre_render and #post_render expect the first argument to be returned.
|
||||
return reset($args);
|
||||
}
|
||||
@@ -517,51 +547,11 @@ function ddebug_backtrace($return = FALSE, $pop = 0, $options = DEBUG_BACKTRACE_
|
||||
if ($return) {
|
||||
return $nicetrace;
|
||||
}
|
||||
kprint_r($nicetrace);
|
||||
kpr($nicetrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Migration-related functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Regenerates the data in node_comment_statistics table.
|
||||
* Technique - http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#101
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function devel_rebuild_node_comment_statistics() {
|
||||
// Empty table.
|
||||
db_truncate('node_comment_statistics')->execute();
|
||||
|
||||
// TODO: DBTNG. Ignore keyword is Mysql only? Is only used in the rare case
|
||||
// when two comments on the same node share same timestamp.
|
||||
$sql = "
|
||||
INSERT IGNORE INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) (
|
||||
SELECT c.nid, c.cid, c.created, c.name, c.uid, c2.comment_count FROM {comment} c
|
||||
JOIN (
|
||||
SELECT c.nid, MAX(c.created) AS created, COUNT(*) AS comment_count FROM {comment} c WHERE status = 1 GROUP BY c.nid
|
||||
) AS c2 ON c.nid = c2.nid AND c.created = c2.created
|
||||
)";
|
||||
db_query($sql, array(':published' => CommentInterface::PUBLISHED));
|
||||
|
||||
// Insert records into the node_comment_statistics for nodes that are missing.
|
||||
$query = db_select('node', 'n');
|
||||
$query->leftJoin('node_comment_statistics', 'ncs', 'ncs.nid = n.nid');
|
||||
$query->addField('n', 'changed', 'last_comment_timestamp');
|
||||
$query->addField('n', 'uid', 'last_comment_uid');
|
||||
$query->addField('n', 'nid');
|
||||
$query->addExpression('0', 'comment_count');
|
||||
$query->addExpression('NULL', 'last_comment_name');
|
||||
$query->isNull('ncs.comment_count');
|
||||
|
||||
db_insert('node_comment_statistics', array('return' => Database::RETURN_NULL))
|
||||
->from($query)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*
|
||||
|
||||
@@ -6,6 +6,15 @@ devel.admin_settings:
|
||||
requirements:
|
||||
_permission: 'administer site configuration'
|
||||
|
||||
devel.toolbar.settings_form:
|
||||
path: '/admin/config/development/devel/toolbar'
|
||||
defaults:
|
||||
_form: '\Drupal\devel\Form\ToolbarSettingsForm'
|
||||
_title: 'Devel Toolbar Settings'
|
||||
requirements:
|
||||
_permission: 'administer site configuration'
|
||||
_module_dependencies: 'toolbar'
|
||||
|
||||
devel.reinstall:
|
||||
path: '/devel/reinstall'
|
||||
defaults:
|
||||
@@ -19,8 +28,8 @@ devel.reinstall:
|
||||
devel.menu_rebuild:
|
||||
path: '/devel/menu/reset'
|
||||
defaults:
|
||||
_form: '\Drupal\devel\Form\DevelRebuildMenus'
|
||||
_title: 'Rebuild menus'
|
||||
_form: '\Drupal\devel\Form\RouterRebuildConfirmForm'
|
||||
_title: 'Rebuild router'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
@@ -41,7 +50,7 @@ devel.config_edit:
|
||||
path: '/devel/config/edit/{config_name}'
|
||||
defaults:
|
||||
_form: '\Drupal\devel\Form\ConfigEditor'
|
||||
_title: 'Edit configuration object: !config_name'
|
||||
_title: 'Edit configuration object: @config_name'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
@@ -67,16 +76,6 @@ devel.system_state_edit:
|
||||
requirements:
|
||||
_permission: 'administer site configuration'
|
||||
|
||||
devel.menu_item:
|
||||
path: '/devel/menu/item'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\DevelController::menuItem'
|
||||
_title: 'Menu item'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel.theme_registry:
|
||||
path: '/devel/theme/registry'
|
||||
defaults:
|
||||
@@ -87,16 +86,6 @@ devel.theme_registry:
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel.entity_info_page:
|
||||
path: '/devel/entity/info'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\DevelController::entityInfoPage'
|
||||
_title: 'Entity info'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel.field_info_page:
|
||||
path: '/devel/field/info'
|
||||
defaults:
|
||||
@@ -139,16 +128,6 @@ devel.switch:
|
||||
_permission: 'switch users'
|
||||
_csrf_token: 'TRUE'
|
||||
|
||||
devel.elements_page:
|
||||
path: '/devel/elements'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\DevelController::elementsPage'
|
||||
_title: 'Element Info'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel.cache_clear:
|
||||
path: '/devel/cache/clear'
|
||||
defaults:
|
||||
@@ -170,3 +149,130 @@ devel.run_cron:
|
||||
requirements:
|
||||
_permission: 'administer site configuration'
|
||||
_csrf_token: 'TRUE'
|
||||
|
||||
# Container info
|
||||
devel.container_info.service:
|
||||
path: '/devel/container/service'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\ContainerInfoController::serviceList'
|
||||
_title: 'Container services'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel.container_info.service.detail:
|
||||
path: '/devel/container/service/{service_id}'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\ContainerInfoController::serviceDetail'
|
||||
_title: 'Service @service_id detail'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel.container_info.parameter:
|
||||
path: '/devel/container/parameter'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\ContainerInfoController::parameterList'
|
||||
_title: 'Container parameters'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel.container_info.parameter.detail:
|
||||
path: '/devel/container/parameter/{parameter_name}'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\ContainerInfoController::parameterDetail'
|
||||
_title: 'Parameter @parameter_name value'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
# Route info
|
||||
devel.route_info:
|
||||
path: '/devel/routes'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\RouteInfoController::routeList'
|
||||
_title: 'Routes'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel.route_info.item:
|
||||
path: '/devel/routes/item'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\RouteInfoController::routeDetail'
|
||||
_title: 'Route detail'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
# Event info
|
||||
devel.event_info:
|
||||
path: '/devel/events'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\EventInfoController::eventList'
|
||||
_title: 'Events'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
# Layouts info
|
||||
devel.layout_info:
|
||||
path: '/devel/layouts'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\LayoutInfoController::layoutInfoPage'
|
||||
_title: 'Layouts'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
_module_dependencies: 'layout_discovery'
|
||||
|
||||
# Element info
|
||||
devel.elements_page:
|
||||
path: '/devel/elements'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\ElementInfoController::elementList'
|
||||
_title: 'Element Info'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel.elements_page.detail:
|
||||
path: '/devel/elements/{element_name}'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\ElementInfoController::elementDetail'
|
||||
_title: 'Element @element_name'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
# Entity type info
|
||||
devel.entity_info_page:
|
||||
path: '/devel/entity/info'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\EntityTypeInfoController::entityTypeList'
|
||||
_title: 'Entity info'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
devel.entity_info_page.detail:
|
||||
path: '/devel/entity/info/{entity_type_id}'
|
||||
defaults:
|
||||
_controller: '\Drupal\devel\Controller\EntityTypeInfoController::entityTypeDetail'
|
||||
_title: 'Entity type @entity_type_id'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
requirements:
|
||||
_permission: 'access devel information'
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_rules_action_info().
|
||||
*/
|
||||
function devel_rules_action_info() {
|
||||
return array(
|
||||
'devel_debug' => array(
|
||||
'base' => 'devel_rules_debug_action',
|
||||
'label' => t('Debug value'),
|
||||
'group' => t('Devel'),
|
||||
'parameter' => array(
|
||||
'value' => array('type' => 'unknown', 'label' => t('Value to debug')),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules action for debugging values.
|
||||
*/
|
||||
function devel_rules_debug_action($value) {
|
||||
dpm($value);
|
||||
}
|
||||
@@ -1,7 +1,19 @@
|
||||
services:
|
||||
devel.event_subscriber:
|
||||
class: Drupal\devel\EventSubscriber\DevelEventSubscriber
|
||||
arguments: ['@config.factory', '@current_user', '@module_handler', '@url_generator']
|
||||
devel.error_subscriber:
|
||||
class: Drupal\devel\EventSubscriber\ErrorHandlerSubscriber
|
||||
arguments: ['@current_user']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
|
||||
devel.theme_rebuild_subscriber:
|
||||
class: Drupal\devel\EventSubscriber\ThemeInfoRebuildSubscriber
|
||||
arguments: ['@config.factory', '@current_user', '@theme_handler']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
|
||||
devel.route_subscriber:
|
||||
class: Drupal\devel\Routing\RouteSubscriber
|
||||
arguments: ['@entity_type.manager']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
|
||||
@@ -13,12 +25,6 @@ services:
|
||||
class: Drupal\devel\DevelDumperManager
|
||||
arguments: ['@config.factory', '@current_user', '@plugin.manager.devel_dumper']
|
||||
|
||||
devel.route_subscriber:
|
||||
class: Drupal\devel\Routing\RouteSubscriber
|
||||
arguments: ['@entity_type.manager']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
|
||||
devel.twig.debug_extension:
|
||||
class: Drupal\devel\Twig\Extension\Debug
|
||||
arguments: ['@devel.dumper']
|
||||
|
||||
@@ -36,7 +36,9 @@ You can alter existing properties for every plugin implementing hook_devel_gener
|
||||
DevelGenerateBaseInterface details base wrapping methods that most DevelGenerate implementations
|
||||
will want to directly inherit from Drupal\devel_generate\DevelGenerateBase.
|
||||
|
||||
DevelGenerateFieldBaseInterface details base wrapping methods that most class implementations
|
||||
for supporting new field types will want to directly inherit from Drupal\devel_generate\DevelGenerateFieldBase.
|
||||
So to give support for a new field type should be enough to create a class called
|
||||
"DevelGenerateFieldNewfieldtype" extending DevelGenerateFieldBase and to implement "generateValues" method.
|
||||
To give support for a new field type the field type base class should properly
|
||||
implements \Drupal\Core\Field\FieldItemInterface::generateSampleValue().
|
||||
Devel generate automatically use the values returned by this method during the
|
||||
generate process for generate placeholder field values. For more information
|
||||
see:
|
||||
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Field%21FieldItemInterface.php/function/FieldItemInterface::generateSampleValue
|
||||
|
||||
@@ -6,8 +6,8 @@ package: Development
|
||||
tags:
|
||||
- developer
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-09-03
|
||||
version: '8.x-1.0-alpha1+23-dev'
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1472897643
|
||||
datestamp: 1502732047
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
services:
|
||||
develgenerate.command:
|
||||
class: Drupal\devel_generate\Commands\DevelGenerateCommands
|
||||
arguments: ['@plugin.manager.develgenerate']
|
||||
tags:
|
||||
- { name: drush.command }
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Unish;
|
||||
|
||||
if (class_exists('Unish\CommandUnishTestCase')) {
|
||||
|
||||
/**
|
||||
* Tests for devel_generate drush commands.
|
||||
*
|
||||
* @group devel_generate
|
||||
*/
|
||||
class DevelGenerateUnishTest extends CommandUnishTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
if (UNISH_DRUPAL_MAJOR_VERSION < 8) {
|
||||
$this->markTestSkipped('Devel Generate Tests only available on D8+.');
|
||||
}
|
||||
|
||||
if (!$this->getSites()) {
|
||||
$this->setUpDrupal(1, TRUE, UNISH_DRUPAL_MAJOR_VERSION, 'standard');
|
||||
|
||||
// Symlink the devel module into the sandbox.
|
||||
$devel_directory = dirname(dirname(__DIR__));
|
||||
symlink($devel_directory, $this->webroot() . '/modules/devel');
|
||||
|
||||
// Enable the devel_generate modules.
|
||||
$this->drush('pm-enable', ['devel_generate'], $this->getOptions());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel generate terms.
|
||||
*/
|
||||
public function testDevelGenerateTerms() {
|
||||
$this->drush('pm-enable', ['taxonomy'], $this->getOptions());
|
||||
|
||||
$this->drush('generate-terms', [], $this->getOptions(), NULL, NULL, static::EXIT_ERROR);
|
||||
$this->assertContains('Please provide a vocabulary machine name.', $this->getErrorOutput());
|
||||
|
||||
$this->drush('generate-terms', ['unknown'], $this->getOptions(), NULL, NULL, static::EXIT_ERROR);
|
||||
$this->assertContains('Invalid vocabulary name: unknown', $this->getErrorOutput());
|
||||
|
||||
$this->drush('generate-terms', ['tags', 'NaN'], $this->getOptions(), NULL, NULL, static::EXIT_ERROR);
|
||||
$this->assertContains('Invalid number of terms: NaN', $this->getErrorOutput());
|
||||
|
||||
$eval_term_count = "return \\Drupal::entityQuery('taxonomy_term')->count()->execute();";
|
||||
$eval_options = $this->getOptions() + ['format' => 'string'];
|
||||
|
||||
$this->drush('generate-terms', ['tags'], $this->getOptions());
|
||||
$this->assertContains('Created the following new terms:', $this->getErrorOutput());
|
||||
$this->drush('php-eval', [$eval_term_count], $eval_options);
|
||||
$this->assertEquals(10, $this->getOutput());
|
||||
|
||||
$this->drush('generate-terms', ['tags', '1'], $this->getOptions());
|
||||
$this->assertContains('Created the following new terms:', $this->getErrorOutput());
|
||||
$this->drush('php-eval', [$eval_term_count], $eval_options);
|
||||
$this->assertEquals(11, $this->getOutput());
|
||||
|
||||
$this->drush('generate-terms', ['tags', '1'], $this->getOptions(TRUE));
|
||||
$this->assertContains('Deleted existing terms.', $this->getErrorOutput());
|
||||
$this->assertContains('Created the following new terms:', $this->getErrorOutput());
|
||||
$this->drush('php-eval', [$eval_term_count], $eval_options);
|
||||
$this->assertEquals(1, $this->getOutput());
|
||||
|
||||
$this->drush('gent', ['tags', '1'], $this->getOptions());
|
||||
$this->assertContains('Created the following new terms:', $this->getErrorOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel generate contents.
|
||||
*/
|
||||
public function testDevelGenerateContents() {
|
||||
$this->drush('pm-enable', ['node'], $this->getOptions());
|
||||
|
||||
$eval_content_count = "return \\Drupal::entityQuery('node')->count()->execute();";
|
||||
$eval_options = $this->getOptions() + ['format' => 'string'];
|
||||
|
||||
// Try to generate 10 content of type "page" or "article"
|
||||
$this->drush('generate-content', [10], $this->getOptions(), NULL, NULL, static::EXIT_SUCCESS);
|
||||
$this->assertContains('Finished creating 10 nodes', $this->getErrorOutput());
|
||||
$this->drush('php-eval', [$eval_content_count], $eval_options);
|
||||
$this->assertEquals(10, $this->getOutput());
|
||||
|
||||
// Try to generate 1 content of type "page" or "article"
|
||||
$this->drush('generate-content', [1], $this->getOptions(), NULL, NULL, static::EXIT_SUCCESS);
|
||||
$this->assertContains('1 node created.', $this->getErrorOutput());
|
||||
$this->drush('php-eval', [$eval_content_count], $eval_options);
|
||||
$this->assertEquals(11, $this->getOutput());
|
||||
|
||||
// Try to generate 5 content of type "page" or "article", removing all
|
||||
// previous contents.
|
||||
$this->drush('generate-content', [5], $this->getOptions(TRUE), NULL, NULL, static::EXIT_SUCCESS);
|
||||
$this->assertContains('Finished creating 5 nodes', $this->getErrorOutput());
|
||||
$this->drush('php-eval', [$eval_content_count], $eval_options);
|
||||
$this->assertEquals(5, $this->getOutput());
|
||||
|
||||
// Try to generate other 5 content with "crappy" type. Output should
|
||||
// remains 5.
|
||||
$generate_content_wrong_ct = $this->getOptions(TRUE) + ['types' => 'crappy'];
|
||||
$this->drush('generate-content', [5], $generate_content_wrong_ct, NULL, NULL, static::EXIT_ERROR);
|
||||
$this->assertContains('One or more content types have been entered that don', $this->getErrorOutput());
|
||||
$this->drush('php-eval', [$eval_content_count], $eval_options);
|
||||
$this->assertEquals(5, $this->getOutput());
|
||||
|
||||
// Try to generate other 5 content with empty types. Output should
|
||||
// remains 5.
|
||||
$generate_content_no_types = $this->getOptions(TRUE) + ['types' => ''];
|
||||
$this->drush('generate-content', [5], $generate_content_no_types, NULL, NULL, static::EXIT_ERROR);
|
||||
$this->assertContains('No content types available', $this->getErrorOutput());
|
||||
$this->drush('php-eval', [$eval_content_count], $eval_options);
|
||||
$this->assertEquals(5, $this->getOutput());
|
||||
|
||||
// Try to generate other 5 content without any types. Output should
|
||||
// remains 5.
|
||||
$generate_content_no_types = $this->getOptions(TRUE) + ['types' => NULL];
|
||||
$this->drush('generate-content', [5], $generate_content_no_types, NULL, NULL, static::EXIT_ERROR);
|
||||
$this->assertContains('Wrong syntax or no content type selected. The correct syntax uses', $this->getErrorOutput());
|
||||
$this->drush('php-eval', [$eval_content_count], $eval_options);
|
||||
$this->assertEquals(5, $this->getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* Default drush options.
|
||||
*
|
||||
* @param bool $kill
|
||||
* Whether add kill option.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the default options for drush commands.
|
||||
*/
|
||||
protected function getOptions($kill = FALSE) {
|
||||
$options = [
|
||||
'yes' => NULL,
|
||||
'root' => $this->webroot(),
|
||||
'uri' => key($this->getSites()),
|
||||
];
|
||||
|
||||
if($kill) {
|
||||
$options['kill'] = NULL;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Generate content, taxonomy, menu, and users via drush framework.
|
||||
* Integration with Drush8. Drush9 commands are in src/Commands.
|
||||
*/
|
||||
use Drupal\devel_generate\DevelGenerateBaseInterface;
|
||||
use Drupal\devel_generate\DevelGeneratePluginManager;
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
namespace Drupal\devel_generate\Commands;
|
||||
|
||||
use Consolidation\AnnotatedCommand\CommandData;
|
||||
use Drupal\devel_generate\DevelGenerateBaseInterface;
|
||||
use Drush\Commands\DrushCommands;
|
||||
|
||||
/**
|
||||
* For commands that are parts of modules, Drush expects to find commandfiles in
|
||||
* __MODULE__/src/Commands, and the namespace is Drupal/__MODULE__/Commands.
|
||||
*
|
||||
* In addition to a commandfile like this one, you need to add a drush.services.yml
|
||||
* in root of your module like this module does.
|
||||
*/
|
||||
class DevelGenerateCommands extends DrushCommands {
|
||||
|
||||
/**
|
||||
* @var DevelGenerateBaseInterface $manager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* The plugin instance.
|
||||
*
|
||||
* @var DevelGenerateBaseInterface $instance
|
||||
*/
|
||||
protected $pluginInstance;
|
||||
|
||||
/**
|
||||
* The Generate plugin parameters.
|
||||
*
|
||||
* @var array $parameters
|
||||
*/
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* DevelGenerateCommands constructor.
|
||||
* @param $manager
|
||||
*/
|
||||
public function __construct($manager) {
|
||||
parent::__construct();
|
||||
$this->setManager($manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\devel_generate\DevelGenerateBaseInterface
|
||||
*/
|
||||
public function getManager() {
|
||||
return $this->manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\devel_generate\DevelGenerateBaseInterface $manager
|
||||
*/
|
||||
public function setManager($manager) {
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPluginInstance() {
|
||||
return $this->pluginInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $pluginInstance
|
||||
*/
|
||||
public function setPluginInstance($pluginInstance) {
|
||||
$this->pluginInstance = $pluginInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getParameters() {
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parameters
|
||||
*/
|
||||
public function setParameters($parameters) {
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create users.
|
||||
*
|
||||
* @command devel-generate-users
|
||||
* @pluginId user
|
||||
* @param $num Number of users to generate.
|
||||
* @option kill Delete all users before generating new ones.
|
||||
* @option roles A comma delimited list of role IDs for new users. Don't specify 'authenticated'.
|
||||
* @option pass Specify a password to be set for all generated users.
|
||||
* @aliases genu
|
||||
*/
|
||||
public function users($num = 50, $options = ['kill' => FALSE, 'roles' => '']) {
|
||||
// @todo pass $options to the plugins.
|
||||
$this->generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create terms in specified vocabulary.
|
||||
*
|
||||
* @command devel-generate-terms
|
||||
* @pluginId term
|
||||
* @param $machine_name Vocabulary machine name into which new terms will be inserted.
|
||||
* @param $num Number of terms to generate.
|
||||
* @option kill Delete all terms before generating new ones.
|
||||
* @option feedback An integer representing interval for insertion rate logging.
|
||||
* @validate-entity-load taxonomy_vocabulary machine_name
|
||||
* @aliases gent
|
||||
*/
|
||||
public function terms($machine_name, $num = 50, $options = ['feedback' => 1000]) {
|
||||
$this->generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create vocabularies.
|
||||
*
|
||||
* @command devel-generate-vocabs
|
||||
* @pluginId vocabulary
|
||||
* @param $num Number of vocabularies to generate.
|
||||
* @option kill Delete all vocabs before generating new ones.
|
||||
* @aliases genv
|
||||
* @validate-module-enabled taxonomy
|
||||
*/
|
||||
public function vocabs($num = 1, $options = ['kill' => FALSE]) {
|
||||
$this->generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create menus.
|
||||
*
|
||||
* @command devel-generate-menus
|
||||
* @pluginId menu
|
||||
* @param $number_menus Number of menus to generate.
|
||||
* @param $number_links Number of links to generate.
|
||||
* @param $max_depth Max link depth.
|
||||
* @param $max_width Max width of first level of links.
|
||||
* @option kill Delete all content before generating new content.
|
||||
* @aliases genm
|
||||
*/
|
||||
public function menus($number_menus = 2, $number_links = 50, $max_depth = 3, $max_width = 8, $options = ['kill' => FALSE]) {
|
||||
$this->generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content.
|
||||
*
|
||||
* @command devel-generate-content
|
||||
* @pluginId content
|
||||
* @param $num Number of nodes to generate.
|
||||
* @param $max_comments Maximum number of comments to generate.
|
||||
* @option kill Delete all content before generating new content.
|
||||
* @option types A comma delimited list of content types to create. Defaults to page,article.
|
||||
* @option feedback An integer representing interval for insertion rate logging.
|
||||
* @option skip-fields A comma delimited list of fields to omit when generating random values
|
||||
* @option languages A comma-separated list of language codes
|
||||
* @aliases genc
|
||||
* @validate-module-enabled node
|
||||
*/
|
||||
public function content($num = 50, $max_comments = 0, $options = ['kill' => FALSE, 'types' => 'page,article', 'feedback' => 1000]) {
|
||||
$this->generate();
|
||||
drush_backend_batch_process();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hook validate
|
||||
* @param \Consolidation\AnnotatedCommand\CommandData $commandData
|
||||
* @return \Consolidation\AnnotatedCommand\CommandError|null
|
||||
*/
|
||||
public function validate(CommandData $commandData) {
|
||||
$manager = $this->getManager();
|
||||
$args = $commandData->input()->getArguments();
|
||||
$commandName = array_shift($args);
|
||||
/** @var DevelGenerateBaseInterface $instance */
|
||||
$instance = $manager->createInstance($commandData->annotationData()->get('pluginId'), array());
|
||||
$this->setPluginInstance($instance);
|
||||
$parameters = $instance->validateDrushParams($args);
|
||||
$this->setParameters($parameters);
|
||||
}
|
||||
|
||||
public function generate() {
|
||||
$instance = $this->getPluginInstance();
|
||||
$instance->generate($this->getParameters());
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -14,6 +14,7 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Routing\UrlGeneratorInterface;
|
||||
use Drupal\devel_generate\DevelGenerateBase;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drush\Utils\StringUtils;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
@@ -343,7 +344,7 @@ class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactory
|
||||
$operations[] = array('devel_generate_operation', array($this, 'batchContentAddNode', $values));
|
||||
}
|
||||
|
||||
// Start the batch.
|
||||
// Set the batch.
|
||||
$batch = array(
|
||||
'title' => $this->t('Generating Content'),
|
||||
'operations' => $operations,
|
||||
@@ -386,27 +387,27 @@ class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactory
|
||||
$values['max_comments'] = array_shift($args);
|
||||
$all_types = array_keys(node_type_get_names());
|
||||
$default_types = array_intersect(array('page', 'article'), $all_types);
|
||||
$selected_types = _convert_csv_to_array(drush_get_option('types', $default_types));
|
||||
$selected_types = StringUtils::csvToArray(drush_get_option('types', $default_types));
|
||||
|
||||
// Validates the input format for content types option.
|
||||
if (drush_get_option('types', $default_types) === TRUE) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Wrong syntax or no content type selected. The correct syntax uses "=", eg.: --types=page,article'));
|
||||
throw new \Exception(dt('Wrong syntax or no content type selected. The correct syntax uses "=", eg.: --types=page,article'));
|
||||
}
|
||||
|
||||
if (empty($selected_types)) {
|
||||
return drush_set_error('DEVEL_GENERATE_NO_CONTENT_TYPES', dt('No content types available'));
|
||||
throw new \Exception(dt('No content types available'));
|
||||
}
|
||||
|
||||
$values['node_types'] = array_combine($selected_types, $selected_types);
|
||||
$node_types = array_filter($values['node_types']);
|
||||
|
||||
if (!empty($values['kill']) && empty($node_types)) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Please provide content type (--types) in which you want to delete the content.'));
|
||||
throw new \Exception(dt('Please provide content type (--types) in which you want to delete the content.'));
|
||||
}
|
||||
|
||||
// Checks for any missing content types before generating nodes.
|
||||
if (array_diff($node_types, $all_types)) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('One or more content types have been entered that don\'t exist on this site'));
|
||||
throw new \Exception(dt('One or more content types have been entered that don\'t exist on this site'));
|
||||
}
|
||||
|
||||
return $values;
|
||||
@@ -437,7 +438,6 @@ class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactory
|
||||
protected function develGenerateContentPreNode(&$results) {
|
||||
// Get user id.
|
||||
$users = $this->getUsers();
|
||||
$users = array_merge($users, array('0'));
|
||||
$results['users'] = $users;
|
||||
}
|
||||
|
||||
@@ -492,7 +492,7 @@ class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive 50 uids from the database.
|
||||
* Retrieve 50 uids from the database.
|
||||
*/
|
||||
protected function getUsers() {
|
||||
$users = array();
|
||||
|
||||
+3
-3
@@ -7,8 +7,8 @@ configure: admin/config/development/generate
|
||||
tags:
|
||||
- developer
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-09-03
|
||||
version: '8.x-1.0-alpha1+23-dev'
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1472897643
|
||||
datestamp: 1502732047
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
README
|
||||
======
|
||||
|
||||
This module contains tools for developers using access control modules
|
||||
to restrict access to some nodes. It is intended to help catch some
|
||||
common mistakes and provide feedback to confirm that restricted nodes
|
||||
are in fact visible only to the intended users.
|
||||
|
||||
Provides a summary page which queries the node_access table and
|
||||
reports common mistakes such as the presence of Drupal's default entry
|
||||
which grants all users read access to all nodes. Also reports the
|
||||
presence of nodes not represented in node_access table. This may
|
||||
occur when an access control module is installed after nodes have
|
||||
already been created.
|
||||
|
||||
Provides a block which shows all node_access entries for the nodes
|
||||
shown on a given page. This gives developers a quick check to see
|
||||
that grants are provided as they should be. This block auto-enables
|
||||
to the footer region. You may move it as desired.
|
||||
|
||||
If Views module is installed, allows browsing of nodes by realm,
|
||||
including those nodes not in the node_access table (NULL realm).
|
||||
|
||||
WISHLIST
|
||||
========
|
||||
|
||||
Things I'd like to see but haven't had time to do:
|
||||
|
||||
* Automatically solve common problems. I.e. delete the "all" realm
|
||||
entry, and automatically save all nodes not in the node_access table.
|
||||
|
||||
* Nicer feedback indicating whether nodes are visible to the public or
|
||||
not. I.e. use color coding or icons.
|
||||
|
||||
* Summary does not differentiate between view grants and other types
|
||||
of grants. I personally use node_access only for view grants so I'm
|
||||
not sure exactly what else it should show.
|
||||
|
||||
AUTHOR
|
||||
======
|
||||
|
||||
Dave Cohen AKA yogadex on drupal.org
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
debug_mode: FALSE
|
||||
by_user_mode: FALSE
|
||||
user_ajax: FALSE
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
# Schema for the configuration files of the Devel Node Access module.
|
||||
|
||||
devel_node_access.settings:
|
||||
type: mapping
|
||||
label: 'Devel Node Access settings'
|
||||
mapping:
|
||||
debug_mode:
|
||||
type: boolean
|
||||
label: 'Debug Mode'
|
||||
by_user_mode:
|
||||
type: boolean
|
||||
label: 'By-User Analysis'
|
||||
user_ajax:
|
||||
type: boolean
|
||||
label: 'User AJAX'
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
div.devel-node-access-inline > div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hook provided by the Devel Node Access module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Explain your records in the {node_access} table.
|
||||
*
|
||||
* In order to help developers and administrators understand the forces
|
||||
* that control access to any given node, the DNA module provides the
|
||||
* Devel Node Access block, which lists all the grant records in the
|
||||
* {node_access} table for that node.
|
||||
*
|
||||
* However, every Node Access module is free in how it defines and uses the
|
||||
* 'realm' and 'gid' fields in its records in the {node_access} table, and
|
||||
* it's often difficult to interpret them. This hook passes each record
|
||||
* that DNA wants to display, and the owning module is expected to return
|
||||
* an explanation of that record.
|
||||
*
|
||||
* The explanation should not be localized (not be passed through t()), so
|
||||
* that administrators seeking help can present English explanations.
|
||||
*
|
||||
* @param $row
|
||||
* The record from the {node_access} table, as object. The member fields are:
|
||||
* nid, gid, realm, grant_view, grant_update, grant_delete.
|
||||
*
|
||||
* @return string|null
|
||||
* A string with a (short!) explanation of the given {node_access} row,
|
||||
* to be displayed in DNA's 'Devel Node Access' block. It will be displayed
|
||||
* as HTML; any variable parts must already be sanitized.
|
||||
*
|
||||
* @see hook_node_access_records()
|
||||
* @see devel_node_access_node_access_explain()
|
||||
*
|
||||
* @ingroup node_access
|
||||
*/
|
||||
function hook_node_access_explain($row) {
|
||||
if ($row->realm == 'mymodule_myrealm') {
|
||||
if ($row->grant_view) {
|
||||
/** @var \Drupal\user\RoleInterface $role */
|
||||
$role = \Drupal\user\Entity\Role::load($row->gid);
|
||||
return t('Role %role may view this node.', array('%role' => $role->get('name')));
|
||||
}
|
||||
else {
|
||||
return 'No access.';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledge ownership of 'alien' grant records.
|
||||
*
|
||||
* Some node access modules store grant records directly into the {node_access}
|
||||
* table rather than returning them through hook_node_access_records(). This
|
||||
* practice is not recommended and DNA will flag all such records as 'alien'.
|
||||
*
|
||||
* If this is unavoidable, a module can confess to being the owner of these
|
||||
* grant records, so that DNA can properly attribute them.
|
||||
*
|
||||
* @see hook_node_access_records()
|
||||
*
|
||||
* @ingroup node_access
|
||||
*/
|
||||
function hook_node_access_acknowledge($grant) {
|
||||
if ($grant['realm'] == 'mymodule_all' && $grant['nid'] == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
||||
@@ -1,16 +0,0 @@
|
||||
type: module
|
||||
name: 'Devel Node Access'
|
||||
description: 'Developer blocks and page illustrating relevant node_access records.'
|
||||
package: Development
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- devel
|
||||
- menu_ui
|
||||
tags:
|
||||
- developer
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-09-03
|
||||
version: '8.x-1.0-alpha1+23-dev'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1472897643
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the Devel Node Access module.
|
||||
*/
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/**
|
||||
* @file devel_node_access.js.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* Perform the access by user ajax request.
|
||||
*/
|
||||
function devel_node_access_user_ajax(context, settings) {
|
||||
// Get the cell ID for the first .dna-permission that isn't processed.
|
||||
var cell = $('td.dna-permission', context)
|
||||
.not('.ajax-processed', context)
|
||||
.attr('id');
|
||||
if (cell !== undefined) {
|
||||
// Generate the URI from the basePath, path, data type, cell ID, and a
|
||||
// random token to bypass caching.
|
||||
var url = settings.path.basePath
|
||||
+ "?q="
|
||||
+ 'devel/node_access/by_user/json/'
|
||||
+ cell
|
||||
+ '/'
|
||||
+ Math.floor((1000000000 * Math.random())).toString(16);
|
||||
// Execute Ajax callback and handle the response.
|
||||
$.getJSON(url, function(data) {
|
||||
$('#' + cell, context).html(data).addClass('ajax-processed');
|
||||
// Call this function again.
|
||||
devel_node_access_user_ajax(context);
|
||||
});
|
||||
// Ajax fails silently on error, mark bad requests with an error message.
|
||||
// If the request is just slow this will update when the request succeeds.
|
||||
setTimeout(
|
||||
function() {
|
||||
if ($('#' + cell, context).hasClass('ajax-processed') == false) {
|
||||
$('#' + cell, context)
|
||||
.html(
|
||||
'<span class="error">'
|
||||
+ '<a href="' + url.replace('/json/', '/html/') + '">'
|
||||
+ Drupal.t('Error: could not explain access')
|
||||
+ '</a>'
|
||||
+ '</span>'
|
||||
)
|
||||
.addClass('ajax-processed');
|
||||
// Call this function again.
|
||||
devel_node_access_user_ajax(context);
|
||||
}
|
||||
},
|
||||
3000
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the access by user behavior which initiates ajax.
|
||||
*/
|
||||
Drupal.behaviors.develNodeAccessUserAjax = {
|
||||
attach: function(context, settings) {
|
||||
// Start the ajax.
|
||||
devel_node_access_user_ajax(context, settings);
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
@@ -1,7 +0,0 @@
|
||||
devel_node_access:
|
||||
version: 0
|
||||
js:
|
||||
devel_node_access.js: {}
|
||||
css:
|
||||
component:
|
||||
css/devel_node_access.module.css: {}
|
||||
@@ -1,269 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* This module gives developers feedback as to what their
|
||||
* node_access table contains, and which nodes are protected or
|
||||
* visible to the public.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\devel_node_access\Plugin\Block\DnaBlock;
|
||||
|
||||
define('DNA_ACCESS_VIEW', 'view devel_node_access information');
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function devel_node_access_help($route_name, \Drupal\Core\Routing\RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.devel_node_access':
|
||||
$output = '<p>' . t('This module helps in site development. Specifically, when an access control module is used to limit access to some or all nodes, this module provides feedback showing the node_access table in the database.') . "</p>\n";
|
||||
$output .= '<p>' . t('The node_access table is one method Drupal provides to hide content from some users while displaying it to others. By default, Drupal shows all nodes to all users. There are a number of optional modules which may be installed to hide content from specific users based on various criteria.') . "</p>\n";
|
||||
$output .= '<p>' . t('If you have not installed any of these modules, you really have no need for the devel_node_access module. This module is intended for use during development, so that developers and admins can confirm that the node_access table is working as expected. You probably do not want this module enabled on a production site.') . "</p>\n";
|
||||
$output .= '<p>' . t('To use this module, enable its Devel Node Access block in a wide region. The block shows the entries in the node_access table for any nodes on the current page, and it provides options to display much more information at the cost of some overhead (requires JavaScript).') . "</p>\n";
|
||||
// $output .= '<p>' . t('This module also provides a <a href="@summary_page">summary page</a> which shows general information about your node_access table. If you have installed the Views module, you may browse node_access by realm.',
|
||||
// array('@summary_page' => url('devel/node_access/summary'))
|
||||
// ) . "</p>\n";
|
||||
return $output;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function devel_node_access_menu() {
|
||||
$items = array();
|
||||
|
||||
if (!\Drupal::moduleHandler()->moduleExists('devel')) {
|
||||
// We have to create the 'Devel settings' menu item ourselves.
|
||||
$items['admin/config/development/devel'] = array(
|
||||
'title' => 'Devel settings',
|
||||
'description' => 'Helper pages and blocks to assist Drupal developers and admins with node_access. The devel blocks can be managed via the <a href="' . url('admin/structure/block') . '">block administration</a> page.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('devel_node_access_admin_settings'),
|
||||
'access arguments' => array('administer site configuration'),
|
||||
);
|
||||
$items['devel/settings'] = $items['admin/config/development/devel'] + array(
|
||||
'menu_name' => 'devel',
|
||||
);
|
||||
}
|
||||
|
||||
// Create a callback for use by devel_node_access_user_ajax().
|
||||
$items['devel/node_access/by_user/%/%'] = array(
|
||||
'page callback' => 'devel_node_access_user_ajax',
|
||||
'page arguments' => array(3, 4),
|
||||
'access arguments' => array(DNA_ACCESS_VIEW),
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
// Add this to the custom menu 'devel' created by the devel module.
|
||||
$items['devel/node_access/summary'] = array(
|
||||
'title' => 'Node_access summary',
|
||||
'page callback' => 'dna_summary',
|
||||
'access arguments' => array(DNA_ACCESS_VIEW),
|
||||
'menu_name' => 'devel',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the DNA Summary page.
|
||||
*/
|
||||
function dna_summary() {
|
||||
// Warn user if they have any entries that could grant access to all nodes.
|
||||
$output = '';
|
||||
$result = db_query('SELECT DISTINCT realm FROM {node_access} WHERE nid = 0 AND gid = 0');
|
||||
$rows = array();
|
||||
foreach ($result as $row) {
|
||||
$rows[] = array($row->realm);
|
||||
}
|
||||
if (!empty($rows)) {
|
||||
$output .= '<h3>' . t('Access Granted to All Nodes (All Users)') . "</h3>\n";
|
||||
$output .= '<p>' . t('Your node_access table contains entries that may be granting all users access to all nodes. Depending on which access control module(s) you use, you may want to delete these entries. If you are not using an access control module, you should probably leave these entries as is.') . "</p>\n";
|
||||
$headers = array(t('realm'));
|
||||
$output .= theme('table', array('header' => $headers, 'rows' => $rows));
|
||||
$access_granted_to_all_nodes = TRUE;
|
||||
}
|
||||
|
||||
// How many nodes are not represented in the node_access table?
|
||||
$num = db_query('SELECT COUNT(n.nid) AS num_nodes FROM {node} n LEFT JOIN {node_access} na ON n.nid = na.nid WHERE na.nid IS NULL')->fetchField();
|
||||
if ($num) {
|
||||
$output .= '<h3>' . t('Legacy Nodes') . "</h3>\n";
|
||||
$output .= '<p>' .
|
||||
t('You have !num nodes in your node table which are not represented in your node_access table. If you have an access control module installed, these nodes may be hidden from all users. This could be caused by publishing nodes before enabling the access control module. If this is the case, manually updating each node should add it to the node_access table and fix the problem.', array('!num' => l($num, 'devel/node_access/view/NULL')))
|
||||
. "</p>\n";
|
||||
if (!empty($access_granted_to_all_nodes)) {
|
||||
$output .= '<p>' .
|
||||
t('This issue may be masked by the one above, so look into the former first.')
|
||||
. "</p>\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$output .= '<h3>' . t('All Nodes Represented') . "</h3>\n";
|
||||
$output .= '<p>' . t('All nodes are represented in the node_access table.') . "</p>\n";
|
||||
}
|
||||
|
||||
|
||||
// A similar warning to the one above, but slightly more specific.
|
||||
$result = db_query('SELECT DISTINCT realm FROM {node_access} WHERE nid = 0 AND gid <> 0');
|
||||
$rows = array();
|
||||
foreach ($result as $row) {
|
||||
$rows[] = array($row->realm);
|
||||
}
|
||||
if (!empty($rows)) {
|
||||
$output .= '<h3>' . t('Access Granted to All Nodes (Some Users)') . "</h3>\n";
|
||||
$output .= '<p>' . t('Your node_access table contains entries that may be granting some users access to all nodes. This may be perfectly normal, depending on which access control module(s) you use.') . "</p>\n";
|
||||
$headers = array(t('realm'));
|
||||
$output .= theme('table', array('header' => $headers, 'rows' => $rows));
|
||||
}
|
||||
|
||||
|
||||
// Find specific nodes which may be visible to all users.
|
||||
$result = db_query('SELECT DISTINCT realm, COUNT(DISTINCT nid) as node_count FROM {node_access} WHERE gid = 0 AND nid > 0 GROUP BY realm');
|
||||
$rows = array();
|
||||
foreach ($result as $row) {
|
||||
$rows[] = array(
|
||||
$row->realm,
|
||||
array(
|
||||
'data' => $row->node_count,
|
||||
'align' => 'center',
|
||||
),
|
||||
);
|
||||
}
|
||||
if (!empty($rows)) {
|
||||
$output .= '<h3>' . t('Access Granted to Some Nodes') . "</h3>\n";
|
||||
$output .= '<p>' .
|
||||
t('The following realms appear to grant all users access to some specific nodes. This may be perfectly normal, if some of your content is available to the public.')
|
||||
. "</p>\n";
|
||||
$headers = array(t('realm'), t('public nodes'));
|
||||
$output .= theme('table', array('header' => $headers, 'rows' => $rows, 'caption' => t('Public Nodes')));
|
||||
}
|
||||
|
||||
|
||||
// Find specific nodes protected by node_access table.
|
||||
$result = db_query('SELECT DISTINCT realm, COUNT(DISTINCT nid) as node_count FROM {node_access} WHERE gid <> 0 AND nid > 0 GROUP BY realm');
|
||||
$rows = array();
|
||||
foreach ($result as $row) {
|
||||
// No Views yet:
|
||||
//$rows[] = array(l($row->realm, "devel/node_access/view/$row->realm"),
|
||||
$rows[] = array(
|
||||
$row->realm,
|
||||
array(
|
||||
'data' => $row->node_count,
|
||||
'align' => 'center',
|
||||
),
|
||||
);
|
||||
}
|
||||
if (!empty($rows)) {
|
||||
$output .= '<h3>' . t('Summary by Realm') . "</h3>\n";
|
||||
$output .= '<p>' . t('The following realms grant limited access to some specific nodes.') . "</p>\n";
|
||||
$headers = array(t('realm'), t('private nodes'));
|
||||
$output .= theme('table', array('header' => $headers, 'rows' => $rows, 'caption' => t('Protected Nodes')));
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_view().
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function devel_node_access_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
|
||||
// Remember this node, for display in our block.
|
||||
DnaBlock::visibleNodes($entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for Node Access by User block ajax.
|
||||
*/
|
||||
function devel_node_access_user_ajax($data_type, $cell) {
|
||||
list($op, $nid, $uid) = explode('-', $cell);
|
||||
$node = node_load($nid);
|
||||
$account = user_load($uid);
|
||||
$output = array(
|
||||
'#theme' => 'dna_permission',
|
||||
'#permission' => DnaBlock::explainAccess($op, $node, $account),
|
||||
);
|
||||
// JSON output for Ajax callbacks.
|
||||
if ($data_type == 'json') {
|
||||
drupal_json_output(\Drupal::service('renderer')->render($output));
|
||||
exit;
|
||||
}
|
||||
// HTML output for error message click-throughs.
|
||||
if ($node) {
|
||||
$node_title = l($node->title, 'node/' . $node->id());
|
||||
}
|
||||
else {
|
||||
$node_title = t('unknown node %nid', array('%nid' => $nid));
|
||||
}
|
||||
drupal_set_title(
|
||||
t(
|
||||
'Devel Node Access: %op permission for !user on !node',
|
||||
array(
|
||||
'%op' => $op,
|
||||
'!user' => theme('username', array('account' => $account)),
|
||||
'!node' => $node_title,
|
||||
)
|
||||
),
|
||||
PASS_THROUGH
|
||||
);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_access_explain().
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function devel_node_access_node_access_explain($row) {
|
||||
if ($row->gid == 0 && $row->realm == 'all') {
|
||||
foreach (array('view', 'update', 'delete') as $op) {
|
||||
$gop = 'grant_' . $op;
|
||||
if (!empty($row->$gop)) {
|
||||
$ops[] = $op;
|
||||
}
|
||||
}
|
||||
if (empty($ops)) {
|
||||
return '(No access granted to ' . ($row->nid == 0 ? 'any nodes.)' : 'this node.)');
|
||||
}
|
||||
else {
|
||||
return 'All users may ' . implode('/', $ops) . ($row->nid == 0 ? ' all nodes.' : ' this node.');
|
||||
}
|
||||
}
|
||||
if (\Drupal::moduleHandler()->moduleExists('node_access_test')) {
|
||||
switch ($row->realm) {
|
||||
case 'node_access_test_author':
|
||||
return "Author (uid=$row->gid) has full access.";
|
||||
case 'node_access_test':
|
||||
return "Users with the 'node test view' permission may view.";
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function devel_node_access_theme() {
|
||||
return array(
|
||||
'dna_permission' => array(
|
||||
'variables' => array(
|
||||
'permission' => NULL,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether user has a permission or not.
|
||||
*/
|
||||
function theme_dna_permission($variables) {
|
||||
$permission = &$variables['permission'];
|
||||
return '<span class="' . ($permission[0] ? 'ok' : 'error') . '" title="' . $permission[2] . '">' . $permission[1] . '</span>';
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
view devel_node_access information:
|
||||
title: 'Access DNA information'
|
||||
description: 'View the node_access information block on node pages and the summary page.'
|
||||
restrict access: TRUE
|
||||
@@ -1,135 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_node_access\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\devel_node_access\Plugin\Block\DnaBlock;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines the form to change the DNA settings.
|
||||
*/
|
||||
class DnaForm extends FormBase {
|
||||
|
||||
/**
|
||||
* Constructs a new DnaForm object.
|
||||
*/
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'devel_dna_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['#attached'] = array(
|
||||
'library' => array(
|
||||
'devel_node_access/devel_node_access'
|
||||
),
|
||||
);
|
||||
|
||||
$debug_mode = \Drupal::config('devel_node_access.settings')->get('debug_mode');
|
||||
$by_user_mode = \Drupal::config('devel_node_access.settings')->get('by_user_mode');
|
||||
|
||||
$form['node_content'] = array(
|
||||
'#prefix' => '<div id="devel-node-access-node-content-div">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$form['node_content'][0] = DnaBlock::buildNodeInfo($debug_mode);
|
||||
|
||||
$form['user_content'] = array(
|
||||
'#prefix' => '<div id="devel-node-access-by-user-content-div">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
if ($by_user_mode) {
|
||||
$form['user_content'][0] = DnaBlock::buildByUserInfo();
|
||||
}
|
||||
|
||||
$form['setup'] = array(
|
||||
'#markup' => t('Enable:'),
|
||||
'#prefix' => '<div class="devel-node-access-inline">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$form['setup']['debug_mode'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#value' => $debug_mode,
|
||||
'#prefix' => ' ',
|
||||
'#title' => t('Debug Mode'),
|
||||
'#ajax' => array(
|
||||
'callback' => '::toggleDebugMode',
|
||||
'wrapper' => 'devel-node-access-node-content-div',
|
||||
),
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
$form['setup']['by_user_mode'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#value' => $by_user_mode,
|
||||
'#prefix' => ' ',
|
||||
'#title' => t('By-User Analysis (slow!)'),
|
||||
'#ajax' => array(
|
||||
'callback' => '::toggleByUserMode',
|
||||
'wrapper' => 'devel-node-access-by-user-content-div',
|
||||
),
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for form_state.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toggleDebugMode($form, FormStateInterface $form_state) {
|
||||
$debug_mode = $form_state->getUserInput();
|
||||
$debug_mode = !empty($debug_mode['debug_mode']);
|
||||
\Drupal::configFactory()->getEditable('devel_node_access.settings')->set('debug_mode', $debug_mode)->save(TRUE);
|
||||
$form['node_content'][0] = DnaBlock::buildNodeInfo($debug_mode);
|
||||
return $form['node_content'];
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for by_user_state.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toggleByUserMode($form, FormStateInterface $form_state) {
|
||||
$by_user_mode = $form_state->getUserInput();
|
||||
$by_user_mode = !empty($by_user_mode['by_user_mode']);
|
||||
\Drupal::configFactory()->getEditable('devel_node_access.settings')->set('by_user_mode', $by_user_mode)->save(TRUE);
|
||||
$form['user_content'][0] = ($by_user_mode ? DnaBlock::buildByUserInfo() : []);
|
||||
return $form['user_content'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
services:
|
||||
devel.command:
|
||||
class: Drupal\devel\Commands\DevelCommands
|
||||
arguments: ['@token', '@service_container', '@event_dispatcher']
|
||||
tags:
|
||||
- { name: drush.command }
|
||||
+3
-1
@@ -2,7 +2,8 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Drush integration for the devel module.
|
||||
* This file is only used by Drush8. Drush9 discovers its commands via tagged
|
||||
* service(s) in devel.services.yml. Also see classes in src/Commands.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Uuid\Php;
|
||||
@@ -160,6 +161,7 @@ function drush_devel_fn_event($event = NULL) {
|
||||
drush_log(dt('No implementations.'), 'ok');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Command handler. Show source code of specified function or method.
|
||||
*/
|
||||
@@ -1,105 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Generate PhpStorm metadata file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements of hook_drush_command().
|
||||
*/
|
||||
function phpstorm_drush_command() {
|
||||
$items = array();
|
||||
|
||||
$items['phpstorm-metadata'] = array(
|
||||
'description' => 'Save the PhpStorm Metadata file to Drupal root.',
|
||||
'core' => array('8+'),
|
||||
'aliases' => array('phpm'),
|
||||
'category' => 'devel',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_drush_help_alter().
|
||||
*/
|
||||
function phpstorm_drush_help_alter(&$command) {
|
||||
if ($command['command'] == 'cache-rebuild') {
|
||||
$command['options']['storm'] = 'Write a new PHPstorm metadata file to Drupal root.';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements drush_hook_post_COMMAND().
|
||||
*/
|
||||
function drush_phpstorm_post_cache_rebuild() {
|
||||
if (drush_get_option('storm')) {
|
||||
drush_invoke_process('@self', 'phpstorm-metadata');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate PhpStorm Metadata file.
|
||||
*
|
||||
* @see http://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Advanced+Metadata
|
||||
*/
|
||||
function drush_phpstorm_metadata() {
|
||||
$container = \Drupal::getContainer();
|
||||
|
||||
$reflectedClass = new ReflectionClass($container);
|
||||
|
||||
$map = array();
|
||||
|
||||
// Map for all services of the container.
|
||||
// @see \Symfony\Component\DependencyInjection\Container::getServiceIds().
|
||||
foreach ($reflectedClass->getMethods() as $method) {
|
||||
if (preg_match('/^get(.+)Service$/', $method->name, $match)) {
|
||||
$id = strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($match[1], '_', '.')));
|
||||
$service = \Drupal::service($id);
|
||||
if (is_object($service)) {
|
||||
$map["\\Drupal::service('')"][$id] = '\\' . get_class($service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entity Manager - getStorage
|
||||
foreach (\Drupal::entityTypeManager()->getDefinitions() as $type => $definition) {
|
||||
$class = Drupal::entityTypeManager()->getStorage($type);
|
||||
$map["\\Drupal::entityManager()->getStorage('')"][$type] = '\\' . get_class($class);
|
||||
$map["\\Drupal::entityTypeManager()->getStorage('')"][$type] = '\\' . get_class($class);
|
||||
}
|
||||
|
||||
$content = _drush_phpstorm_metadata_phpstorm_metadata_template($map);
|
||||
file_put_contents(DRUPAL_ROOT . '/.phpstorm.meta.php', $content);
|
||||
}
|
||||
|
||||
function _drush_phpstorm_metadata_phpstorm_metadata_template($data) {
|
||||
$file = '<?php
|
||||
|
||||
namespace PHPSTORM_META {
|
||||
|
||||
/** @noinspection PhpUnusedLocalVariableInspection */
|
||||
/** @noinspection PhpIllegalArrayKeyTypeInspection */
|
||||
$STATIC_METHOD_TYPES = [
|
||||
';
|
||||
|
||||
foreach ($data as $method => $map) {
|
||||
$file .= "\n";
|
||||
$file .= " {$method} => [\n";
|
||||
|
||||
foreach ($map as $argument => $class) {
|
||||
$file .= " '{$argument}' instanceof {$class},\n";
|
||||
}
|
||||
|
||||
$file .= " ],";
|
||||
$file .= "\n";
|
||||
}
|
||||
|
||||
$file .= '
|
||||
];
|
||||
}
|
||||
';
|
||||
|
||||
return $file;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#bebebe" d="M15.176 9.041c.045-.327.076-.658.076-.998 0-.36-.035-.71-.086-1.056l-2.275-.293c-.115-.426-.283-.827-.498-1.201l1.396-1.808c-.416-.551-.906-1.039-1.459-1.452l-1.807 1.391c-.373-.215-.774-.383-1.2-.499l-.292-2.252c-.338-.048-.677-.081-1.029-.081s-.694.033-1.032.082l-.291 2.251c-.426.116-.826.284-1.2.499l-1.805-1.391c-.552.413-1.044.901-1.459 1.452l1.395 1.808c-.215.374-.383.774-.499 1.2l-2.276.294c-.05.346-.085.696-.085 1.056 0 .34.031.671.077.998l2.285.295c.115.426.284.826.499 1.2l-1.417 1.836c.411.55.896 1.038 1.443 1.452l1.842-1.42c.374.215.774.383 1.2.498l.298 2.311c.337.047.677.08 1.025.08s.688-.033 1.021-.08l.299-2.311c.426-.115.826-.283 1.201-.498l1.842 1.42c.547-.414 1.031-.902 1.443-1.452l-1.416-1.837c.215-.373.383-.773.498-1.199l2.286-.295zm-7.174 1.514c-1.406 0-2.543-1.137-2.543-2.541 0-1.402 1.137-2.541 2.543-2.541 1.402 0 2.541 1.138 2.541 2.541 0 1.404-1.139 2.541-2.541 2.541z"/></svg>
|
||||
|
After Width: | Height: | Size: 998 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#ffffff" d="M15.176 9.041c.045-.327.076-.658.076-.998 0-.36-.035-.71-.086-1.056l-2.275-.293c-.115-.426-.283-.827-.498-1.201l1.396-1.808c-.416-.551-.906-1.039-1.459-1.452l-1.807 1.391c-.373-.215-.774-.383-1.2-.499l-.292-2.252c-.338-.048-.677-.081-1.029-.081s-.694.033-1.032.082l-.291 2.251c-.426.116-.826.284-1.2.499l-1.805-1.391c-.552.413-1.044.901-1.459 1.452l1.395 1.808c-.215.374-.383.774-.499 1.2l-2.276.294c-.05.346-.085.696-.085 1.056 0 .34.031.671.077.998l2.285.295c.115.426.284.826.499 1.2l-1.417 1.836c.411.55.896 1.038 1.443 1.452l1.842-1.42c.374.215.774.383 1.2.498l.298 2.311c.337.047.677.08 1.025.08s.688-.033 1.021-.08l.299-2.311c.426-.115.826-.283 1.201-.498l1.842 1.42c.547-.414 1.031-.902 1.443-1.452l-1.416-1.837c.215-.373.383-.773.498-1.199l2.286-.295zm-7.174 1.514c-1.406 0-2.543-1.137-2.543-2.541 0-1.402 1.137-2.541 2.543-2.541 1.402 0 2.541 1.138 2.541 2.541 0 1.404-1.139 2.541-2.541 2.541z"/></svg>
|
||||
|
After Width: | Height: | Size: 998 B |
@@ -6,8 +6,8 @@ package: Development
|
||||
tags:
|
||||
- developer
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-09-03
|
||||
version: '8.x-1.0-alpha1+23-dev'
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1472897643
|
||||
datestamp: 1502732047
|
||||
|
||||
@@ -24,13 +24,6 @@ class Kint extends DevelDumperBase {
|
||||
kint_require();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump($input, $name = NULL) {
|
||||
echo (string) $this->export($input, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
namespace Drupal\devel\Commands;
|
||||
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
|
||||
use Drupal\Component\Uuid\Php;
|
||||
use Drupal\Core\Utility\Token;
|
||||
use Drush\Commands\DrushCommands;
|
||||
use Drush\Exceptions\UserAbortException;
|
||||
|
||||
/**
|
||||
* For commands that are parts of modules, Drush expects to find commandfiles in
|
||||
* __MODULE__/src/Commands, and the namespace is Drupal/__MODULE__/Commands.
|
||||
*
|
||||
* In addition to a commandfile like this one, you need to add a drush.services.yml
|
||||
* in root of your module like this module does.
|
||||
*/
|
||||
class DevelCommands extends DrushCommands {
|
||||
|
||||
protected $token;
|
||||
|
||||
protected $container;
|
||||
|
||||
protected $eventDispatcher;
|
||||
|
||||
public function __construct(Token $token, $container, $eventDispatcher) {
|
||||
parent::__construct();
|
||||
$this->token = $token;
|
||||
$this->container = $container;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getEventDispatcher() {
|
||||
return $this->eventDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getContainer() {
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Token
|
||||
*/
|
||||
public function getToken() {
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall, and Install a list of modules.
|
||||
|
||||
* @command devel-reinstall
|
||||
* @param $modules A comma-separated list of module names.
|
||||
* @aliases dre
|
||||
* @allow-additional-options pm-uninstall,pm-enable
|
||||
*/
|
||||
public function reinstall($projects) {
|
||||
$projects = _convert_csv_to_array($projects);
|
||||
|
||||
// This is faster than 3 separate bootstraps.
|
||||
$args = array_merge(array('pm-uninstall'), $projects);
|
||||
// @todo. Use $application dispatch instead of drush_invoke().
|
||||
call_user_func_array('drush_invoke', $args);
|
||||
|
||||
$args = array_merge(array('pm-enable'), $projects);
|
||||
call_user_func_array('drush_invoke', $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* List implementations of a given hook and optionally edit one.
|
||||
*
|
||||
* @command devel-hook
|
||||
* @param $hook The name of the hook to explore.
|
||||
* @usage devel-hook cron
|
||||
* List implementations of hook_cron().
|
||||
* @aliases fnh,fn-hook,hook
|
||||
*/
|
||||
function hook($hook) {
|
||||
// Get implementations in the .install files as well.
|
||||
include_once './core/includes/install.inc';
|
||||
drupal_load_updates();
|
||||
|
||||
if ($hook_implementations = \Drupal::moduleHandler()->getImplementations($hook)) {
|
||||
if ($choice = drush_choice(array_combine($hook_implementations, $hook_implementations), 'Enter the number of the hook implementation you wish to view.')) {
|
||||
$info= $this->codeLocate($choice . "_$hook");
|
||||
$exec = drush_get_editor();
|
||||
drush_shell_exec_interactive($exec, $info['file']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->logger()->success(dt('No implementations.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List implementations of a given event and optionally edit one.
|
||||
*
|
||||
* @command devel-event
|
||||
* @param $event The name of the event to explore. If omitted, a list of events is shown.
|
||||
* @usage devel-event
|
||||
* Pick a Kernel event, then pick an implementation, and then view its source code.
|
||||
* @usage devel-event kernel.terminate
|
||||
* Pick a terminate subscribers and view its source code.
|
||||
* @aliases fne,fn-event,event
|
||||
*/
|
||||
function event($event) {
|
||||
$dispatcher = $this->getEventDispatcher();
|
||||
if (empty($event)) {
|
||||
// @todo Expand this list and move to interact().
|
||||
$events = array('kernel.controller', 'kernel.exception', 'kernel.request', 'kernel.response', 'kernel.terminate', 'kernel.view');
|
||||
$events = array_combine($events, $events);
|
||||
if (!$event = drush_choice($events, 'Enter the event you wish to explore.')) {
|
||||
throw new UserAbortException();
|
||||
}
|
||||
}
|
||||
if ($implementations = $dispatcher->getListeners($event)) {
|
||||
foreach ($implementations as $implementation) {
|
||||
$callable = get_class($implementation[0]) . '::' . $implementation[1];
|
||||
$choices[$callable] = $callable;
|
||||
}
|
||||
if ($choice = drush_choice($choices, 'Enter the number of the implementation you wish to view.')) {
|
||||
$info= $this->codeLocate($choice);
|
||||
$exec = drush_get_editor();
|
||||
drush_shell_exec_interactive($exec, $info['file']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->logger()->success(dt('No implementations.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List available tokens.
|
||||
*
|
||||
* @command devel-token
|
||||
* @aliases token
|
||||
* @field-labels
|
||||
* group: Group
|
||||
* token: Token
|
||||
* name: Name
|
||||
* @default-fields group,token,name
|
||||
*
|
||||
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
|
||||
*/
|
||||
public function token($options = ['format' => 'table']) {
|
||||
$all = $this->getToken()->getInfo();
|
||||
foreach ($all['tokens'] as $group => $tokens) {
|
||||
foreach ($tokens as $key => $token) {
|
||||
$rows[] = [
|
||||
'group' => $group,
|
||||
'token' => $key,
|
||||
'name' => $token['name'],
|
||||
];
|
||||
}
|
||||
}
|
||||
return new RowsOfFields($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a UUID.
|
||||
*
|
||||
* @command devel-uuid
|
||||
* @aliases uuid
|
||||
* @usage drush devel-uuid
|
||||
* Outputs a Universally Unique Identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function uuid() {
|
||||
$uuid = new Php();
|
||||
return $uuid->generate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get source code line for specified function or method.
|
||||
*/
|
||||
function codeLocate($function_name) {
|
||||
// Get implementations in the .install files as well.
|
||||
include_once './core/includes/install.inc';
|
||||
drupal_load_updates();
|
||||
|
||||
if (strpos($function_name, '::') === FALSE) {
|
||||
if (!function_exists($function_name)) {
|
||||
throw new \Exception(dt('Function not found'));
|
||||
}
|
||||
$reflect = new \ReflectionFunction($function_name);
|
||||
}
|
||||
else {
|
||||
list($class, $method) = explode('::', $function_name);
|
||||
if (!method_exists($class, $method)) {
|
||||
throw new \Exception(dt('Method not found'));
|
||||
}
|
||||
$reflect = new \ReflectionMethod($class, $method);
|
||||
}
|
||||
return array('file' => $reflect->getFileName(), 'startline' => $reflect->getStartLine(), 'endline' => $reflect->getEndLine());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of available container services.
|
||||
*
|
||||
* @command devel-services
|
||||
* @param $prefix A prefix to filter the service list by.
|
||||
* @aliases devel-container-services,dcs
|
||||
* @usage drush devel-services
|
||||
* Gets a list of all available container services
|
||||
* @usage drush dcs plugin.manager
|
||||
* Get all services containing "plugin.manager"
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function services($prefix = NULL, $options = ['format' => 'yaml']) {
|
||||
$container = $this->getContainer();
|
||||
|
||||
// Get a list of all available service IDs.
|
||||
$services = $container->getServiceIds();
|
||||
|
||||
// If there is a prefix, try to find matches.
|
||||
if (isset($prefix)) {
|
||||
$services = preg_grep("/$prefix/", $services);
|
||||
}
|
||||
|
||||
if (empty($services)) {
|
||||
throw new \Exception(dt('No container services found.'));
|
||||
}
|
||||
|
||||
sort($services);
|
||||
return $services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\DrupalKernelInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\devel\DevelDumperManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides route responses for the container info pages.
|
||||
*/
|
||||
class ContainerInfoController extends ControllerBase implements ContainerAwareInterface {
|
||||
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* The drupal kernel.
|
||||
*
|
||||
* @var \Drupal\Core\DrupalKernelInterface
|
||||
*/
|
||||
protected $kernel;
|
||||
|
||||
/**
|
||||
* The dumper manager service.
|
||||
*
|
||||
* @var \Drupal\devel\DevelDumperManagerInterface
|
||||
*/
|
||||
protected $dumper;
|
||||
|
||||
/**
|
||||
* ServiceInfoController constructor.
|
||||
*
|
||||
* @param \Drupal\Core\DrupalKernelInterface $drupalKernel
|
||||
* The drupal kernel.
|
||||
* @param \Drupal\devel\DevelDumperManagerInterface $dumper
|
||||
* The dumper manager service.
|
||||
*/
|
||||
public function __construct(DrupalKernelInterface $drupalKernel, DevelDumperManagerInterface $dumper) {
|
||||
$this->kernel = $drupalKernel;
|
||||
$this->dumper = $dumper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('kernel'),
|
||||
$container->get('devel.dumper')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the services overview page.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by the renderer.
|
||||
*/
|
||||
public function serviceList() {
|
||||
$headers = [
|
||||
$this->t('ID'),
|
||||
$this->t('Class'),
|
||||
$this->t('Alias'),
|
||||
$this->t('Operations'),
|
||||
];
|
||||
|
||||
$rows = [];
|
||||
|
||||
if ($container = $this->kernel->getCachedContainerDefinition()) {
|
||||
foreach ($container['services'] as $service_id => $definition) {
|
||||
$service = unserialize($definition);
|
||||
|
||||
$row['id'] = [
|
||||
'data' => $service_id,
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['class'] = [
|
||||
'data' => isset($service['class']) ? $service['class'] : '',
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['alias'] = [
|
||||
'data' => array_search($service_id, $container['aliases']) ?: '',
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['operations']['data'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => [
|
||||
'devel' => [
|
||||
'title' => $this->t('Devel'),
|
||||
'url' => Url::fromRoute('devel.container_info.service.detail', ['service_id' => $service_id]),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$rows[$service_id] = $row;
|
||||
}
|
||||
|
||||
ksort($rows);
|
||||
}
|
||||
|
||||
$output['#attached']['library'][] = 'system/drupal.system.modules';
|
||||
|
||||
$output['filters'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter', 'js-show'],
|
||||
],
|
||||
];
|
||||
$output['filters']['text'] = [
|
||||
'#type' => 'search',
|
||||
'#title' => $this->t('Search'),
|
||||
'#size' => 30,
|
||||
'#placeholder' => $this->t('Enter service id, alias or class'),
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter-text'],
|
||||
'data-table' => '.devel-filter-text',
|
||||
'autocomplete' => 'off',
|
||||
'title' => $this->t('Enter a part of the service id, service alias or class to filter by.'),
|
||||
],
|
||||
];
|
||||
$output['services'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $headers,
|
||||
'#rows' => $rows,
|
||||
'#empty' => $this->t('No services found.'),
|
||||
'#sticky' => TRUE,
|
||||
'#attributes' => [
|
||||
'class' => ['devel-service-list', 'devel-filter-text'],
|
||||
],
|
||||
];
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a render array representation of the service.
|
||||
*
|
||||
* @param string $service_id
|
||||
* The ID of the service to retrieve.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing the service detail.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* If the requested service is not defined.
|
||||
*/
|
||||
public function serviceDetail($service_id) {
|
||||
$instance = $this->container->get($service_id, ContainerInterface::NULL_ON_INVALID_REFERENCE);
|
||||
if ($instance === NULL) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$output = [];
|
||||
|
||||
if ($cached_definitions = $this->kernel->getCachedContainerDefinition()) {
|
||||
// Tries to retrieve the service definition from the kernel's cached
|
||||
// container definition.
|
||||
if (isset($cached_definitions['services'][$service_id])) {
|
||||
$definition = unserialize($cached_definitions['services'][$service_id]);
|
||||
|
||||
// If the service has an alias add it to the definition.
|
||||
if ($alias = array_search($service_id, $cached_definitions['aliases'])) {
|
||||
$definition['alias'] = $alias;
|
||||
}
|
||||
|
||||
$output['definition'] = $this->dumper->exportAsRenderable($definition, $this->t('Computed Definition'));
|
||||
}
|
||||
}
|
||||
|
||||
$output['instance'] = $this->dumper->exportAsRenderable($instance, $this->t('Instance'));
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the parameters overview page.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by the renderer.
|
||||
*/
|
||||
public function parameterList() {
|
||||
$headers = [
|
||||
$this->t('Name'),
|
||||
$this->t('Operations'),
|
||||
];
|
||||
|
||||
$rows = [];
|
||||
|
||||
if ($container = $this->kernel->getCachedContainerDefinition()) {
|
||||
foreach ($container['parameters'] as $parameter_name => $definition) {
|
||||
$row['name'] = [
|
||||
'data' => $parameter_name,
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['operations']['data'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => [
|
||||
'devel' => [
|
||||
'title' => $this->t('Devel'),
|
||||
'url' => Url::fromRoute('devel.container_info.parameter.detail', ['parameter_name' => $parameter_name]),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$rows[$parameter_name] = $row;
|
||||
}
|
||||
|
||||
ksort($rows);
|
||||
}
|
||||
|
||||
$output['#attached']['library'][] = 'system/drupal.system.modules';
|
||||
|
||||
$output['filters'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter', 'js-show'],
|
||||
],
|
||||
];
|
||||
$output['filters']['text'] = [
|
||||
'#type' => 'search',
|
||||
'#title' => $this->t('Search'),
|
||||
'#size' => 30,
|
||||
'#placeholder' => $this->t('Enter parameter name'),
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter-text'],
|
||||
'data-table' => '.devel-filter-text',
|
||||
'autocomplete' => 'off',
|
||||
'title' => $this->t('Enter a part of the parameter name to filter by.'),
|
||||
],
|
||||
];
|
||||
$output['parameters'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $headers,
|
||||
'#rows' => $rows,
|
||||
'#empty' => $this->t('No parameters found.'),
|
||||
'#sticky' => TRUE,
|
||||
'#attributes' => [
|
||||
'class' => ['devel-parameter-list', 'devel-filter-text'],
|
||||
],
|
||||
];
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a render array representation of the parameter value.
|
||||
*
|
||||
* @param string $parameter_name
|
||||
* The name of the parameter to retrieve.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing the parameter value.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* If the requested parameter is not defined.
|
||||
*/
|
||||
public function parameterDetail($parameter_name) {
|
||||
try {
|
||||
$parameter = $this->container->getParameter($parameter_name);
|
||||
}
|
||||
catch (ParameterNotFoundException $e) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
return $this->dumper->exportAsRenderable($parameter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,19 +3,41 @@
|
||||
namespace Drupal\devel\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\devel\DevelDumperManagerInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Returns responses for devel module routes.
|
||||
*/
|
||||
class DevelController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The dumper service.
|
||||
*
|
||||
* @var \Drupal\devel\DevelDumperManagerInterface
|
||||
*/
|
||||
protected $dumper;
|
||||
|
||||
/**
|
||||
* EntityDebugController constructor.
|
||||
*
|
||||
* @param \Drupal\devel\DevelDumperManagerInterface $dumper
|
||||
* The dumper service.
|
||||
*/
|
||||
public function __construct(DevelDumperManagerInterface $dumper) {
|
||||
$this->dumper = $dumper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('devel.dumper'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all caches, then redirects to the previous page.
|
||||
*/
|
||||
@@ -25,64 +47,10 @@ class DevelController extends ControllerBase {
|
||||
return $this->redirect('<front>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a dump of a route object.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* Page request object.
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing the route object.
|
||||
*/
|
||||
public function menuItem(Request $request, RouteMatchInterface $route_match) {
|
||||
$output = [];
|
||||
|
||||
// Get the route object from the path query string if available.
|
||||
if ($path = $request->query->get('path')) {
|
||||
try {
|
||||
/* @var \Symfony\Cmf\Component\Routing\ChainRouter $router */
|
||||
$router = \Drupal::service('router');
|
||||
$route = $router->match($path);
|
||||
$output['route'] = ['#markup' => kpr($route, TRUE)];
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
drupal_set_message($this->t("Unable to load route for url '%url'", ['%url' => $path]), 'warning');
|
||||
}
|
||||
}
|
||||
// No path specified, get the current route.
|
||||
else {
|
||||
$route = $route_match->getRouteObject();
|
||||
$output['route'] = ['#markup' => kpr($route, TRUE)];
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function themeRegistry() {
|
||||
$hooks = theme_get_registry();
|
||||
ksort($hooks);
|
||||
return array('#markup' => kprint_r($hooks, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the elements info overview page.
|
||||
*
|
||||
* @return array
|
||||
* Array of page elements to render.
|
||||
*/
|
||||
public function elementsPage() {
|
||||
$element_info_manager = \Drupal::service('element_info');
|
||||
|
||||
$elements_info = array();
|
||||
foreach ($element_info_manager->getDefinitions() as $element_type => $definition) {
|
||||
$elements_info[$element_type] = $definition + $element_info_manager->getInfo($element_type);
|
||||
}
|
||||
|
||||
ksort($elements_info);
|
||||
|
||||
return array('#markup' => kdevel_print_object($elements_info));
|
||||
return $this->dumper->exportAsRenderable($hooks);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,43 +62,31 @@ class DevelController extends ControllerBase {
|
||||
public function fieldInfoPage() {
|
||||
$fields = FieldStorageConfig::loadMultiple();
|
||||
ksort($fields);
|
||||
$output['fields'] = array('#markup' => kprint_r($fields, TRUE, $this->t('Fields')));
|
||||
$output['fields'] = $this->dumper->exportAsRenderable($fields, $this->t('Fields'));
|
||||
|
||||
$field_instances = FieldConfig::loadMultiple();
|
||||
ksort($field_instances);
|
||||
$output['instances'] = array('#markup' => kprint_r($field_instances, TRUE, $this->t('Instances')));
|
||||
$output['instances'] = $this->dumper->exportAsRenderable($field_instances, $this->t('Instances'));
|
||||
|
||||
$bundles = \Drupal::service('entity_type.bundle.info')->getAllBundleInfo();
|
||||
ksort($bundles);
|
||||
$output['bundles'] = array('#markup' => kprint_r($bundles, TRUE, $this->t('Bundles')));
|
||||
$output['bundles'] = $this->dumper->exportAsRenderable($bundles, $this->t('Bundles'));
|
||||
|
||||
$field_types = \Drupal::service('plugin.manager.field.field_type')->getUiDefinitions();
|
||||
ksort($field_types);
|
||||
$output['field_types'] = array('#markup' => kprint_r($field_types, TRUE, $this->t('Field types')));
|
||||
$output['field_types'] = $this->dumper->exportAsRenderable($field_types, $this->t('Field types'));
|
||||
|
||||
$formatter_types = \Drupal::service('plugin.manager.field.formatter')->getDefinitions();
|
||||
ksort($formatter_types);
|
||||
$output['formatter_types'] = array('#markup' => kprint_r($formatter_types, TRUE, $this->t('Formatter types')));
|
||||
$output['formatter_types'] = $this->dumper->exportAsRenderable($formatter_types, $this->t('Formatter types'));
|
||||
|
||||
$widget_types = \Drupal::service('plugin.manager.field.widget')->getDefinitions();
|
||||
ksort($widget_types);
|
||||
$output['widget_types'] = array('#markup' => kprint_r($widget_types, TRUE, $this->t('Widget types')));
|
||||
$output['widget_types'] = $this->dumper->exportAsRenderable($widget_types, $this->t('Widget types'));
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the entity types overview page.
|
||||
*
|
||||
* @return array
|
||||
* Array of page elements to render.
|
||||
*/
|
||||
public function entityInfoPage() {
|
||||
$types = $this->entityTypeManager()->getDefinitions();
|
||||
ksort($types);
|
||||
return array('#markup' => kprint_r($types, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the state variable overview page.
|
||||
*
|
||||
@@ -181,7 +137,7 @@ class DevelController extends ControllerBase {
|
||||
'class' => 'table-filter-text-source',
|
||||
),
|
||||
'value' => array(
|
||||
'data' => kprint_r($state, TRUE),
|
||||
'data' => $this->dumper->export($state),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -225,9 +181,7 @@ class DevelController extends ControllerBase {
|
||||
'#rows' => array(array(session_name(), session_id())),
|
||||
'#empty' => $this->t('No session available.'),
|
||||
);
|
||||
$output['data'] = array(
|
||||
'#markup' => kprint_r($_SESSION, TRUE),
|
||||
);
|
||||
$output['data'] = $this->dumper->exportAsRenderable($_SESSION);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Controller;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Render\ElementInfoManagerInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\devel\DevelDumperManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides route responses for the element info page.
|
||||
*/
|
||||
class ElementInfoController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* Element info manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Render\ElementInfoManagerInterface
|
||||
*/
|
||||
protected $elementInfo;
|
||||
|
||||
/**
|
||||
* The dumper service.
|
||||
*
|
||||
* @var \Drupal\devel\DevelDumperManagerInterface
|
||||
*/
|
||||
protected $dumper;
|
||||
|
||||
/**
|
||||
* EventInfoController constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
|
||||
* Element info manager service.
|
||||
* @param \Drupal\devel\DevelDumperManagerInterface $dumper
|
||||
* The dumper service.
|
||||
*/
|
||||
public function __construct(ElementInfoManagerInterface $element_info, DevelDumperManagerInterface $dumper) {
|
||||
$this->elementInfo = $element_info;
|
||||
$this->dumper = $dumper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('element_info'),
|
||||
$container->get('devel.dumper')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the element overview page.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by the renderer.
|
||||
*/
|
||||
public function elementList() {
|
||||
$headers = [
|
||||
$this->t('Name'),
|
||||
$this->t('Provider'),
|
||||
$this->t('Class'),
|
||||
$this->t('Operations'),
|
||||
];
|
||||
|
||||
$rows = [];
|
||||
|
||||
foreach ($this->elementInfo->getDefinitions() as $element_type => $definition) {
|
||||
$row['name'] = [
|
||||
'data' => $element_type,
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['provider'] = [
|
||||
'data' => $definition['provider'],
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['class'] = [
|
||||
'data' => $definition['class'],
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['operations']['data'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => [
|
||||
'devel' => [
|
||||
'title' => $this->t('Devel'),
|
||||
'url' => Url::fromRoute('devel.elements_page.detail', ['element_name' => $element_type]),
|
||||
'attributes' => [
|
||||
'class' => ['use-ajax'],
|
||||
'data-dialog-type' => 'modal',
|
||||
'data-dialog-options' => Json::encode([
|
||||
'width' => 700,
|
||||
'minHeight' => 500,
|
||||
]),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$rows[$element_type] = $row;
|
||||
}
|
||||
|
||||
ksort($rows);
|
||||
|
||||
$output['#attached']['library'][] = 'system/drupal.system.modules';
|
||||
|
||||
$output['filters'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter', 'js-show'],
|
||||
],
|
||||
];
|
||||
$output['filters']['text'] = [
|
||||
'#type' => 'search',
|
||||
'#title' => $this->t('Search'),
|
||||
'#size' => 30,
|
||||
'#placeholder' => $this->t('Enter element id, provider or class'),
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter-text'],
|
||||
'data-table' => '.devel-filter-text',
|
||||
'autocomplete' => 'off',
|
||||
'title' => $this->t('Enter a part of the element id, provider or class to filter by.'),
|
||||
],
|
||||
];
|
||||
$output['elements'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $headers,
|
||||
'#rows' => $rows,
|
||||
'#empty' => $this->t('No elements found.'),
|
||||
'#sticky' => TRUE,
|
||||
'#attributes' => [
|
||||
'class' => ['devel-element-list', 'devel-filter-text'],
|
||||
],
|
||||
];
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a render array representation of the element.
|
||||
*
|
||||
* @param string $element_name
|
||||
* The name of the element to retrieve.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing the element.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* If the requested element is not defined.
|
||||
*/
|
||||
public function elementDetail($element_name) {
|
||||
if (!$element = $this->elementInfo->getDefinition($element_name, FALSE)) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$element += $this->elementInfo->getInfo($element_name);
|
||||
return $this->dumper->exportAsRenderable($element, $element_name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,6 +41,27 @@ class EntityDebugController extends ControllerBase {
|
||||
return new static($container->get('devel.dumper'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity type definition of the current entity.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* A RouteMatch object.
|
||||
*
|
||||
* @return array
|
||||
* Array of page elements to render.
|
||||
*/
|
||||
public function entityTypeDefinition(RouteMatchInterface $route_match) {
|
||||
$output = [];
|
||||
|
||||
$entity = $this->getEntityFromRouteMatch($route_match);
|
||||
|
||||
if ($entity instanceof EntityInterface) {
|
||||
$output = $this->dumper->exportAsRenderable($entity->getEntityType());
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loaded structure of the current entity.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Controller;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\devel\DevelDumperManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides route responses for the entity types info page.
|
||||
*/
|
||||
class EntityTypeInfoController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The dumper service.
|
||||
*
|
||||
* @var \Drupal\devel\DevelDumperManagerInterface
|
||||
*/
|
||||
protected $dumper;
|
||||
|
||||
/**
|
||||
* EntityTypeInfoController constructor.
|
||||
*
|
||||
* @param \Drupal\devel\DevelDumperManagerInterface $dumper
|
||||
* The dumper service.
|
||||
*/
|
||||
public function __construct(DevelDumperManagerInterface $dumper) {
|
||||
$this->dumper = $dumper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('devel.dumper')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the entity types overview page.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by the renderer.
|
||||
*/
|
||||
public function entityTypeList() {
|
||||
$headers = [
|
||||
$this->t('ID'),
|
||||
$this->t('Name'),
|
||||
$this->t('Provider'),
|
||||
$this->t('Class'),
|
||||
$this->t('Operations'),
|
||||
];
|
||||
|
||||
$rows = [];
|
||||
|
||||
foreach ($this->entityTypeManager()->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
$row['id'] = [
|
||||
'data' => $entity_type->id(),
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['name'] = [
|
||||
'data' => $entity_type->getLabel(),
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['provider'] = [
|
||||
'data' => $entity_type->getProvider(),
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['class'] = [
|
||||
'data' => $entity_type->getClass(),
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['operations']['data'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => [
|
||||
'devel' => [
|
||||
'title' => $this->t('Devel'),
|
||||
'url' => Url::fromRoute('devel.entity_info_page.detail', ['entity_type_id' => $entity_type_id]),
|
||||
'attributes' => [
|
||||
'class' => ['use-ajax'],
|
||||
'data-dialog-type' => 'modal',
|
||||
'data-dialog-options' => Json::encode([
|
||||
'width' => 700,
|
||||
'minHeight' => 500,
|
||||
]),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$rows[$entity_type_id] = $row;
|
||||
}
|
||||
|
||||
ksort($rows);
|
||||
|
||||
$output['#attached']['library'][] = 'system/drupal.system.modules';
|
||||
|
||||
$output['filters'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter', 'js-show'],
|
||||
],
|
||||
];
|
||||
$output['filters']['text'] = [
|
||||
'#type' => 'search',
|
||||
'#title' => $this->t('Search'),
|
||||
'#size' => 30,
|
||||
'#placeholder' => $this->t('Enter entity type id, provider or class'),
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter-text'],
|
||||
'data-table' => '.devel-filter-text',
|
||||
'autocomplete' => 'off',
|
||||
'title' => $this->t('Enter a part of the entity type id, provider or class to filter by.'),
|
||||
],
|
||||
];
|
||||
$output['entities'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $headers,
|
||||
'#rows' => $rows,
|
||||
'#empty' => $this->t('No entity types found.'),
|
||||
'#sticky' => TRUE,
|
||||
'#attributes' => [
|
||||
'class' => ['devel-entity-type-list', 'devel-filter-text'],
|
||||
],
|
||||
];
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a render array representation of the entity type.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The name of the entity type to retrieve.
|
||||
*
|
||||
* @return array
|
||||
* A render array containing the entity type.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* If the requested entity type is not defined.
|
||||
*/
|
||||
public function entityTypeDetail($entity_type_id) {
|
||||
if (!$entity_type = $this->entityTypeManager()->getDefinition($entity_type_id, FALSE)) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
return $this->dumper->exportAsRenderable($entity_type, $entity_type_id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Provides route responses for the event info page.
|
||||
*/
|
||||
class EventInfoController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* Event dispatcher service.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* EventInfoController constructor.
|
||||
*
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
||||
* Event dispatcher service.
|
||||
*/
|
||||
public function __construct(EventDispatcherInterface $event_dispatcher) {
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('event_dispatcher')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the events overview page.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by the renderer.
|
||||
*/
|
||||
public function eventList() {
|
||||
$headers = [
|
||||
'name' => [
|
||||
'data' => $this->t('Event Name'),
|
||||
'class' => 'visually-hidden',
|
||||
],
|
||||
'callable' => $this->t('Callable'),
|
||||
'priority' => $this->t('Priority'),
|
||||
];
|
||||
|
||||
$event_listeners = $this->eventDispatcher->getListeners();
|
||||
ksort($event_listeners);
|
||||
|
||||
$rows = [];
|
||||
|
||||
foreach ($event_listeners as $event_name => $listeners) {
|
||||
|
||||
$rows[][] = [
|
||||
'data' => $event_name,
|
||||
'class' => 'table-filter-text-source devel-event-name-header',
|
||||
'colspan' => '3',
|
||||
'header' => TRUE,
|
||||
];
|
||||
|
||||
foreach ($listeners as $priority => $listener) {
|
||||
$row['name'] = [
|
||||
'data' => $event_name,
|
||||
'class' => 'table-filter-text-source visually-hidden',
|
||||
];
|
||||
$row['class'] = [
|
||||
'data' => $this->resolveCallableName($listener),
|
||||
];
|
||||
$row['priority'] = [
|
||||
'data' => $priority,
|
||||
];
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
$output['#attached']['library'][] = 'system/drupal.system.modules';
|
||||
|
||||
$output['filters'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter', 'js-show'],
|
||||
],
|
||||
];
|
||||
$output['filters']['name'] = [
|
||||
'#type' => 'search',
|
||||
'#title' => $this->t('Search'),
|
||||
'#size' => 30,
|
||||
'#placeholder' => $this->t('Enter event name'),
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter-text'],
|
||||
'data-table' => '.devel-filter-text',
|
||||
'autocomplete' => 'off',
|
||||
'title' => $this->t('Enter a part of the event name to filter by.'),
|
||||
],
|
||||
];
|
||||
$output['events'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $headers,
|
||||
'#rows' => $rows,
|
||||
'#empty' => $this->t('No events found.'),
|
||||
'#attributes' => [
|
||||
'class' => ['devel-event-list', 'devel-filter-text'],
|
||||
],
|
||||
];
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for resolve callable name.
|
||||
*
|
||||
* @param mixed $callable
|
||||
* The for which resolve the name. Can be either the name of a function
|
||||
* stored in a string variable, or an object and the name of a method
|
||||
* within the object.
|
||||
*
|
||||
* @return string
|
||||
* The resolved callable name or an empty string.
|
||||
*/
|
||||
protected function resolveCallableName($callable) {
|
||||
if (is_callable($callable, TRUE, $callable_name)) {
|
||||
return $callable_name;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Layout\LayoutPluginManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Returns response for Layout Info route.
|
||||
*/
|
||||
class LayoutInfoController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The Layout Plugin Manager.
|
||||
*
|
||||
* @var Drupal\Core\Layout\LayoutPluginManagerInterface
|
||||
*/
|
||||
protected $layoutPluginManager;
|
||||
|
||||
/**
|
||||
* LayoutInfoController constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Layout\LayoutPluginManagerInterface $pluginManagerLayout
|
||||
* The layout manager.
|
||||
*/
|
||||
public function __construct(LayoutPluginManagerInterface $pluginManagerLayout) {
|
||||
$this->layoutPluginManager = $pluginManagerLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.core.layout')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the Layout Info page.
|
||||
*
|
||||
* @return array
|
||||
* Array of page elements to render.
|
||||
*/
|
||||
public function layoutInfoPage() {
|
||||
$definedLayouts = [];
|
||||
$layouts = $this->layoutPluginManager->getDefinitions();
|
||||
foreach ($layouts as $layout) {
|
||||
// @todo Revisit once https://www.drupal.org/node/2660124 gets in, getting
|
||||
// the image should be as simple as $layout->getIcon().
|
||||
$image = NULL;
|
||||
if ($layout->getIconPath() != NULL) {
|
||||
$image = [
|
||||
'data' => [
|
||||
'#theme' => 'image',
|
||||
'#uri' => $layout->getIconPath(),
|
||||
'#alt' => $layout->getLabel(),
|
||||
'#height' => '65',
|
||||
]
|
||||
];
|
||||
}
|
||||
$definedLayouts[] = [
|
||||
$image,
|
||||
$layout->getLabel(),
|
||||
$layout->getDescription(),
|
||||
$layout->getCategory(),
|
||||
implode(', ', $layout->getRegionLabels()),
|
||||
$layout->getProvider(),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'#theme' => 'table',
|
||||
'#header' => [
|
||||
$this->t('Icon'),
|
||||
$this->t('Label'),
|
||||
$this->t('Description'),
|
||||
$this->t('Category'),
|
||||
$this->t('Regions'),
|
||||
$this->t('Provider'),
|
||||
],
|
||||
'#rows' => $definedLayouts,
|
||||
'#empty' => $this->t('No layouts available.'),
|
||||
'#attributes' => [
|
||||
'class' => ['devel-layout-list'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\devel\DevelDumperManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
|
||||
/**
|
||||
* Provides route responses for the route info pages.
|
||||
*/
|
||||
class RouteInfoController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The route provider.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteProviderInterface
|
||||
*/
|
||||
protected $routeProvider;
|
||||
|
||||
/**
|
||||
* The router service.
|
||||
*
|
||||
* @var \Symfony\Component\Routing\RouterInterface
|
||||
*/
|
||||
protected $router;
|
||||
|
||||
/**
|
||||
* The dumper service.
|
||||
*
|
||||
* @var \Drupal\devel\DevelDumperManagerInterface
|
||||
*/
|
||||
protected $dumper;
|
||||
|
||||
/**
|
||||
* RouterInfoController constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteProviderInterface $provider
|
||||
* The route provider.
|
||||
* @param \Symfony\Component\Routing\RouterInterface $router
|
||||
* The router service.
|
||||
* @param \Drupal\devel\DevelDumperManagerInterface $dumper
|
||||
* The dumper service.
|
||||
*/
|
||||
public function __construct(RouteProviderInterface $provider, RouterInterface $router, DevelDumperManagerInterface $dumper) {
|
||||
$this->routeProvider = $provider;
|
||||
$this->router = $router;
|
||||
$this->dumper = $dumper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('router.route_provider'),
|
||||
$container->get('router.no_access_checks'),
|
||||
$container->get('devel.dumper')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the routes overview page.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by the renderer.
|
||||
*/
|
||||
public function routeList() {
|
||||
$headers = [
|
||||
$this->t('Route Name'),
|
||||
$this->t('Path'),
|
||||
$this->t('Allowed Methods'),
|
||||
$this->t('Operations'),
|
||||
];
|
||||
|
||||
$rows = [];
|
||||
|
||||
foreach ($this->routeProvider->getAllRoutes() as $route_name => $route) {
|
||||
$row['name'] = [
|
||||
'data' => $route_name,
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['path'] = [
|
||||
'data' => $route->getPath(),
|
||||
'class' => 'table-filter-text-source',
|
||||
];
|
||||
$row['methods']['data'] = [
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $route->getMethods(),
|
||||
'#empty' => $this->t('ANY'),
|
||||
'#context' => ['list_style' => 'comma-list'],
|
||||
];
|
||||
|
||||
// We cannot resolve routes with dynamic parameters from route path. For
|
||||
// these routes we pass the route name.
|
||||
// @see ::routeItem()
|
||||
if (strpos($route->getPath(), '{') !== FALSE) {
|
||||
$parameters = ['query' => ['route_name' => $route_name]];
|
||||
}
|
||||
else {
|
||||
$parameters = ['query' => ['path' => $route->getPath()]];
|
||||
}
|
||||
|
||||
$row['operations']['data'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => [
|
||||
'devel' => [
|
||||
'title' => $this->t('Devel'),
|
||||
'url' => Url::fromRoute('devel.route_info.item', [], $parameters),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
$output['#attached']['library'][] = 'system/drupal.system.modules';
|
||||
|
||||
$output['filters'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter', 'js-show'],
|
||||
],
|
||||
];
|
||||
$output['filters']['name'] = [
|
||||
'#type' => 'search',
|
||||
'#title' => $this->t('Search'),
|
||||
'#size' => 30,
|
||||
'#placeholder' => $this->t('Enter route name or path'),
|
||||
'#attributes' => [
|
||||
'class' => ['table-filter-text'],
|
||||
'data-table' => '.devel-filter-text',
|
||||
'autocomplete' => 'off',
|
||||
'title' => $this->t('Enter a part of the route name or path to filter by.'),
|
||||
],
|
||||
];
|
||||
$output['routes'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => $headers,
|
||||
'#rows' => $rows,
|
||||
'#empty' => $this->t('No routes found.'),
|
||||
'#sticky' => TRUE,
|
||||
'#attributes' => [
|
||||
'class' => ['devel-route-list', 'devel-filter-text'],
|
||||
],
|
||||
];
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a render array representation of the route object.
|
||||
*
|
||||
* The method tries to resolve the route from the 'path' or the 'route_name'
|
||||
* query string value if available. If no route is retrieved from the query
|
||||
* string parameters it fallbacks to the current route.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request object.
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by the renderer.
|
||||
*/
|
||||
public function routeDetail(Request $request, RouteMatchInterface $route_match) {
|
||||
$route = NULL;
|
||||
|
||||
// Get the route object from the path query string if available.
|
||||
if ($path = $request->query->get('path')) {
|
||||
try {
|
||||
$route = $this->router->match($path);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
drupal_set_message($this->t("Unable to load route for url '%url'", ['%url' => $path]), 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
// Get the route object from the route name query string if available and
|
||||
// the route is not retrieved by path.
|
||||
if ($route === NULL && $route_name = $request->query->get('route_name')) {
|
||||
try {
|
||||
$route = $this->routeProvider->getRouteByName($route_name);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
drupal_set_message($this->t("Unable to load route '%name'", ['%name' => $route_name]), 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
// No route retrieved from path or name specified, get the current route.
|
||||
if ($route === NULL) {
|
||||
$route = $route_match->getRouteObject();
|
||||
}
|
||||
|
||||
return $this->dumper->exportAsRenderable($route);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace Drupal\devel;
|
||||
|
||||
use Drupal\Core\Render\Markup;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\devel\Render\FilteredMarkup;
|
||||
|
||||
/**
|
||||
* Defines a base devel dumper implementation.
|
||||
@@ -15,6 +15,13 @@ use Drupal\Core\Plugin\PluginBase;
|
||||
*/
|
||||
abstract class DevelDumperBase extends PluginBase implements DevelDumperInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump($input, $name = NULL) {
|
||||
echo (string) $this->export($input, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -32,7 +39,7 @@ abstract class DevelDumperBase extends PluginBase implements DevelDumperInterfac
|
||||
* The unaltered input value.
|
||||
*/
|
||||
protected function setSafeMarkup($input) {
|
||||
return Markup::create($input);
|
||||
return FilteredMarkup::create($input);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Manipulates entity type information.
|
||||
*
|
||||
* This class contains primarily bridged hooks for compile-time or
|
||||
* cache-clear-time hooks. Runtime hooks should be placed in EntityOperations.
|
||||
*/
|
||||
class EntityTypeInfo implements ContainerInjectionInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* EntityTypeInfo constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* Current user.
|
||||
*/
|
||||
public function __construct(AccountInterface $current_user) {
|
||||
$this->currentUser = $current_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('current_user')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds devel links to appropriate entity types.
|
||||
*
|
||||
* This is an alter hook bridge.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
|
||||
* The master entity type list to alter.
|
||||
*
|
||||
* @see hook_entity_type_alter()
|
||||
*/
|
||||
public function entityTypeAlter(array &$entity_types) {
|
||||
foreach ($entity_types as $entity_type_id => $entity_type) {
|
||||
if (($entity_type->getFormClass('default') || $entity_type->getFormClass('edit')) && $entity_type->hasLinkTemplate('edit-form')) {
|
||||
$entity_type->setLinkTemplate('devel-load', "/devel/$entity_type_id/{{$entity_type_id}}");
|
||||
}
|
||||
if ($entity_type->hasViewBuilderClass() && $entity_type->hasLinkTemplate('canonical')) {
|
||||
$entity_type->setLinkTemplate('devel-render', "/devel/$entity_type_id/{{$entity_type_id}}/render");
|
||||
}
|
||||
if ($entity_type->hasLinkTemplate('devel-render') || $entity_type->hasLinkTemplate('devel-load')) {
|
||||
$entity_type->setLinkTemplate('devel-definition', "/devel/$entity_type_id/{{$entity_type_id}}/definition");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds devel operations on entity that supports it.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity on which to define an operation.
|
||||
*
|
||||
* @return array
|
||||
* An array of operation definitions.
|
||||
*
|
||||
* @see hook_entity_operation()
|
||||
*/
|
||||
public function entityOperation(EntityInterface $entity) {
|
||||
$operations = [];
|
||||
if ($this->currentUser->hasPermission('access devel information')) {
|
||||
if ($entity->hasLinkTemplate('devel-load')) {
|
||||
$operations['devel'] = [
|
||||
'title' => $this->t('Devel'),
|
||||
'weight' => 100,
|
||||
'url' => $entity->toUrl('devel-load'),
|
||||
];
|
||||
}
|
||||
elseif ($entity->hasLinkTemplate('devel-render')) {
|
||||
$operations['devel'] = [
|
||||
'title' => $this->t('Devel'),
|
||||
'weight' => 100,
|
||||
'url' => $entity->toUrl('devel-render'),
|
||||
];
|
||||
}
|
||||
}
|
||||
return $operations;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Routing\UrlGeneratorInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
|
||||
class DevelEventSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The devel.settings config object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config;
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Routing\UrlGeneratorInterface
|
||||
*/
|
||||
protected $urlGenerator;
|
||||
|
||||
/**
|
||||
* Constructs a DevelEventSubscriber object.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config, AccountInterface $account, ModuleHandlerInterface $module_handler, UrlGeneratorInterface $url_generator) {
|
||||
$this->config = $config->get('devel.settings');
|
||||
$this->account = $account;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->urlGenerator = $url_generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the devel error handler.
|
||||
*
|
||||
* @param \Symfony\Component\EventDispatcher\Event $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function registerErrorHandler(Event $event = NULL) {
|
||||
if ($this->account && $this->account->hasPermission('access devel information')) {
|
||||
devel_set_handler(devel_get_handlers());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes devel module requirements.
|
||||
*/
|
||||
public function onRequest(GetResponseEvent $event) {
|
||||
if ($this->config->get('rebuild_theme')) {
|
||||
drupal_theme_rebuild();
|
||||
|
||||
// Ensure that the active theme object is cleared.
|
||||
$theme_name = \Drupal::theme()->getActiveTheme()->getName();
|
||||
\Drupal::state()->delete('theme.active_theme.' . $theme_name);
|
||||
\Drupal::theme()->resetActiveTheme();
|
||||
|
||||
/** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler*/
|
||||
$theme_handler = \Drupal::service('theme_handler');
|
||||
$theme_handler->refreshInfo();
|
||||
// @todo This is not needed after https://www.drupal.org/node/2330755
|
||||
$list = $theme_handler->listInfo();
|
||||
$theme_handler->addTheme($list[$theme_name]);
|
||||
|
||||
if (\Drupal::service('flood')->isAllowed('devel.rebuild_theme_warning', 1)) {
|
||||
\Drupal::service('flood')->register('devel.rebuild_theme_warning');
|
||||
if ($this->account->hasPermission('access devel information')) {
|
||||
drupal_set_message(t('The theme information is being rebuilt on every request. Remember to <a href=":url">turn off</a> this feature on production websites.', array(':url' => $this->urlGenerator->generateFromRoute('devel.admin_settings'))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
// Set a low value to start as early as possible.
|
||||
$events[KernelEvents::REQUEST][] = ['onRequest', -100];
|
||||
|
||||
// Runs as soon as possible in the request but after
|
||||
// AuthenticationSubscriber (priority 300) because you need to access to
|
||||
// the current user for determine whether register the devel error handler
|
||||
// or not.
|
||||
$events[KernelEvents::REQUEST][] = ['registerErrorHandler', 256];
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Session\AccountProxyInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* Listener for handling PHP errors.
|
||||
*/
|
||||
class ErrorHandlerSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountProxyInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* ErrorHandlerSubscriber constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountProxyInterface $account
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(AccountProxyInterface $account) {
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register devel error handler.
|
||||
*
|
||||
* @param \Symfony\Component\EventDispatcher\Event $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function registerErrorHandler(Event $event = NULL) {
|
||||
if ($this->account && $this->account->hasPermission('access devel information')) {
|
||||
devel_set_handler(devel_get_handlers());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
// Runs as soon as possible in the request but after
|
||||
// AuthenticationSubscriber (priority 300) because you need to access to
|
||||
// the current user for determine whether register the devel error handler
|
||||
// or not.
|
||||
$events[KernelEvents::REQUEST][] = ['registerErrorHandler', 256];
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Session\AccountProxyInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* Subscriber for force the system to rebuild the theme registry.
|
||||
*/
|
||||
class ThemeInfoRebuildSubscriber implements EventSubscriberInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Internal flag for handle user notification.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $notificationFlag = 'devel.rebuild_theme_warning';
|
||||
|
||||
/**
|
||||
* The devel config.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config;
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountProxyInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* Constructs a ThemeInfoRebuildSubscriber object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
|
||||
* The config factory.
|
||||
* @param \Drupal\Core\Session\AccountProxyInterface $account
|
||||
* The current user.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config, AccountProxyInterface $account, ThemeHandlerInterface $theme_handler) {
|
||||
$this->config = $config->get('devel.settings');
|
||||
$this->account = $account;
|
||||
$this->themeHandler = $theme_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the system to rebuild the theme registry.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function rebuildThemeInfo(GetResponseEvent $event) {
|
||||
if ($this->config->get('rebuild_theme')) {
|
||||
// Update the theme registry.
|
||||
drupal_theme_rebuild();
|
||||
// Refresh theme data.
|
||||
$this->themeHandler->refreshInfo();
|
||||
// Resets the internal state of the theme handler and clear the 'system
|
||||
// list' cache; this allow to properly register, if needed, PSR-4
|
||||
// namespaces for theme extensions after refreshing the info data.
|
||||
$this->themeHandler->reset();
|
||||
// Notify the user that the theme info are rebuilt on every request.
|
||||
$this->triggerWarningIfNeeded($event->getRequest());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the user that the theme info are rebuilt on every request.
|
||||
*
|
||||
* The warning message is shown only to users with adequate permissions and
|
||||
* only once per session.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request.
|
||||
*/
|
||||
protected function triggerWarningIfNeeded(Request $request) {
|
||||
if ($this->account && $this->account->hasPermission('access devel information')) {
|
||||
$session = $request->getSession();
|
||||
if (!$session->has($this->notificationFlag)) {
|
||||
$session->set($this->notificationFlag, TRUE);
|
||||
$message = $this->t('The theme information is being rebuilt on every request. Remember to <a href=":url">turn off</a> this feature on production websites.', [':url' => Url::fromRoute('devel.admin_settings')->toString()]);
|
||||
drupal_set_message($message, 'warning', TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
// Set high priority value to start as early as possible.
|
||||
$events[KernelEvents::REQUEST][] = ['rebuildThemeInfo', 256];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,7 +32,7 @@ class ConfigEditor extends FormBase {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $config->get();
|
||||
$data = $config->getOriginal();
|
||||
|
||||
if (empty($data)) {
|
||||
drupal_set_message(t('Config @name exists but has no data.', array('@name' => $config_name)), 'warning');
|
||||
|
||||
+5
-5
@@ -11,7 +11,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
/**
|
||||
* Provides confirmation form for rebuilding the routes.
|
||||
*/
|
||||
class DevelRebuildMenus extends ConfirmFormBase {
|
||||
class RouterRebuildConfirmForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The route builder service.
|
||||
@@ -21,7 +21,7 @@ class DevelRebuildMenus extends ConfirmFormBase {
|
||||
protected $routeBuilder;
|
||||
|
||||
/**
|
||||
* Constructs a new DevelRebuildMenus object.
|
||||
* Constructs a new RouterRebuildConfirmForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteBuilderInterface $route_builder
|
||||
* The route builder service.
|
||||
@@ -50,7 +50,7 @@ class DevelRebuildMenus extends ConfirmFormBase {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to rebuild menus?');
|
||||
return $this->t('Are you sure you want to rebuild the router?');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,7 +64,7 @@ class DevelRebuildMenus extends ConfirmFormBase {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->t('Rebuild menu based on hook_menu() and revert any custom changes. All menu items return to their default settings.');
|
||||
return $this->t('Rebuilds the routes information gathering all routing data from .routing.yml files and from classes which subscribe to the route build events. This action cannot be undone.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +79,7 @@ class DevelRebuildMenus extends ConfirmFormBase {
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->routeBuilder->rebuild();
|
||||
drupal_set_message($this->t('The menu router has been rebuilt.'));
|
||||
drupal_set_message($this->t('The router has been rebuilt.'));
|
||||
$form_state->setRedirect('<front>');
|
||||
}
|
||||
|
||||
@@ -73,6 +73,12 @@ class SettingsForm extends ConfigFormBase {
|
||||
'#default_value' => $devel_config->get('raw_names'),
|
||||
'#description' => t('Display the language-independent machine names of the permissions in mouse-over hints on the <a href=":permissions_url">Permissions</a> page and the module base file names on the Permissions and <a href=":modules_url">Modules</a> pages.', array(':permissions_url' => Url::fromRoute('user.admin_permissions')->toString(), ':modules_url' => Url::fromRoute('system.modules_list')->toString())),
|
||||
);
|
||||
$form['rebuild_theme'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Rebuild the theme registry on every page load'),
|
||||
'#description' => $this->t('New templates, theme overrides, and changes to the theme.info.yml need the theme registry to be rebuilt in order to appear on the site.'),
|
||||
'#default_value' => $devel_config->get('rebuild_theme'),
|
||||
);
|
||||
|
||||
$error_handlers = devel_get_handlers();
|
||||
$form['error_handlers'] = array(
|
||||
@@ -116,13 +122,6 @@ class SettingsForm extends ConfigFormBase {
|
||||
$request->query->remove('demo');
|
||||
}
|
||||
|
||||
$form['rebuild_theme'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Rebuild the theme information like the registry'),
|
||||
'#description' => t('While creating new templates, change the $theme.info.yml and theme_ overrides the theme information needs to be rebuilt.'),
|
||||
'#default_value' => $devel_config->get('rebuild_theme'),
|
||||
);
|
||||
|
||||
$dumper = $devel_config->get('devel_dumper');
|
||||
$default = $this->dumperManager->isPluginSupported($dumper) ? $dumper : $this->dumperManager->getFallbackPluginId(NULL);
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class SystemStateEdit extends FormBase {
|
||||
$form['value'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Current value for %name', array('%name' => $state_name)),
|
||||
'#markup' => kprint_r($old_value, TRUE),
|
||||
'#markup' => kpr($old_value, TRUE),
|
||||
);
|
||||
|
||||
$transport = 'plain';
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Menu\MenuLinkTreeInterface;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Configures devel toolbar settings.
|
||||
*/
|
||||
class ToolbarSettingsForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* The menu link tree service.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkTree
|
||||
*/
|
||||
protected $menuLinkTree;
|
||||
|
||||
/**
|
||||
* ToolbarSettingsForm constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_link_tree
|
||||
* The menu link tree service.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, MenuLinkTreeInterface $menu_link_tree) {
|
||||
parent::__construct($config_factory);
|
||||
$this->menuLinkTree = $menu_link_tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('menu.link_tree')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'devel_toolbar_settings_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return [
|
||||
'devel.toolbar.settings',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('devel.toolbar.settings');
|
||||
|
||||
$form['toolbar_items'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Menu items always visible'),
|
||||
'#options' => $this->getLinkLabels(),
|
||||
'#default_value' => $config->get('toolbar_items') ?: [],
|
||||
'#required' => TRUE,
|
||||
'#description' => $this->t('Select the menu items always visible in devel toolbar tray. All the items not selected in this list will be visible only when the toolbar orientation is vertical.'),
|
||||
];
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$values = $form_state->getValues();
|
||||
$toolbar_items = array_keys(array_filter($values['toolbar_items']));
|
||||
|
||||
$this->config('devel.toolbar.settings')
|
||||
->set('toolbar_items', $toolbar_items)
|
||||
->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an array of available menu items.
|
||||
*
|
||||
* @return array
|
||||
* Associative array of devel menu item labels keyed by plugin ID.
|
||||
*/
|
||||
protected function getLinkLabels() {
|
||||
$options = [];
|
||||
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->onlyEnabledLinks()->setTopLevelOnly();
|
||||
$tree = $this->menuLinkTree->load('devel', $parameters);
|
||||
|
||||
foreach ($tree as $element) {
|
||||
$link = $element->link;
|
||||
$options[$link->getPluginId()] = $link->getTitle();
|
||||
}
|
||||
|
||||
asort($options);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,7 @@ class DevelLocalTask extends DeriverBase implements ContainerDeriverInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$this->derivatives = array();
|
||||
$this->derivatives = [];
|
||||
|
||||
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
|
||||
@@ -62,29 +62,36 @@ class DevelLocalTask extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
if ($has_edit_path || $has_canonical_path) {
|
||||
|
||||
$this->derivatives["$entity_type_id.devel_tab"] = array(
|
||||
$this->derivatives["$entity_type_id.devel_tab"] = [
|
||||
'route_name' => "entity.$entity_type_id." . ($has_edit_path ? 'devel_load' : 'devel_render'),
|
||||
'title' => $this->t('Devel'),
|
||||
'base_route' => "entity.$entity_type_id." . ($has_canonical_path ? "canonical" : "edit_form"),
|
||||
'weight' => 100,
|
||||
);
|
||||
];
|
||||
|
||||
$this->derivatives["$entity_type_id.devel_definition_tab"] = [
|
||||
'route_name' => "entity.$entity_type_id.devel_definition",
|
||||
'title' => $this->t('Definition'),
|
||||
'parent_id' => "devel.entities:$entity_type_id.devel_tab",
|
||||
'weight' => 100,
|
||||
];
|
||||
|
||||
if ($has_canonical_path) {
|
||||
$this->derivatives["$entity_type_id.devel_render_tab"] = array(
|
||||
$this->derivatives["$entity_type_id.devel_render_tab"] = [
|
||||
'route_name' => "entity.$entity_type_id.devel_render",
|
||||
'weight' => 100,
|
||||
'title' => $this->t('Render'),
|
||||
'parent_id' => "devel.entities:$entity_type_id.devel_tab",
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
if ($has_edit_path) {
|
||||
$this->derivatives["$entity_type_id.devel_load_tab"] = array(
|
||||
$this->derivatives["$entity_type_id.devel_load_tab"] = [
|
||||
'route_name' => "entity.$entity_type_id.devel_load",
|
||||
'weight' => 100,
|
||||
'title' => $this->t('Load'),
|
||||
'parent_id' => "devel.entities:$entity_type_id.devel_tab",
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Drupal\devel\Plugin\Devel\Dumper;
|
||||
|
||||
use Doctrine\Common\Util\Debug;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\devel\DevelDumperBase;
|
||||
|
||||
/**
|
||||
@@ -16,16 +17,6 @@ use Drupal\devel\DevelDumperBase;
|
||||
*/
|
||||
class DoctrineDebug extends DevelDumperBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump($input, $name = NULL) {
|
||||
if ($name) {
|
||||
echo $name . ' => ';
|
||||
}
|
||||
Debug::dump($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -38,6 +29,10 @@ class DoctrineDebug extends DevelDumperBase {
|
||||
$dump = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// Run Xss::filterAdmin on the resulting string to prevent
|
||||
// cross-site-scripting (XSS) vulnerabilities.
|
||||
$dump = Xss::filterAdmin($dump);
|
||||
|
||||
$dump = '<pre>' . $name . $dump . '</pre>';
|
||||
|
||||
return $this->setSafeMarkup($dump);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Drupal\devel\Plugin\Devel\Dumper;
|
||||
|
||||
use Drupal\Component\Utility\Variable;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\devel\DevelDumperBase;
|
||||
|
||||
/**
|
||||
@@ -16,21 +17,16 @@ use Drupal\devel\DevelDumperBase;
|
||||
*/
|
||||
class DrupalVariable extends DevelDumperBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump($input, $name = NULL) {
|
||||
$name = $name ? $name . ' => ' : '';
|
||||
$output = Variable::export($input);
|
||||
echo '<pre>' . $name . print_r($output) . '</pre>';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function export($input, $name = NULL) {
|
||||
$name = $name ? $name . ' => ' : '';
|
||||
$dump = '<pre>' . $name . Variable::export($input) . '</pre>';
|
||||
$dump = Variable::export($input);
|
||||
// Run Xss::filterAdmin on the resulting string to prevent
|
||||
// cross-site-scripting (XSS) vulnerabilities.
|
||||
$dump = Xss::filterAdmin($dump);
|
||||
$dump = '<pre>' . $name . $dump . '</pre>';
|
||||
return $this->setSafeMarkup($dump);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,13 +19,6 @@ use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
||||
*/
|
||||
class VarDumper extends DevelDumperBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump($input, $name = NULL) {
|
||||
echo (string) $this->export($input, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -37,6 +30,10 @@ class VarDumper extends DevelDumperBase {
|
||||
$dumper->dump($cloner->cloneVar($input), $output);
|
||||
$output = stream_get_contents($output, -1, 0);
|
||||
|
||||
if ($name) {
|
||||
$output = $name . ' => ' . $output;
|
||||
}
|
||||
|
||||
return $this->setSafeMarkup($output);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
namespace Drupal\devel\Plugin\Mail;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Mail\Plugin\Mail\PhpMail;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Mail\MailFormatHelper;
|
||||
use Drupal\Core\Mail\MailInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Exception;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a mail backend that saves emails as temporary files.
|
||||
@@ -39,10 +42,86 @@ use Exception;
|
||||
* description = @Translation("Outputs the message as a file in the temporary directory.")
|
||||
* )
|
||||
*/
|
||||
class DevelMailLog extends PhpMail {
|
||||
class DevelMailLog implements MailInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
public function composeMessage($message) {
|
||||
$mimeheaders = array();
|
||||
/**
|
||||
* The devel.settings config object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config;
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Constructs a new DevelMailLog object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory service.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $config_factory) {
|
||||
$this->config = $config_factory->get('devel.settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('config.factory')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mail(array $message) {
|
||||
$directory = $this->config->get('debug_mail_directory');
|
||||
|
||||
if (!$this->prepareDirectory($directory)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$pattern = $this->config->get('debug_mail_file_format');
|
||||
$filename = $this->replacePlaceholders($pattern, $message);
|
||||
$output = $this->composeMessage($message);
|
||||
|
||||
return (bool) file_put_contents($directory . '/' . $filename, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function format(array $message) {
|
||||
// Join the body array into one string.
|
||||
$message['body'] = implode("\n\n", $message['body']);
|
||||
|
||||
// Convert any HTML to plain-text.
|
||||
$message['body'] = MailFormatHelper::htmlToText($message['body']);
|
||||
// Wrap the mail body for sending.
|
||||
$message['body'] = MailFormatHelper::wrapMail($message['body']);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose the output message.
|
||||
*
|
||||
* @param array $message
|
||||
* A message array, as described in hook_mail_alter().
|
||||
*
|
||||
* @return string
|
||||
* The output message.
|
||||
*/
|
||||
protected function composeMessage($message) {
|
||||
$mimeheaders = [];
|
||||
$message['headers']['To'] = $message['to'];
|
||||
foreach ($message['headers'] as $name => $value) {
|
||||
$mimeheaders[] = $name . ': ' . Unicode::mimeHeaderEncode($value);
|
||||
@@ -58,41 +137,52 @@ class DevelMailLog extends PhpMail {
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function getFileName($message) {
|
||||
$output_directory = $this->getOutputDirectory();
|
||||
$this->makeOutputDirectory($output_directory);
|
||||
$output_file_format = \Drupal::config('devel.settings')->get('debug_mail_file_format');
|
||||
|
||||
$tokens = array(
|
||||
/**
|
||||
* Replaces placeholders with sanitized values in a string.
|
||||
*
|
||||
* @param $filename
|
||||
* The string that contains the placeholders. The following placeholders
|
||||
* are considered in the replacement:
|
||||
* - %to: replaced by the email recipient value.
|
||||
* - %subject: replaced by the email subject value.
|
||||
* - %datetime: replaced by the current datetime in 'y-m-d_his' format.
|
||||
* @param array $message
|
||||
* A message array, as described in hook_mail_alter().
|
||||
*
|
||||
* @return string
|
||||
* The formatted string.
|
||||
*/
|
||||
protected function replacePlaceholders($filename, $message) {
|
||||
$tokens = [
|
||||
'%to' => $message['to'],
|
||||
'%subject' => $message['subject'],
|
||||
'%datetime' => date('y-m-d_his'),
|
||||
);
|
||||
return $output_directory . '/' . $this->dirify(str_replace(array_keys($tokens), array_values($tokens), $output_file_format));
|
||||
}
|
||||
|
||||
private function dirify($string) {
|
||||
return preg_replace('/[^a-zA-Z0-9_\-\.@]/', '_', $string);
|
||||
];
|
||||
$filename = str_replace(array_keys($tokens), array_values($tokens), $filename);
|
||||
return preg_replace('/[^a-zA-Z0-9_\-\.@]/', '_', $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Checks that the directory exists and is writable.
|
||||
* Public directories will be protected by adding an .htaccess which
|
||||
* indicates that the directory is private.
|
||||
*
|
||||
* @param $directory
|
||||
* A string reference containing the name of a directory path or URI.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the directory exists (or was created), is writable and is
|
||||
* protected (if it is public). FALSE otherwise.
|
||||
*/
|
||||
public function mail(array $message) {
|
||||
$output = $this->composeMessage($message);
|
||||
$output_file = $this->getFileName($message);
|
||||
|
||||
return file_put_contents($output_file, $output);
|
||||
}
|
||||
|
||||
protected function makeOutputDirectory($output_directory) {
|
||||
if (!file_prepare_directory($output_directory, FILE_CREATE_DIRECTORY)) {
|
||||
throw new Exception("Unable to continue sending mail, $output_directory is not writable");
|
||||
protected function prepareDirectory($directory) {
|
||||
if (!file_prepare_directory($directory, FILE_CREATE_DIRECTORY)) {
|
||||
return FALSE;
|
||||
}
|
||||
if (0 === strpos($directory, 'public://')) {
|
||||
return file_save_htaccess($directory);
|
||||
}
|
||||
}
|
||||
|
||||
public function getOutputDirectory() {
|
||||
return \Drupal::config('devel.settings')->get('debug_mail_directory');
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,28 +2,10 @@
|
||||
|
||||
namespace Drupal\devel\Plugin\Menu;
|
||||
|
||||
use Drupal\Core\Menu\MenuLinkDefault;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Modifies the menu link to add current route path.
|
||||
*
|
||||
* @deprecated in Devel 8.1.0-beta1, will be removed before Devel 8.1.0.
|
||||
* Use \Drupal\devel\Plugin\Menu\RouteDetailMenuLink instead.
|
||||
*/
|
||||
class MenuItemMenuLink extends MenuLinkDefault {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptions() {
|
||||
$options = parent::getOptions();
|
||||
$options['query']['path'] = '/' . Url::fromRoute('<current>')->getInternalPath();
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
class MenuItemMenuLink extends RouteDetailMenuLink {}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Plugin\Menu;
|
||||
|
||||
use Drupal\Core\Menu\MenuLinkDefault;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Modifies the menu link to add current route path.
|
||||
*/
|
||||
class RouteDetailMenuLink extends MenuLinkDefault {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptions() {
|
||||
$options = parent::getOptions();
|
||||
$options['query']['path'] = '/' . Url::fromRoute('<current>')->getInternalPath();
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Render;
|
||||
|
||||
use Drupal\Component\Render\MarkupInterface;
|
||||
use Drupal\Component\Render\MarkupTrait;
|
||||
|
||||
/**
|
||||
* Defines an object that passes safe strings through the Devel system.
|
||||
*
|
||||
* This object should only be constructed with a known safe string. If there is
|
||||
* any risk that the string contains user-entered data that has not been
|
||||
* filtered first, it must not be used.
|
||||
*
|
||||
* @internal
|
||||
* This object is marked as internal because it should only be used in the
|
||||
* Devel module.
|
||||
* @see \Drupal\Core\Render\Markup
|
||||
*/
|
||||
final class FilteredMarkup implements MarkupInterface, \Countable {
|
||||
use MarkupTrait;
|
||||
|
||||
}
|
||||
@@ -45,11 +45,14 @@ class RouteSubscriber extends RouteSubscriberBase {
|
||||
if ($route = $this->getEntityRenderRoute($entity_type)) {
|
||||
$collection->add("entity.$entity_type_id.devel_render", $route);
|
||||
}
|
||||
if ($route = $this->getEntityTypeDefinitionRoute($entity_type)) {
|
||||
$collection->add("entity.$entity_type_id.devel_definition", $route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the devel load route.
|
||||
* Gets the entity load route.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
@@ -80,7 +83,7 @@ class RouteSubscriber extends RouteSubscriberBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the devel render route.
|
||||
* Gets the entity render route.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
@@ -110,6 +113,37 @@ class RouteSubscriber extends RouteSubscriberBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity type definition route.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\Route|null
|
||||
* The generated route, if available.
|
||||
*/
|
||||
protected function getEntityTypeDefinitionRoute(EntityTypeInterface $entity_type) {
|
||||
if ($devel_definition = $entity_type->getLinkTemplate('devel-definition')) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
$route = new Route($devel_definition);
|
||||
$route
|
||||
->addDefaults([
|
||||
'_controller' => '\Drupal\devel\Controller\EntityDebugController::entityTypeDefinition',
|
||||
'_title' => 'Entity type definition',
|
||||
])
|
||||
->addRequirements([
|
||||
'_permission' => 'access devel information'
|
||||
])
|
||||
->setOption('_admin_route', TRUE)
|
||||
->setOption('_devel_entity_type_id', $entity_type_id)
|
||||
->setOption('parameters', [
|
||||
$entity_type_id => ['type' => 'entity:' . $entity_type_id],
|
||||
]);
|
||||
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
@@ -65,11 +65,15 @@ class DevelControllerTest extends WebTestBase {
|
||||
$this->assertText('Devel', 'Devel tab is present');
|
||||
$this->drupalGet('devel/entity_test/' . $this->entity->id());
|
||||
$this->assertResponse(200);
|
||||
$this->assertText('Definition', 'Devel definition tab is present');
|
||||
$this->assertText('Load', 'Devel load tab is present');
|
||||
$this->assertText('Render', 'Devel load tab is present');
|
||||
$this->assertLinkByHref('devel/entity_test/' . $this->entity->id() . '/render');
|
||||
$this->drupalGet('devel/entity_test/' . $this->entity->id() . '/render');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLinkByHref('devel/entity_test/' . $this->entity->id() . '/definition');
|
||||
$this->drupalGet('devel/entity_test/' . $this->entity->id() . '/definition');
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Test Devel load and render routes for entities with only canonical route
|
||||
// definitions.
|
||||
@@ -82,6 +86,9 @@ class DevelControllerTest extends WebTestBase {
|
||||
$this->assertResponse(404);
|
||||
$this->drupalGet('devel/devel_entity_test_canonical/' . $this->entity_canonical->id() . '/render');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLinkByHref('devel/devel_entity_test_canonical/' . $this->entity_canonical->id() . '/definition');
|
||||
$this->drupalGet('devel/devel_entity_test_canonical/' . $this->entity_canonical->id() . '/definition');
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Test Devel load and render routes for entities with only edit route
|
||||
// definitions.
|
||||
@@ -89,10 +96,13 @@ class DevelControllerTest extends WebTestBase {
|
||||
$this->assertText('Devel', 'Devel tab is present');
|
||||
$this->assertLinkByHref('devel/devel_entity_test_edit/' . $this->entity_edit->id());
|
||||
$this->assertNoLinkByHref('devel/devel_entity_test_edit/' . $this->entity_edit->id() . '/render');
|
||||
$this->assertNoLinkByHref('devel/devel_entity_test_edit/' . $this->entity_edit->id() . '/definition');
|
||||
$this->drupalGet('devel/devel_entity_test_edit/' . $this->entity_edit->id());
|
||||
$this->assertResponse(200);
|
||||
$this->drupalGet('devel/devel_entity_test_edit/' . $this->entity_edit->id() . '/render');
|
||||
$this->assertResponse(404);
|
||||
$this->drupalGet('devel/devel_entity_test_edit/' . $this->entity_edit->id() . '/definition');
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Test Devel load and render routes for entities with no route
|
||||
// definitions.
|
||||
@@ -100,10 +110,13 @@ class DevelControllerTest extends WebTestBase {
|
||||
$this->assertNoText('Devel', 'Devel tab is not present');
|
||||
$this->assertNoLinkByHref('devel/devel_entity_test_no_links/' . $this->entity_no_links->id());
|
||||
$this->assertNoLinkByHref('devel/devel_entity_test_no_links/' . $this->entity_no_links->id() . '/render');
|
||||
$this->assertNoLinkByHref('devel/devel_entity_test_no_links/' . $this->entity_no_links->id() . '/definition');
|
||||
$this->drupalGet('devel/devel_entity_test_no_links/' . $this->entity_no_links->id());
|
||||
$this->assertResponse(404);
|
||||
$this->drupalGet('devel/devel_entity_test_no_links/' . $this->entity_no_links->id() . '/render');
|
||||
$this->assertResponse(404);
|
||||
$this->drupalGet('devel/devel_entity_test_no_links/' . $this->entity_no_links->id() . '/definition');
|
||||
$this->assertResponse(404);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Tests;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests devel error handler.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelErrorHandlerTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['devel'];
|
||||
|
||||
/**
|
||||
* Tests devel error handler.
|
||||
*/
|
||||
public function testErrorHandler() {
|
||||
$error_notice = [
|
||||
'%type' => 'Notice',
|
||||
'@message' => 'Undefined variable: undefined',
|
||||
'%function' => 'Drupal\devel\Form\SettingsForm->demonstrateErrorHandlers()',
|
||||
];
|
||||
$error_warning = [
|
||||
'%type' => 'Warning',
|
||||
'@message' => 'Division by zero',
|
||||
'%function' => 'Drupal\devel\Form\SettingsForm->demonstrateErrorHandlers()',
|
||||
];
|
||||
|
||||
$config = $this->config('system.logging');
|
||||
$config->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save();
|
||||
|
||||
$admin_user = $this->drupalCreateUser(['administer site configuration', 'access devel information']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Ensures that the error handler config is present on the config page and
|
||||
// by default the standard error handler is selected.
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEqual($error_handlers, [DEVEL_ERROR_HANDLER_STANDARD => DEVEL_ERROR_HANDLER_STANDARD]);
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_STANDARD);
|
||||
|
||||
// Ensures that selecting the DEVEL_ERROR_HANDLER_NONE option no error
|
||||
// (raw or message) is shown on the site in case of php errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => DEVEL_ERROR_HANDLER_NONE,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEqual($error_handlers, [DEVEL_ERROR_HANDLER_NONE => DEVEL_ERROR_HANDLER_NONE]);
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_NONE);
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertResponse(200, 'Received expected HTTP status code.');
|
||||
$this->assertNoRawErrorMessage($error_notice);
|
||||
$this->assertNoRawErrorMessage($error_warning);
|
||||
$this->assertNoErrorMessage($error_notice);
|
||||
$this->assertNoErrorMessage($error_warning);
|
||||
|
||||
// Ensures that selecting the DEVEL_ERROR_HANDLER_BACKTRACE_KINT option a
|
||||
// backtrace above the rendered page is shown on the site in case of php
|
||||
// errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => DEVEL_ERROR_HANDLER_BACKTRACE_KINT,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEqual($error_handlers, [DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT]);
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_KINT);
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertResponse(200, 'Received expected HTTP status code.');
|
||||
$this->assertRawErrorMessage($error_notice);
|
||||
$this->assertRawErrorMessage($error_warning);
|
||||
|
||||
// Ensures that selecting the DEVEL_ERROR_HANDLER_BACKTRACE_DPM option a
|
||||
// backtrace in the message area is shown on the site in case of php errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => DEVEL_ERROR_HANDLER_BACKTRACE_DPM,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEqual($error_handlers, [DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM]);
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_DPM);
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertResponse(200, 'Received expected HTTP status code.');
|
||||
$this->assertErrorMessage($error_notice);
|
||||
$this->assertErrorMessage($error_warning);
|
||||
|
||||
// Ensures that when multiple handlers are selected, the output produced by
|
||||
// every handler is shown on the site in case of php errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => [
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM,
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT,
|
||||
]
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEqual($error_handlers, [
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM,
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT,
|
||||
]);
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_DPM);
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_KINT);
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertResponse(200, 'Received expected HTTP status code.');
|
||||
$this->assertRawErrorMessage($error_notice);
|
||||
$this->assertRawErrorMessage($error_warning);
|
||||
$this->assertErrorMessage($error_notice);
|
||||
$this->assertErrorMessage($error_warning);
|
||||
|
||||
// Ensures that setting the error reporting to all the output produced by
|
||||
// handlers is shown on the site in case of php errors.
|
||||
$config->set('error_level', ERROR_REPORTING_DISPLAY_ALL)->save();
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertResponse(200, 'Received expected HTTP status code.');
|
||||
$this->assertRawErrorMessage($error_notice);
|
||||
$this->assertRawErrorMessage($error_warning);
|
||||
$this->assertErrorMessage($error_notice);
|
||||
$this->assertErrorMessage($error_warning);
|
||||
|
||||
// Ensures that setting the error reporting to some the output produced by
|
||||
// handlers is shown on the site in case of php errors.
|
||||
$config->set('error_level', ERROR_REPORTING_DISPLAY_SOME)->save();
|
||||
$this->assertResponse(200, 'Received expected HTTP status code.');
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertRawErrorMessage($error_notice);
|
||||
$this->assertRawErrorMessage($error_warning);
|
||||
$this->assertErrorMessage($error_notice);
|
||||
$this->assertErrorMessage($error_warning);
|
||||
|
||||
// Ensures that setting the error reporting to none the output produced by
|
||||
// handlers is not shown on the site in case of php errors.
|
||||
$config->set('error_level', ERROR_REPORTING_HIDE)->save();
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertResponse(200, 'Received expected HTTP status code.');
|
||||
$this->assertNoRawErrorMessage($error_notice);
|
||||
$this->assertNoRawErrorMessage($error_warning);
|
||||
$this->assertNoErrorMessage($error_notice);
|
||||
$this->assertNoErrorMessage($error_warning);
|
||||
|
||||
// The errors are expected. Do not interpret them as a test failure.
|
||||
// Not using File API; a potential error must trigger a PHP warning.
|
||||
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function: assert that the error message is found.
|
||||
*
|
||||
* @param array $error
|
||||
* The error to check.
|
||||
*/
|
||||
protected function assertRawErrorMessage(array $error) {
|
||||
$message = new FormattableMarkup('%type: @message in %function (line ', $error);
|
||||
$this->assertRaw($message, new FormattableMarkup('Found raw error message: @message.', ['@message' => $message]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function: assert that the error message is not found.
|
||||
*
|
||||
*
|
||||
* @param array $error
|
||||
* The error to check.
|
||||
*/
|
||||
protected function assertNoRawErrorMessage(array $error) {
|
||||
$message = new FormattableMarkup('%type: @message in %function (line ', $error);
|
||||
$this->assertNoRaw($message, new FormattableMarkup('Did not find raw error message: @message.', ['@message' => $message]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function: assert that the error message is found.
|
||||
*
|
||||
* @param array $error
|
||||
* The error to check.
|
||||
*/
|
||||
protected function assertErrorMessage(array $error) {
|
||||
$pattern = '//div[contains(@class, "messages--warning")]//pre[contains(., :content)]';
|
||||
$message = new FormattableMarkup('%type: @message in %function (line ', $error);
|
||||
$message = html_entity_decode(strip_tags((string) $message));
|
||||
$xpath = $this->xpath($pattern, [':content' => $message]);
|
||||
$this->assertTrue(!empty($xpath), new FormattableMarkup('Found error message: @message.', ['@message' => $message]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function: assert that the error message is not found.
|
||||
*
|
||||
* @param array $error
|
||||
* The error to check.
|
||||
*/
|
||||
protected function assertNoErrorMessage(array $error) {
|
||||
$pattern = '//div[contains(@class, "messages--warning")]//pre[contains(., :content)]';
|
||||
$message = new FormattableMarkup('%type: @message in %function (line ', $error);
|
||||
$message = html_entity_decode(strip_tags((string) $message));
|
||||
$xpath = $this->xpath($pattern, [':content' => $message]);
|
||||
$this->assertTrue(empty($xpath), new FormattableMarkup('Found error message: @message.', ['@message' => $message]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Tests;
|
||||
|
||||
use Drupal\devel\Plugin\Mail\DevelMailLog;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests sending mails with debug interface.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelMailTest extends WebTestBase {
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('devel');
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* Test mail logging functionality.
|
||||
*/
|
||||
public function testDevelMail() {
|
||||
$message = array();
|
||||
$message['to'] = 'drupal@example.com';
|
||||
$message['subject'] = 'Test mail';
|
||||
$message['headers'] = array(
|
||||
'From' => 'postmaster@example.com',
|
||||
'X-stupid' => 'dumb',
|
||||
);
|
||||
$message['body'] = "I am the body of this message";
|
||||
$d = new DevelMailLog();
|
||||
|
||||
$filename = $d->getFileName($message);
|
||||
$content = $d->composeMessage($message);
|
||||
$expected_filename = $d->getOutputDirectory() . '/drupal@example.com-Test_mail-' . date('y-m-d_his') . '.mail.txt';
|
||||
$this->assertEqual($filename, $expected_filename);
|
||||
$content = str_replace("\r", '', $content);
|
||||
$this->assertEqual($content, 'From: postmaster@example.com
|
||||
X-stupid: dumb
|
||||
To: drupal@example.com
|
||||
Subject: Test mail
|
||||
|
||||
I am the body of this message');
|
||||
}
|
||||
}
|
||||
@@ -106,42 +106,4 @@ class DevelMenuLinksTest extends WebTestBase {
|
||||
$this->assertUrl($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests menu item link.
|
||||
*/
|
||||
public function testMenuItemLink() {
|
||||
// Ensures that devel menu item works properly.
|
||||
$url = $this->develUser->toUrl();
|
||||
$path = '/' . $url->getInternalPath();
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->clickLink(t('Menu Item'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertText('Menu item');
|
||||
$this->assertUrl('devel/menu/item', ['query' => ['path' => $path]]);
|
||||
|
||||
// Ensures that devel menu item works properly even when dynamic cache is
|
||||
// enabled.
|
||||
$url = Url::fromRoute('devel.simple_page');
|
||||
$path = '/' . $url->getInternalPath();
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->clickLink(t('Menu Item'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertText('Menu item');
|
||||
$this->assertUrl('devel/menu/item', ['query' => ['path' => $path]]);
|
||||
|
||||
// Ensures that if no 'path' query string is passed devel menu item does
|
||||
// not return errors.
|
||||
$this->drupalGet('devel/menu/item');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText('Menu item');
|
||||
|
||||
// Ensures that devel menu item is accessible ony to users with the
|
||||
// adequate permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/menu/item');
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests routes rebuild.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelRebuildMenusTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('devel');
|
||||
|
||||
/**
|
||||
* Set up test.
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(array('administer site configuration'));
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test routes rebuild.
|
||||
*/
|
||||
public function testDevelRebuildMenus() {
|
||||
$this->drupalGet('devel/menu/reset');
|
||||
$this->assertResponse(200);
|
||||
$this->drupalPostForm('devel/menu/reset', array(), t('Rebuild'));
|
||||
$this->assertText(t('The menu router has been rebuilt.'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Tests;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests devel state editor.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelStateEditorTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['devel'];
|
||||
|
||||
/**
|
||||
* The state store.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* The user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->state = $this->container->get('state');
|
||||
|
||||
$this->develUser = $this->drupalCreateUser(['access devel information']);
|
||||
$this->adminUser = $this->drupalCreateUser(['access devel information', 'administer site configuration']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests state listing.
|
||||
*/
|
||||
public function testStateListing() {
|
||||
// Ensure that state listing page is accessible only by users with the
|
||||
// adequate permissions.
|
||||
$this->drupalGet('devel/state');
|
||||
$this->assertResponse(403);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->drupalGet('devel/state');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText(t('State editor'));
|
||||
|
||||
// Ensure that the state variables table is visible.
|
||||
$table = $this->xpath('//table[contains(@class, "devel-state-list")]');
|
||||
$this->assertTrue($table, 'State list table found.');
|
||||
|
||||
// Ensure that all state variables are listed in the table.
|
||||
$states = \Drupal::keyValue('state')->getAll();
|
||||
$rows = $this->xpath('//table[contains(@class, "devel-state-list")]//tbody//tr');
|
||||
$this->assertEqual(count($rows), count($states), 'All states are listed in the table.');
|
||||
|
||||
// Ensure that the added state variables are listed in the table.
|
||||
$this->state->set('devel.simple', 'Hello!');
|
||||
$this->drupalGet('devel/state');
|
||||
$this->assertFieldByXpath('//table[contains(@class, "devel-state-list")]//tbody//td', 'devel.simple', 'Label found for the added state.');
|
||||
|
||||
$thead_xpath = '//table[contains(@class, "devel-state-list")]/thead/tr/th';
|
||||
$action_xpath = '//table[contains(@class, "devel-state-list")]//ul[@class="dropbutton"]/li/a';
|
||||
|
||||
// Ensure that the operations column and the actions buttons are not
|
||||
// available for user without 'administer site configuration' permission.
|
||||
$elements = $this->xpath($thead_xpath);
|
||||
$this->assertEqual(count($elements), 2, 'Correct number of table header cells found.');
|
||||
|
||||
$expected_items = ['Name', 'Value'];
|
||||
foreach ($elements as $key => $element) {
|
||||
$this->assertIdentical((string) $element[0], $expected_items[$key]);
|
||||
}
|
||||
|
||||
$this->assertFalse($this->xpath($action_xpath), 'Action buttons are not visible.');
|
||||
|
||||
// Ensure that the operations column and the actions buttons are
|
||||
// available for user with 'administer site configuration' permission.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('devel/state');
|
||||
|
||||
$elements = $this->xpath($thead_xpath);
|
||||
$this->assertEqual(count($elements), 3, 'Correct number of table header cells found.');
|
||||
|
||||
$expected_items = ['Name', 'Value', 'Operations'];
|
||||
foreach ($elements as $key => $element) {
|
||||
$this->assertIdentical((string) $element[0], $expected_items[$key]);
|
||||
}
|
||||
|
||||
$this->assertTrue($this->xpath($action_xpath), 'Action buttons are visible.');
|
||||
|
||||
// Test that the edit button works properly.
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests state edit.
|
||||
*/
|
||||
public function testStateEdit() {
|
||||
// Create some state variables for the test.
|
||||
$this->state->set('devel.simple', 0);
|
||||
$this->state->set('devel.array', ['devel' => 'value']);
|
||||
$this->state->set('devel.object', $this->randomObject());
|
||||
|
||||
// Ensure that state edit form is accessible only by users with the
|
||||
// adequate permissions.
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->drupalGet('devel/state/edit/devel.simple');
|
||||
$this->assertResponse(403);
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Ensure that accessing an un-existent state variable cause a warning
|
||||
// message.
|
||||
$this->drupalGet('devel/state/edit/devel.unknown');
|
||||
$this->assertText(t('State @name does not exist in the system.', ['@name' => 'devel.unknown']));
|
||||
|
||||
// Ensure that state variables that contain simple type can be edited and
|
||||
// saved.
|
||||
$this->drupalGet('devel/state/edit/devel.simple');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText(t('Edit state variable: @name', ['@name' => 'devel.simple']));
|
||||
$this->assertInputNotDisabledById('edit-new-value');
|
||||
$this->assertInputNotDisabledById('edit-submit');
|
||||
|
||||
$edit = ['new_value' => 1];
|
||||
$this->drupalPostForm('devel/state/edit/devel.simple', $edit, t('Save'));
|
||||
$this->assertText(t('Variable @name was successfully edited.', ['@name' => 'devel.simple']));
|
||||
$this->assertEqual(1, $this->state->get('devel.simple'));
|
||||
|
||||
// Ensure that state variables that contain array can be edited and saved
|
||||
// and the new value is properly validated.
|
||||
$this->drupalGet('devel/state/edit/devel.array');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText(t('Edit state variable: @name', ['@name' => 'devel.array']));
|
||||
$this->assertInputNotDisabledById('edit-new-value');
|
||||
$this->assertInputNotDisabledById('edit-submit');
|
||||
|
||||
// Try to save an invalid yaml input.
|
||||
$edit = ['new_value' => 'devel: \'value updated'];
|
||||
$this->drupalPostForm('devel/state/edit/devel.array', $edit, t('Save'));
|
||||
$this->assertText(t('Invalid input:'));
|
||||
|
||||
$edit = ['new_value' => 'devel: \'value updated\''];
|
||||
$this->drupalPostForm('devel/state/edit/devel.array', $edit, t('Save'));
|
||||
$this->assertText(t('Variable @name was successfully edited.', ['@name' => 'devel.array']));
|
||||
$this->assertEqual(['devel' => 'value updated'], $this->state->get('devel.array'));
|
||||
|
||||
// Ensure that state variables that contain objects cannot be edited.
|
||||
$this->drupalGet('devel/state/edit/devel.object');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText(t('Edit state variable: @name', ['@name' => 'devel.object']));
|
||||
$this->assertText(t('Only simple structures are allowed to be edited. State @name contains objects.', ['@name' => 'devel.object']));
|
||||
$this->assertInputDisabledById('edit-new-value');
|
||||
$this->assertInputDisabledById('edit-submit');
|
||||
|
||||
// Ensure that the cancel link works as expected.
|
||||
$this->clickLink(t('Cancel'));
|
||||
$this->assertUrl('devel/state');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for check if an input is disabled.
|
||||
*
|
||||
* @param string $id
|
||||
* The ID of the input.
|
||||
*/
|
||||
protected function assertInputDisabledById($id) {
|
||||
$message = new FormattableMarkup('The input %id is disabled.', ['%id' => $id]);
|
||||
$xpath = '//textarea[@id=:id and @disabled="disabled"]|//input[@id=:id and @disabled="disabled"]|//select[@id=:id and @disabled="disabled"]';
|
||||
$query = $this->buildXPathQuery($xpath, [':id' => $id]);
|
||||
$this->assertFieldByXPath($query, NULL, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for check if an input is not disabled.
|
||||
*
|
||||
* @param string $id
|
||||
* The ID of the input.
|
||||
*/
|
||||
protected function assertInputNotDisabledById($id) {
|
||||
$message = new FormattableMarkup('The input %id is not disabled.', ['%id' => $id]);
|
||||
$xpath = '//textarea[@id=:id and not(@disabled="disabled")]|//input[@id=:id and not(@disabled="disabled")]|//select[@id=:id and not(@disabled="disabled")]';
|
||||
$query = $this->buildXPathQuery($xpath, [':id' => $id]);
|
||||
$this->assertFieldByXPath($query, NULL, $message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -70,7 +70,7 @@ class DevelSwitchUserTest extends WebTestBase {
|
||||
$this->assertNoText($this->block->label(), 'Block title was not found.');
|
||||
|
||||
// Ensure that a token is required to switch user.
|
||||
$this->drupalGet('/devel/switch/' . $this->webUser->getUsername());
|
||||
$this->drupalGet('/devel/switch/' . $this->webUser->getDisplayName());
|
||||
$this->assertResponse(403);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
@@ -83,22 +83,22 @@ class DevelSwitchUserTest extends WebTestBase {
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Ensure that a token is required to switch user.
|
||||
$this->drupalGet('/devel/switch/' . $this->switchUser->getUsername());
|
||||
$this->drupalGet('/devel/switch/' . $this->switchUser->getDisplayName());
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Switch to another user account.
|
||||
$this->drupalGet('/user/' . $this->switchUser->id());
|
||||
$this->clickLink($this->switchUser->getUsername());
|
||||
$this->clickLink($this->switchUser->getDisplayName());
|
||||
$this->assertSessionByUid($this->switchUser->id());
|
||||
$this->assertNoSessionByUid($this->develUser->id());
|
||||
|
||||
// Switch back to initial account.
|
||||
$this->clickLink($this->develUser->getUsername());
|
||||
$this->clickLink($this->develUser->getDisplayName());
|
||||
$this->assertNoSessionByUid($this->switchUser->id());
|
||||
$this->assertSessionByUid($this->develUser->id());
|
||||
|
||||
// Use the search form to switch to another account.
|
||||
$edit = ['userid' => $this->switchUser->getUsername()];
|
||||
$edit = ['userid' => $this->switchUser->getDisplayName()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Switch'));
|
||||
$this->assertSessionByUid($this->switchUser->id());
|
||||
$this->assertNoSessionByUid($this->develUser->id());
|
||||
@@ -166,22 +166,22 @@ class DevelSwitchUserTest extends WebTestBase {
|
||||
|
||||
// Ensure that user with 'switch users' permission are prioritized.
|
||||
$this->assertSwitchUserListCount(2);
|
||||
$this->assertSwitchUserListContainsUser($this->develUser->getUsername());
|
||||
$this->assertSwitchUserListContainsUser($this->switchUser->getUsername());
|
||||
$this->assertSwitchUserListContainsUser($this->develUser->getDisplayName());
|
||||
$this->assertSwitchUserListContainsUser($this->switchUser->getDisplayName());
|
||||
|
||||
// Ensure that blocked users are not shown in the list.
|
||||
$this->switchUser->set('status', 0)->save();
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserListCount(2);
|
||||
$this->assertSwitchUserListContainsUser($this->develUser->getUsername());
|
||||
$this->assertSwitchUserListContainsUser($this->webUser->getUsername());
|
||||
$this->assertSwitchUserListNoContainsUser($this->switchUser->getUsername());
|
||||
$this->assertSwitchUserListContainsUser($this->develUser->getDisplayName());
|
||||
$this->assertSwitchUserListContainsUser($this->webUser->getDisplayName());
|
||||
$this->assertSwitchUserListNoContainsUser($this->switchUser->getDisplayName());
|
||||
|
||||
// Ensure that anonymous user are prioritized if include_anon is set to true.
|
||||
$this->setBlockConfiguration('include_anon', TRUE);
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserListCount(2);
|
||||
$this->assertSwitchUserListContainsUser($this->develUser->getUsername());
|
||||
$this->assertSwitchUserListContainsUser($this->develUser->getDisplayName());
|
||||
$this->assertSwitchUserListContainsUser($anonymous);
|
||||
|
||||
// Ensure that the switch user block works properly even if no prioritized
|
||||
@@ -192,7 +192,7 @@ class DevelSwitchUserTest extends WebTestBase {
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserListCount(2);
|
||||
$this->assertSwitchUserListContainsUser($this->rootUser->getUsername());
|
||||
$this->assertSwitchUserListContainsUser($this->rootUser->getDisplayName());
|
||||
$this->assertSwitchUserListContainsUser($anonymous);
|
||||
|
||||
// Ensure that the switch user block works properly even if no roles have
|
||||
@@ -202,7 +202,7 @@ class DevelSwitchUserTest extends WebTestBase {
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertSwitchUserListCount(2);
|
||||
$this->assertSwitchUserListContainsUser($this->rootUser->getUsername());
|
||||
$this->assertSwitchUserListContainsUser($this->rootUser->getDisplayName());
|
||||
$this->assertSwitchUserListContainsUser($anonymous);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Menu\MenuLinkTreeInterface;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\Session\AccountProxyInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Toolbar integration handler.
|
||||
*/
|
||||
class ToolbarHandler implements ContainerInjectionInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The menu link tree service.
|
||||
*
|
||||
* @var \Drupal\Core\Menu\MenuLinkTreeInterface
|
||||
*/
|
||||
protected $menuLinkTree;
|
||||
|
||||
/**
|
||||
* The devel toolbar config.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ImmutableConfig
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountProxyInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* ToolbarHandler constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_link_tree
|
||||
* The menu link tree service.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Drupal\Core\Session\AccountProxyInterface $account
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(MenuLinkTreeInterface $menu_link_tree, ConfigFactoryInterface $config_factory, AccountProxyInterface $account) {
|
||||
$this->menuLinkTree = $menu_link_tree;
|
||||
$this->config = $config_factory->get('devel.toolbar.settings');
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('toolbar.menu_tree'),
|
||||
$container->get('config.factory'),
|
||||
$container->get('current_user')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook bridge.
|
||||
*
|
||||
* @return array
|
||||
* The devel toolbar items render array.
|
||||
*
|
||||
* @see hook_toolbar()
|
||||
*/
|
||||
public function toolbar() {
|
||||
$items['devel'] = [
|
||||
'#cache' => [
|
||||
'contexts' => ['user.permissions'],
|
||||
],
|
||||
];
|
||||
|
||||
if ($this->account->hasPermission('access devel information')) {
|
||||
$items['devel'] += [
|
||||
'#type' => 'toolbar_item',
|
||||
'#weight' => 999,
|
||||
'tab' => [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Devel'),
|
||||
'#url' => Url::fromRoute('devel.admin_settings'),
|
||||
'#attributes' => [
|
||||
'title' => $this->t('Development menu'),
|
||||
'class' => ['toolbar-icon', 'toolbar-icon-devel'],
|
||||
],
|
||||
],
|
||||
'tray' => [
|
||||
'#heading' => $this->t('Development menu'),
|
||||
'devel_menu' => [
|
||||
// Currently devel menu is uncacheable, so instead of poisoning the
|
||||
// entire page cache we use a lazy builder.
|
||||
// @see \Drupal\devel\Plugin\Menu\DestinationMenuLink
|
||||
// @see \Drupal\devel\Plugin\Menu\RouteDetailMenuItem
|
||||
'#lazy_builder' => [ToolbarHandler::class . ':lazyBuilder', []],
|
||||
// Force the creation of the placeholder instead of rely on the
|
||||
// automatical placeholdering or otherwise the page results
|
||||
// uncacheable when max-age 0 is bubbled up.
|
||||
'#create_placeholder' => TRUE,
|
||||
],
|
||||
'configuration' => [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Configure'),
|
||||
'#url' => Url::fromRoute('devel.toolbar.settings_form'),
|
||||
'#options' => [
|
||||
'attributes' => ['class' => ['edit-devel-toolbar']],
|
||||
],
|
||||
],
|
||||
],
|
||||
'#attached' => [
|
||||
'library' => 'devel/devel-toolbar',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy builder callback for the devel menu toolbar.
|
||||
*
|
||||
* @return array
|
||||
* The renderable array rapresentation of the devel menu.
|
||||
*/
|
||||
public function lazyBuilder() {
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->onlyEnabledLinks()->setTopLevelOnly();
|
||||
|
||||
$tree = $this->menuLinkTree->load('devel', $parameters);
|
||||
|
||||
$manipulators = [
|
||||
['callable' => 'menu.default_tree_manipulators:checkAccess'],
|
||||
['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
|
||||
['callable' => ToolbarHandler::class . ':processTree'],
|
||||
];
|
||||
$tree = $this->menuLinkTree->transform($tree, $manipulators);
|
||||
|
||||
$build = $this->menuLinkTree->build($tree);
|
||||
|
||||
CacheableMetadata::createFromRenderArray($build)
|
||||
->addCacheableDependency($this->config)
|
||||
->applyTo($build);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds toolbar-specific attributes to the menu link tree.
|
||||
*
|
||||
* @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
|
||||
* The menu link tree to manipulate.
|
||||
*
|
||||
* @return \Drupal\Core\Menu\MenuLinkTreeElement[]
|
||||
* The manipulated menu link tree.
|
||||
*/
|
||||
public function processTree(array $tree) {
|
||||
$visible_items = $this->config->get('toolbar_items') ?: [];
|
||||
|
||||
foreach ($tree as $element) {
|
||||
$plugin_id = $element->link->getPluginId();
|
||||
if (!in_array($plugin_id, $visible_items)) {
|
||||
// Add a class that allow to hide the non prioritized menu items when
|
||||
// the toolbar has horizontal orientation.
|
||||
$element->options['attributes']['class'][] = 'toolbar-horizontal-item-hidden';
|
||||
}
|
||||
}
|
||||
|
||||
return $tree;
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-09-03
|
||||
version: '8.x-1.0-alpha1+23-dev'
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1472897643
|
||||
datestamp: 1502732047
|
||||
|
||||
+3
-3
@@ -9,8 +9,8 @@ dependencies:
|
||||
- text
|
||||
- entity_test
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-09-03
|
||||
version: '8.x-1.0-alpha1+23-dev'
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1472897643
|
||||
datestamp: 1502732047
|
||||
|
||||
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-09-03
|
||||
version: '8.x-1.0-alpha1+23-dev'
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1472897643
|
||||
datestamp: 1502732047
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Helper module for devel test.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_mail().
|
||||
*/
|
||||
function devel_test_mail($key, &$message, $params) {
|
||||
switch ($key) {
|
||||
case 'devel_mail_log':
|
||||
$message['subject'] = $params['subject'];
|
||||
$message['body'][] = $params['body'];
|
||||
$message['headers']['From'] = $params['headers']['from'];
|
||||
$message['headers'] += $params['headers']['additional'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
services:
|
||||
|
||||
devel_test.test_route_subscriber:
|
||||
class: Drupal\devel_test\Routing\TestRouteSubscriber
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel_test\Routing;
|
||||
|
||||
use Drupal\Core\Routing\RouteSubscriberBase;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Router subscriber class for testing purpose.
|
||||
*/
|
||||
class TestRouteSubscriber extends RouteSubscriberBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterRoutes(RouteCollection $collection) {
|
||||
\Drupal::state()->set('devel_test_route_rebuild','Router rebuild fired');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests container info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelContainerInfoTest extends BrowserTestBase {
|
||||
|
||||
use DevelWebAssertHelper;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel', 'devel_test', 'block'];
|
||||
|
||||
/**
|
||||
* The user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->develUser = $this->drupalCreateUser(['access devel information']);
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests container info menu link.
|
||||
*/
|
||||
public function testContainerInfoMenuLink() {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
// Ensures that the events info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Container Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/container/service');
|
||||
$this->assertSession()->pageTextContains('Container services');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests service list page.
|
||||
*/
|
||||
public function testServiceList() {
|
||||
$this->drupalGet('/devel/container/service');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Container services');
|
||||
$this->assertContainerInfoLocalTasks();
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the services table is found.
|
||||
$table = $page->find('css', 'table.devel-service-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
/** @var $headers \Behat\Mink\Element\NodeElement[] */
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(4, count($headers));
|
||||
|
||||
$expected_headers = ['ID', 'Class', 'Alias', 'Operations'];
|
||||
$actual_headers = array_map(function ($element) {
|
||||
return $element->getText();
|
||||
}, $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Ensures that all the serivices are listed in the table.
|
||||
$cached_definition = \Drupal::service('kernel')->getCachedContainerDefinition();
|
||||
$this->assertNotNull($cached_definition);
|
||||
$rows = $table->findAll('css', 'tbody tr');
|
||||
$this->assertEquals(count($cached_definition['services']), count($rows));
|
||||
|
||||
// Tests the presence of some (arbitrarily chosen) services in the table.
|
||||
$expected_services = [
|
||||
'config.factory' => [
|
||||
'class' => 'Drupal\Core\Config\ConfigFactory',
|
||||
'alias' => '',
|
||||
],
|
||||
'devel.route_subscriber' => [
|
||||
'class' => 'Drupal\devel\Routing\RouteSubscriber',
|
||||
'alias' => '',
|
||||
],
|
||||
'plugin.manager.element_info' => [
|
||||
'class' => 'Drupal\Core\Render\ElementInfoManager',
|
||||
'alias' => 'element_info',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($expected_services as $service_id => $expected) {
|
||||
$row = $table->find('css', sprintf('tbody tr:contains("%s")', $service_id));
|
||||
$this->assertNotNull($row);
|
||||
|
||||
/** @var $cells \Behat\Mink\Element\NodeElement[] */
|
||||
$cells = $row->findAll('css', 'td');
|
||||
$this->assertEquals(4, count($cells));
|
||||
|
||||
$cell_service_id = $cells[0];
|
||||
$this->assertEquals($service_id, $cell_service_id->getText());
|
||||
$this->assertTrue($cell_service_id->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_class = $cells[1];
|
||||
$this->assertEquals($expected['class'], $cell_class->getText());
|
||||
$this->assertTrue($cell_class->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_alias = $cells[2];
|
||||
$this->assertEquals($expected['alias'], $cell_alias->getText());
|
||||
$this->assertTrue($cell_class->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_operations = $cells[3];
|
||||
$actual_href = $cell_operations->findLink('Devel')->getAttribute('href');
|
||||
$expected_href = Url::fromRoute('devel.container_info.service.detail', ['service_id' => $service_id])->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/container/service');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests service detail page.
|
||||
*/
|
||||
public function testServiceDetail() {
|
||||
$service_id = 'devel.dumper';
|
||||
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet("/devel/container/service/$service_id");
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains("Service $service_id detail");
|
||||
|
||||
// Ensures that the page returns a 404 error if the requested service is
|
||||
// not defined.
|
||||
$this->drupalGet('/devel/container/service/not.exists');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet("devel/container/service/$service_id");
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests parameter list page.
|
||||
*/
|
||||
public function testParameterList() {
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet('/devel/container/parameter');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Container parameters');
|
||||
$this->assertContainerInfoLocalTasks();
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the parameters table is found.
|
||||
$table = $page->find('css', 'table.devel-parameter-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
/** @var $headers \Behat\Mink\Element\NodeElement[] */
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(2, count($headers));
|
||||
|
||||
$expected_headers = ['Name', 'Operations'];
|
||||
$actual_headers = array_map(function ($element) {
|
||||
return $element->getText();
|
||||
}, $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Ensures that all the parameters are listed in the table.
|
||||
$cached_definition = \Drupal::service('kernel')->getCachedContainerDefinition();
|
||||
$this->assertNotNull($cached_definition);
|
||||
$rows = $table->findAll('css', 'tbody tr');
|
||||
$this->assertEquals(count($cached_definition['parameters']), count($rows));
|
||||
|
||||
// Tests the presence of some parameters in the table.
|
||||
$expected_parameters = [
|
||||
'container.modules',
|
||||
'cache_bins',
|
||||
'factory.keyvalue',
|
||||
'twig.config',
|
||||
];
|
||||
|
||||
foreach ($expected_parameters as $parameter_name) {
|
||||
$row = $table->find('css', sprintf('tbody tr:contains("%s")', $parameter_name));
|
||||
$this->assertNotNull($row);
|
||||
|
||||
/** @var $cells \Behat\Mink\Element\NodeElement[] */
|
||||
$cells = $row->findAll('css', 'td');
|
||||
$this->assertEquals(2, count($cells));
|
||||
|
||||
$cell_parameter_name = $cells[0];
|
||||
$this->assertEquals($parameter_name, $cell_parameter_name->getText());
|
||||
$this->assertTrue($cell_parameter_name->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_operations = $cells[1];
|
||||
$actual_href = $cell_operations->findLink('Devel')->getAttribute('href');
|
||||
$expected_href = Url::fromRoute('devel.container_info.parameter.detail', ['parameter_name' => $parameter_name])->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/container/service');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests parameter detail page.
|
||||
*/
|
||||
public function testParameterDetail() {
|
||||
$parameter_name = 'cache_bins';
|
||||
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet("/devel/container/parameter/$parameter_name");
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains("Parameter $parameter_name value");
|
||||
|
||||
// Ensures that the page returns a 404 error if the requested parameter is
|
||||
// not defined.
|
||||
$this->drupalGet('/devel/container/parameter/not_exists');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet("devel/container/service/$parameter_name");
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that container info local tasks are present.
|
||||
*/
|
||||
protected function assertContainerInfoLocalTasks() {
|
||||
$expected_local_tasks = [
|
||||
['devel.container_info.service', []],
|
||||
['devel.container_info.parameter', []],
|
||||
];
|
||||
|
||||
$this->assertLocalTasks($expected_local_tasks);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Behat\Mink\Element\NodeElement;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests element info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelElementInfoTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel', 'block'];
|
||||
|
||||
/**
|
||||
* The user for the test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->develUser = $this->drupalCreateUser(['access devel information']);
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests element info menu link.
|
||||
*/
|
||||
public function testElementInfoMenuLink() {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
// Ensures that the element info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Element Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/elements');
|
||||
$this->assertSession()->pageTextContains('Element Info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests element list page.
|
||||
*/
|
||||
public function testElementList() {
|
||||
$this->drupalGet('/devel/elements');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Element Info');
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the element list table is found.
|
||||
$table = $page->find('css', 'table.devel-element-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(4, count($headers));
|
||||
|
||||
$expected_headers = ['Name', 'Provider', 'Class', 'Operations'];
|
||||
$actual_headers = array_map(function (NodeElement $element) {
|
||||
return $element->getText();
|
||||
}, $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Tests the presence of some (arbitrarily chosen) elements in the table.
|
||||
$expected_elements = [
|
||||
'button' => [
|
||||
'class' => 'Drupal\Core\Render\Element\Button',
|
||||
'provider' => 'core',
|
||||
],
|
||||
'form' => [
|
||||
'class' => 'Drupal\Core\Render\Element\Form',
|
||||
'provider' => 'core',
|
||||
],
|
||||
'html' => [
|
||||
'class' => 'Drupal\Core\Render\Element\Html',
|
||||
'provider' => 'core',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($expected_elements as $element_name => $element) {
|
||||
$row = $table->find('css', sprintf('tbody tr:contains("%s")', $element_name));
|
||||
$this->assertNotNull($row);
|
||||
|
||||
/** @var $cells \Behat\Mink\Element\NodeElement[] */
|
||||
$cells = $row->findAll('css', 'td');
|
||||
$this->assertEquals(4, count($cells));
|
||||
|
||||
$cell = $cells[0];
|
||||
$this->assertEquals($element_name, $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[1];
|
||||
$this->assertEquals($element['provider'], $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[2];
|
||||
$this->assertEquals($element['class'], $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[3];
|
||||
$actual_href = $cell->findLink('Devel')->getAttribute('href');
|
||||
$expected_href = Url::fromRoute('devel.elements_page.detail', ['element_name' => $element_name])->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible only to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/elements');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests element detail page.
|
||||
*/
|
||||
public function testElementDetail() {
|
||||
$element_name = 'button';
|
||||
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet("/devel/elements/$element_name");
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains("Element $element_name");
|
||||
|
||||
// Ensures that the page returns a 404 error if the requested element is
|
||||
// not defined.
|
||||
$this->drupalGet('/devel/elements/not_exists');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet("/devel/elements/$element_name");
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Behat\Mink\Element\NodeElement;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests entity type info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelEntityTypeInfoTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel', 'block'];
|
||||
|
||||
/**
|
||||
* The user for the test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->develUser = $this->drupalCreateUser(['access devel information']);
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity info menu link.
|
||||
*/
|
||||
public function testEntityInfoMenuLink() {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
// Ensures that the entity type info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Entity Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/entity/info');
|
||||
$this->assertSession()->pageTextContains('Entity Info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity type list page.
|
||||
*/
|
||||
public function testEntityTypeList() {
|
||||
$this->drupalGet('/devel/entity/info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Entity Info');
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the entity type list table is found.
|
||||
$table = $page->find('css', 'table.devel-entity-type-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(5, count($headers));
|
||||
|
||||
$expected_headers = ['ID', 'Name', 'Provider', 'Class', 'Operations'];
|
||||
$actual_headers = array_map(function (NodeElement $element) {
|
||||
return $element->getText();
|
||||
}, $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Tests the presence of some (arbitrarily chosen) entity types in the table.
|
||||
$expected_types = [
|
||||
'date_format' => [
|
||||
'name' => 'Date format',
|
||||
'class' => 'Drupal\Core\Datetime\Entity\DateFormat',
|
||||
'provider' => 'core',
|
||||
],
|
||||
'block' => [
|
||||
'name' => 'Block',
|
||||
'class' => 'Drupal\block\Entity\Block',
|
||||
'provider' => 'block',
|
||||
],
|
||||
'entity_view_mode' => [
|
||||
'name' => 'View mode',
|
||||
'class' => 'Drupal\Core\Entity\Entity\EntityViewMode',
|
||||
'provider' => 'core',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($expected_types as $entity_type_id => $entity_type) {
|
||||
$row = $table->find('css', sprintf('tbody tr:contains("%s")', $entity_type_id));
|
||||
$this->assertNotNull($row);
|
||||
|
||||
/** @var $cells \Behat\Mink\Element\NodeElement[] */
|
||||
$cells = $row->findAll('css', 'td');
|
||||
$this->assertEquals(5, count($cells));
|
||||
|
||||
$cell = $cells[0];
|
||||
$this->assertEquals($entity_type_id, $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[1];
|
||||
$this->assertEquals($entity_type['name'], $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[2];
|
||||
$this->assertEquals($entity_type['provider'], $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[3];
|
||||
$this->assertEquals($entity_type['class'], $cell->getText());
|
||||
$this->assertTrue($cell->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell = $cells[4];
|
||||
$actual_href = $cell->findLink('Devel')->getAttribute('href');
|
||||
$expected_href = Url::fromRoute('devel.entity_info_page.detail', ['entity_type_id' => $entity_type_id])->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible only to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/entity/info');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity type detail page.
|
||||
*/
|
||||
public function testEntityTypeDetail() {
|
||||
$entity_type_id = 'date_format';
|
||||
|
||||
// Ensures that the page works as expected.
|
||||
$this->drupalGet("/devel/entity/info/$entity_type_id");
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains("Entity type $entity_type_id");
|
||||
|
||||
// Ensures that the page returns a 404 error if the requested entity type is
|
||||
// not defined.
|
||||
$this->drupalGet('/devel/entity/info/not_exists');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet("/devel/entity/info/$entity_type_id");
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests devel error handler.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelErrorHandlerTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel'];
|
||||
|
||||
/**
|
||||
* Tests devel error handler.
|
||||
*/
|
||||
public function testErrorHandler() {
|
||||
$messages_selector = 'div.messages--warning';
|
||||
|
||||
$expected_notice = new FormattableMarkup('%type: @message in %function (line ', [
|
||||
'%type' => 'Notice',
|
||||
'@message' => 'Undefined variable: undefined',
|
||||
'%function' => 'Drupal\devel\Form\SettingsForm->demonstrateErrorHandlers()',
|
||||
]);
|
||||
|
||||
$expected_warning = new FormattableMarkup('%type: @message in %function (line ', [
|
||||
'%type' => 'Warning',
|
||||
'@message' => 'Division by zero',
|
||||
'%function' => 'Drupal\devel\Form\SettingsForm->demonstrateErrorHandlers()',
|
||||
]);
|
||||
|
||||
$config = $this->config('system.logging');
|
||||
$config->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save();
|
||||
|
||||
$admin_user = $this->drupalCreateUser(['administer site configuration', 'access devel information']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Ensures that the error handler config is present on the config page and
|
||||
// by default the standard error handler is selected.
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEquals($error_handlers, [DEVEL_ERROR_HANDLER_STANDARD => DEVEL_ERROR_HANDLER_STANDARD]);
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_STANDARD);
|
||||
|
||||
// Ensures that selecting the DEVEL_ERROR_HANDLER_NONE option no error
|
||||
// (raw or message) is shown on the site in case of php errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => DEVEL_ERROR_HANDLER_NONE,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEquals($error_handlers, [DEVEL_ERROR_HANDLER_NONE => DEVEL_ERROR_HANDLER_NONE]);
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_NONE);
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->responseNotContains($expected_notice);
|
||||
$this->assertSession()->responseNotContains($expected_warning);
|
||||
$this->assertSession()->elementNotExists('css', $messages_selector);
|
||||
|
||||
// Ensures that selecting the DEVEL_ERROR_HANDLER_BACKTRACE_KINT option a
|
||||
// backtrace above the rendered page is shown on the site in case of php
|
||||
// errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => DEVEL_ERROR_HANDLER_BACKTRACE_KINT,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEquals($error_handlers, [DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT]);
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_KINT);
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->elementNotExists('css', $messages_selector);
|
||||
|
||||
// Ensures that selecting the DEVEL_ERROR_HANDLER_BACKTRACE_DPM option a
|
||||
// backtrace in the message area is shown on the site in case of php errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => DEVEL_ERROR_HANDLER_BACKTRACE_DPM,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEquals($error_handlers, [DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM]);
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_DPM);
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->responseContains($expected_notice);
|
||||
$this->assertSession()->responseContains($expected_warning);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_notice);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_warning);
|
||||
|
||||
// Ensures that when multiple handlers are selected, the output produced by
|
||||
// every handler is shown on the site in case of php errors.
|
||||
$edit = [
|
||||
'error_handlers[]' => [
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT,
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM,
|
||||
],
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$error_handlers = \Drupal::config('devel.settings')->get('error_handlers');
|
||||
$this->assertEquals($error_handlers, [
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT,
|
||||
DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM,
|
||||
]);
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_KINT);
|
||||
$this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_DPM);
|
||||
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->responseContains($expected_notice);
|
||||
$this->assertSession()->responseContains($expected_warning);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_notice);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_warning);
|
||||
|
||||
// Ensures that setting the error reporting to all the output produced by
|
||||
// handlers is shown on the site in case of php errors.
|
||||
$config->set('error_level', ERROR_REPORTING_DISPLAY_ALL)->save();
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->responseContains($expected_notice);
|
||||
$this->assertSession()->responseContains($expected_warning);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_notice);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_warning);
|
||||
|
||||
// Ensures that setting the error reporting to some the output produced by
|
||||
// handlers is shown on the site in case of php errors.
|
||||
$config->set('error_level', ERROR_REPORTING_DISPLAY_SOME)->save();
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->responseContains($expected_notice);
|
||||
$this->assertSession()->responseContains($expected_warning);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_notice);
|
||||
$this->assertSession()->elementContains('css', $messages_selector, $expected_warning);
|
||||
|
||||
// Ensures that setting the error reporting to none the output produced by
|
||||
// handlers is not shown on the site in case of php errors.
|
||||
$config->set('error_level', ERROR_REPORTING_HIDE)->save();
|
||||
$this->clickLink('notice+warning');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->responseNotContains($expected_notice);
|
||||
$this->assertSession()->responseNotContains($expected_warning);
|
||||
$this->assertSession()->elementNotExists('css', $messages_selector);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests event info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelEventInfoTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel', 'block'];
|
||||
|
||||
/**
|
||||
* The user for the test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->develUser = $this->drupalCreateUser(['access devel information']);
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests event info menu link.
|
||||
*/
|
||||
public function testEventsInfoMenuLink() {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
// Ensures that the events info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Events Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/events');
|
||||
$this->assertSession()->pageTextContains('Events');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests event info page.
|
||||
*/
|
||||
public function testEventList() {
|
||||
$event_dispatcher = \Drupal::service('event_dispatcher');
|
||||
|
||||
$this->drupalGet('/devel/events');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Events');
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the event table is found.
|
||||
$table = $page->find('css', 'table.devel-event-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
/** @var $headers \Behat\Mink\Element\NodeElement[] */
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(3, count($headers));
|
||||
|
||||
$expected_headers = ['Event Name', 'Callable', 'Priority'];
|
||||
$actual_headers = array_map(function ($element) {
|
||||
return $element->getText();
|
||||
}, $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Ensures that all the events are listed in the table.
|
||||
$events = $event_dispatcher->getListeners();
|
||||
$event_header_row = $table->findAll('css', 'tbody tr th.devel-event-name-header');
|
||||
$this->assertEquals(count($events), count($event_header_row));
|
||||
|
||||
// Tests the presence of some (arbitrarily chosen) events and related
|
||||
// listeners in the table. The event items are tested dynamically so no
|
||||
// test failures are expected if listeners change.
|
||||
$expected_events = [
|
||||
'config.delete',
|
||||
'kernel.request',
|
||||
'routing.route_alter',
|
||||
];
|
||||
|
||||
foreach ($expected_events as $event_name) {
|
||||
$listeners = $event_dispatcher->getListeners($event_name);
|
||||
|
||||
// Ensures that the event header is present in the table.
|
||||
$event_header_row = $table->findAll('css', sprintf('tbody tr th:contains("%s")', $event_name));
|
||||
$this->assertNotNull($event_header_row);
|
||||
$this->assertEquals(1, count($event_header_row));
|
||||
|
||||
// Ensures that all the event listener are listed in the table.
|
||||
/** @var $event_rows \Behat\Mink\Element\NodeElement[] */
|
||||
$event_rows = $table->findAll('css', sprintf('tbody tr:contains("%s")', $event_name));
|
||||
// Remove the header row.
|
||||
array_shift($event_rows);
|
||||
$this->assertEquals(count($listeners), count($event_rows));
|
||||
|
||||
foreach ($listeners as $index => $listener) {
|
||||
/** @var $cells \Behat\Mink\Element\NodeElement[] */
|
||||
$cells = $event_rows[$index]->findAll('css', 'td');
|
||||
$this->assertEquals(3, count($cells));
|
||||
|
||||
$cell_event_name = $cells[0];
|
||||
$this->assertEquals($event_name, $cell_event_name->getText());
|
||||
$this->assertTrue($cell_event_name->hasClass('table-filter-text-source'));
|
||||
$this->assertTrue($cell_event_name->hasClass('visually-hidden'));
|
||||
|
||||
$cell_callable = $cells[1];
|
||||
is_callable($listener, TRUE, $callable_name);
|
||||
$this->assertEquals($callable_name, $cell_callable->getText());
|
||||
|
||||
$cell_methods = $cells[2];
|
||||
$this->assertEquals($index, $cell_methods->getText());
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible only to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/events');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests layout info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelLayoutInfoTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel', 'block', 'layout_discovery'];
|
||||
|
||||
/**
|
||||
* The user for the test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
// TODO find a cleaner way to skip layout info tests when running tests on
|
||||
// Drupal branch < 8.3.x.
|
||||
if (version_compare(\Drupal::VERSION, '8.3', '<')) {
|
||||
$this->markTestSkipped('Devel Layout Info Tests only available on version 8.3.x+.');
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->develUser = $this->drupalCreateUser(['access devel information']);
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests layout info menu link.
|
||||
*/
|
||||
public function testLayoutsInfoMenuLink() {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
// Ensures that the layout info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Layouts Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/layouts');
|
||||
$this->assertSession()->pageTextContains('Layout');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests layout info page.
|
||||
*/
|
||||
public function testLayoutList() {
|
||||
$this->drupalGet('/devel/layouts');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Layouts');
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the layout table is found.
|
||||
$table = $page->find('css', 'table.devel-layout-list');
|
||||
$this->assertNotNull($table);
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
/** @var $headers \Behat\Mink\Element\NodeElement[] */
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(6, count($headers));
|
||||
|
||||
$expected_headers = ['Icon', 'Label', 'Description', 'Category', 'Regions', 'Provider'];
|
||||
$actual_headers = array_map(function ($element) {
|
||||
return $element->getText();
|
||||
}, $headers);
|
||||
$this->assertSame($expected_headers, $actual_headers);
|
||||
|
||||
// Ensures that all the layouts are listed in the table.
|
||||
$layout_manager = \Drupal::service('plugin.manager.core.layout');
|
||||
$layouts = $layout_manager->getDefinitions();
|
||||
$table_rows = $table->findAll('css', 'tbody tr');
|
||||
$this->assertEquals(count($layouts), count($table_rows));
|
||||
|
||||
$index = 0;
|
||||
foreach ($layouts as $layout) {
|
||||
$cells = $table_rows[$index]->findAll('css', 'td');
|
||||
$this->assertEquals(6, count($cells));
|
||||
|
||||
$cell_layout_icon = $cells[0];
|
||||
if (empty($layout->getIconPath())) {
|
||||
// @todo test that the icon path image is set correctly
|
||||
}
|
||||
else {
|
||||
$this->assertNull($cell_layout_icon->getText());
|
||||
}
|
||||
|
||||
$cell_layout_label = $cells[1];
|
||||
$this->assertEquals($cell_layout_label->getText(), $layout->getLabel());
|
||||
|
||||
$cell_layout_description = $cells[2];
|
||||
$this->assertEquals($cell_layout_description->getText(), $layout->getDescription());
|
||||
|
||||
$cell_layout_category = $cells[3];
|
||||
$this->assertEquals($cell_layout_category->getText(), $layout->getCategory());
|
||||
|
||||
$cell_layout_regions = $cells[4];
|
||||
$this->assertEquals($cell_layout_regions->getText(), implode(', ', $layout->getRegionLabels()));
|
||||
|
||||
$cell_layout_provider = $cells[5];
|
||||
$this->assertEquals($cell_layout_provider->getText(), $layout->getProvider());
|
||||
|
||||
$index++;
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible only to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/layouts');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the dependency with layout_discovery module.
|
||||
*/
|
||||
public function testLayoutDiscoveryDependency() {
|
||||
$this->container->get('module_installer')->uninstall(['layout_discovery']);
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
|
||||
// Ensures that the layout info link is not present on the devel menu.
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->linkNotExists('Layouts Info');
|
||||
|
||||
// Ensures that the layouts info page is not available.
|
||||
$this->drupalGet('/devel/layouts');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Check a few other devel pages to verify devel module stil works.
|
||||
$this->drupalGet('/devel/events');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->drupalGet('devel/routes');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->drupalGet('/devel/container/service');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests devel requirements.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelRequirementsTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel'];
|
||||
|
||||
/**
|
||||
* Tests that the status page shows a warning when evel is enabled.
|
||||
*/
|
||||
public function testStatusPage() {
|
||||
$admin_user = $this->drupalCreateUser(['administer site configuration']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
|
||||
$this->assertSession()->pageTextContains('Devel module enabled');
|
||||
$this->assertSession()->pageTextContains('The Devel module provides access to internal debugging information; therefore it\'s recommended to disable this module on sites in production.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests routes info pages and links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelRouteInfoTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel', 'devel_test', 'block'];
|
||||
|
||||
/**
|
||||
* The user for the test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->develUser = $this->drupalCreateUser(['access devel information']);
|
||||
$this->drupalLogin($this->develUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests routes info.
|
||||
*/
|
||||
public function testRouteList() {
|
||||
// Ensures that the routes info link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('Routes Info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/routes');
|
||||
$this->assertSession()->pageTextContains('Routes');
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Ensures that the expected table headers are found.
|
||||
/** @var $headers \Behat\Mink\Element\NodeElement[] */
|
||||
$headers = $page->findAll('css', 'table.devel-route-list thead th');
|
||||
$this->assertEquals(4, count($headers));
|
||||
|
||||
$expected_items = ['Route Name', 'Path', 'Allowed Methods', 'Operations'];
|
||||
foreach ($headers as $key => $element) {
|
||||
$this->assertSame($element->getText(), $expected_items[$key]);
|
||||
}
|
||||
|
||||
// Ensures that all the routes are listed in the table.
|
||||
$routes = \Drupal::service('router.route_provider')->getAllRoutes();
|
||||
$rows = $page->findAll('css', 'table.devel-route-list tbody tr');
|
||||
$this->assertEquals(count($routes), count($rows));
|
||||
|
||||
// Tests the presence of some (arbitrarily chosen) routes in the table.
|
||||
$expected_routes = [
|
||||
'<current>' => [
|
||||
'path' => '/<current>',
|
||||
'methods' => ['GET', 'POST'],
|
||||
'dynamic' => FALSE,
|
||||
],
|
||||
'user.login' => [
|
||||
'path' => '/user/login',
|
||||
'methods' => ['GET', 'POST'],
|
||||
'dynamic' => FALSE,
|
||||
],
|
||||
'entity.user.canonical' => [
|
||||
'path' => '/user/{user}',
|
||||
'methods' => ['GET', 'POST'],
|
||||
'dynamic' => TRUE,
|
||||
],
|
||||
'entity.user.devel_load' => [
|
||||
'path' => '/devel/user/{user}',
|
||||
'methods' => ['ANY'],
|
||||
'dynamic' => TRUE,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($expected_routes as $route_name => $expected) {
|
||||
$row = $page->find('css', sprintf('table.devel-route-list tbody tr:contains("%s")', $route_name));
|
||||
$this->assertNotNull($row);
|
||||
|
||||
/** @var $cells \Behat\Mink\Element\NodeElement[] */
|
||||
$cells = $row->findAll('css', 'td');
|
||||
$this->assertEquals(4, count($cells));
|
||||
|
||||
$cell_route_name = $cells[0];
|
||||
$this->assertEquals($route_name, $cell_route_name->getText());
|
||||
$this->assertTrue($cell_route_name->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_path = $cells[1];
|
||||
$this->assertEquals($expected['path'], $cell_path->getText());
|
||||
$this->assertTrue($cell_path->hasClass('table-filter-text-source'));
|
||||
|
||||
$cell_methods = $cells[2];
|
||||
$this->assertEquals(implode('', $expected['methods']), $cell_methods->getText());
|
||||
|
||||
$cell_operations = $cells[3];
|
||||
$actual_href = $cell_operations->findLink('Devel')->getAttribute('href');
|
||||
if ($expected['dynamic']) {
|
||||
$parameters = ['query' => ['route_name' => $route_name]];
|
||||
}
|
||||
else {
|
||||
$parameters = ['query' => ['path' => $expected['path']]];
|
||||
}
|
||||
$expected_href = Url::fromRoute('devel.route_info.item', [], $parameters)->toString();
|
||||
$this->assertEquals($expected_href, $actual_href);
|
||||
}
|
||||
|
||||
// Ensures that the page is accessible only to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/routes');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests route detail page.
|
||||
*/
|
||||
public function testRouteDetail() {
|
||||
$expected_title = 'Route detail';
|
||||
$xpath_warning_messages = '//div[contains(@class, "messages--warning")]';
|
||||
|
||||
// Ensures that devel route detail link in the menu works properly.
|
||||
$url = $this->develUser->toUrl();
|
||||
$path = '/' . $url->getInternalPath();
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->clickLink('Current route info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
$expected_url = Url::fromRoute('devel.route_info.item', [], ['query' => ['path' => $path]]);
|
||||
$this->assertSession()->addressEquals($expected_url);
|
||||
$this->assertSession()->elementNotExists('xpath', $xpath_warning_messages);
|
||||
|
||||
// Ensures that devel route detail works properly even when dynamic cache
|
||||
// is enabled.
|
||||
$url = Url::fromRoute('devel.simple_page');
|
||||
$path = '/' . $url->getInternalPath();
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->clickLink('Current route info');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
$expected_url = Url::fromRoute('devel.route_info.item', [], ['query' => ['path' => $path]]);
|
||||
$this->assertSession()->addressEquals($expected_url);
|
||||
$this->assertSession()->elementNotExists('xpath', $xpath_warning_messages);
|
||||
|
||||
// Ensures that if a non existent path is passed as input, a warning
|
||||
// message is shown.
|
||||
$this->drupalGet('devel/routes/item', ['query' => ['path' => '/undefined']]);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
$this->assertSession()->elementExists('xpath', $xpath_warning_messages);
|
||||
|
||||
// Ensures that the route detail page works properly when a valid route
|
||||
// name input is passed.
|
||||
$this->drupalGet('devel/routes/item', ['query' => ['route_name' => 'devel.simple_page']]);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
$this->assertSession()->elementNotExists('xpath', $xpath_warning_messages);
|
||||
|
||||
// Ensures that if a non existent route name is passed as input a warning
|
||||
// message is shown.
|
||||
$this->drupalGet('devel/routes/item', ['query' => ['route_name' => 'not.exists']]);
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
$this->assertSession()->elementExists('xpath', $xpath_warning_messages);
|
||||
|
||||
// Ensures that if no 'path' nor 'name' query string is passed as input,
|
||||
// devel route detail page does not return errors.
|
||||
$this->drupalGet('devel/routes/item');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains($expected_title);
|
||||
|
||||
// Ensures that the page is accessible ony to the users with the adequate
|
||||
// permissions.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('devel/routes/item');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests routes rebuild.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelRouterRebuildTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['devel', 'devel_test'];
|
||||
|
||||
/**
|
||||
* Test routes rebuild.
|
||||
*/
|
||||
public function testRouterRebuildConfirmForm() {
|
||||
// Reset the state flag.
|
||||
\Drupal::state()->set('devel_test_route_rebuild', NULL);
|
||||
|
||||
$this->drupalGet('devel/menu/reset');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
$web_user = $this->drupalCreateUser(['administer site configuration']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$this->drupalGet('devel/menu/reset');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('Are you sure you want to rebuild the router?');
|
||||
$route_rebuild_state = \Drupal::state()->get('devel_test_route_rebuild');
|
||||
$this->assertEmpty($route_rebuild_state);
|
||||
|
||||
$this->drupalPostForm('devel/menu/reset', [], t('Rebuild'));
|
||||
$this->assertSession()->pageTextContains('The router has been rebuilt.');
|
||||
$route_rebuild_state = \Drupal::state()->get('devel_test_route_rebuild');
|
||||
$this->assertEquals('Router rebuild fired', $route_rebuild_state);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Behat\Mink\Element\NodeElement;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests devel state editor.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelStateEditorTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel', 'block'];
|
||||
|
||||
/**
|
||||
* The state store.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* The user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->state = $this->container->get('state');
|
||||
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->develUser = $this->drupalCreateUser(['access devel information']);
|
||||
$this->adminUser = $this->drupalCreateUser(['access devel information', 'administer site configuration']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests state editor menu link.
|
||||
*/
|
||||
public function testStateEditMenuLink() {
|
||||
$this->drupalPlaceBlock('system_menu_block:devel');
|
||||
$this->drupalLogin($this->develUser);
|
||||
// Ensures that the state editor link is present on the devel menu and that
|
||||
// it points to the correct page.
|
||||
$this->drupalGet('');
|
||||
$this->clickLink('State editor');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->addressEquals('/devel/state');
|
||||
$this->assertSession()->pageTextContains('State editor');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests state listing.
|
||||
*/
|
||||
public function testStateListing() {
|
||||
$table_selector = 'table.devel-state-list';
|
||||
|
||||
// Ensure that state listing page is accessible only by users with the
|
||||
// adequate permissions.
|
||||
$this->drupalGet('devel/state');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->drupalGet('devel/state');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains('State editor');
|
||||
|
||||
// Ensure that the state variables table is visible.
|
||||
$table = $this->assertSession()->elementExists('css', $table_selector);
|
||||
|
||||
// Ensure that all state variables are listed in the table.
|
||||
$states = \Drupal::keyValue('state')->getAll();
|
||||
$rows = $table->findAll('css', 'tbody tr');
|
||||
$this->assertEquals(count($rows), count($states), 'All states are listed in the table.');
|
||||
|
||||
// Ensure that the added state variables are listed in the table.
|
||||
$this->state->set('devel.simple', 'Hello!');
|
||||
$this->drupalGet('devel/state');
|
||||
$table = $this->assertSession()->elementExists('css', $table_selector);
|
||||
$this->assertSession()->elementExists('css', sprintf('tbody td:contains("%s")', 'devel.simple'), $table);
|
||||
|
||||
// Ensure that the operations column and the actions buttons are not
|
||||
// available for user without 'administer site configuration' permission.
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(count($headers), 2, 'Correct number of table header cells found.');
|
||||
$this->assertElementsTextEquals($headers, ['Name', 'Value']);
|
||||
$this->assertSession()->elementNotExists('css', 'ul.dropbutton li a', $table);
|
||||
|
||||
// Ensure that the operations column and the actions buttons are
|
||||
// available for user with 'administer site configuration' permission.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('devel/state');
|
||||
|
||||
$table = $this->assertSession()->elementExists('css', $table_selector);
|
||||
$headers = $table->findAll('css', 'thead th');
|
||||
$this->assertEquals(count($headers), 3, 'Correct number of table header cells found.');
|
||||
$this->assertElementsTextEquals($headers, ['Name', 'Value', 'Operations']);
|
||||
$this->assertSession()->elementExists('css', 'ul.dropbutton li a', $table);
|
||||
|
||||
// Test that the edit button works properly.
|
||||
$this->clickLink('Edit');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests state edit.
|
||||
*/
|
||||
public function testStateEdit() {
|
||||
// Create some state variables for the test.
|
||||
$this->state->set('devel.simple', 0);
|
||||
$this->state->set('devel.array', ['devel' => 'value']);
|
||||
$this->state->set('devel.object', $this->randomObject());
|
||||
|
||||
// Ensure that state edit form is accessible only by users with the
|
||||
// adequate permissions.
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->drupalGet('devel/state/edit/devel.simple');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Ensure that accessing an un-existent state variable cause a warning
|
||||
// message.
|
||||
$this->drupalGet('devel/state/edit/devel.unknown');
|
||||
$this->assertSession()->pageTextContains(strtr('State @name does not exist in the system.', ['@name' => 'devel.unknown']));
|
||||
|
||||
// Ensure that state variables that contain simple type can be edited and
|
||||
// saved.
|
||||
$this->drupalGet('devel/state/edit/devel.simple');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains(strtr('Edit state variable: @name', ['@name' => 'devel.simple']));
|
||||
$input = $this->assertSession()->fieldExists('edit-new-value');
|
||||
$this->assertFalse($input->hasAttribute('disabled'));
|
||||
$button = $this->assertSession()->buttonExists('edit-submit');
|
||||
$this->assertFalse($button->hasAttribute('disabled'));
|
||||
|
||||
$edit = ['new_value' => 1];
|
||||
$this->drupalPostForm('devel/state/edit/devel.simple', $edit, 'Save');
|
||||
$this->assertSession()->pageTextContains(strtr('Variable @name was successfully edited.', ['@name' => 'devel.simple']));
|
||||
$this->assertEquals(1, $this->state->get('devel.simple'));
|
||||
|
||||
// Ensure that state variables that contain array can be edited and saved
|
||||
// and the new value is properly validated.
|
||||
$this->drupalGet('devel/state/edit/devel.array');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains(strtr('Edit state variable: @name', ['@name' => 'devel.array']));
|
||||
$input = $this->assertSession()->fieldExists('edit-new-value');
|
||||
$this->assertFalse($input->hasAttribute('disabled'));
|
||||
$button = $this->assertSession()->buttonExists('edit-submit');
|
||||
$this->assertFalse($button->hasAttribute('disabled'));
|
||||
|
||||
// Try to save an invalid yaml input.
|
||||
$edit = ['new_value' => 'devel: \'value updated'];
|
||||
$this->drupalPostForm('devel/state/edit/devel.array', $edit, 'Save');
|
||||
$this->assertSession()->pageTextContains('Invalid input:');
|
||||
|
||||
$edit = ['new_value' => 'devel: \'value updated\''];
|
||||
$this->drupalPostForm('devel/state/edit/devel.array', $edit, 'Save');
|
||||
$this->assertSession()->pageTextContains(strtr('Variable @name was successfully edited.', ['@name' => 'devel.array']));
|
||||
$this->assertEquals(['devel' => 'value updated'], $this->state->get('devel.array'));
|
||||
|
||||
// Ensure that state variables that contain objects cannot be edited.
|
||||
$this->drupalGet('devel/state/edit/devel.object');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->pageTextContains(strtr('Edit state variable: @name', ['@name' => 'devel.object']));
|
||||
$this->assertSession()->pageTextContains(strtr('Only simple structures are allowed to be edited. State @name contains objects.', ['@name' => 'devel.object']));
|
||||
$this->assertSession()->fieldDisabled('edit-new-value');
|
||||
$button = $this->assertSession()->buttonExists('edit-submit');
|
||||
$this->assertTrue($button->hasAttribute('disabled'));
|
||||
|
||||
// Ensure that the cancel link works as expected.
|
||||
$this->clickLink('Cancel');
|
||||
$this->assertSession()->addressEquals('devel/state');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the passed in elements have the expected text.
|
||||
*
|
||||
* @param \Behat\Mink\Element\NodeElement[] $elements
|
||||
* The elements for which check the text.
|
||||
* @param array $expected_elements_text
|
||||
* The expected text for the passed in elements.
|
||||
*/
|
||||
protected function assertElementsTextEquals(array $elements, array $expected_elements_text) {
|
||||
$actual_text = array_map(function (NodeElement $element) {
|
||||
return $element->getText();
|
||||
}, $elements);
|
||||
$this->assertSame($expected_elements_text, $actual_text);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests devel toolbar module functionality.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelToolbarTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel', 'toolbar', 'block'];
|
||||
|
||||
/**
|
||||
* The user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $toolbarUser;
|
||||
|
||||
/**
|
||||
* The user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* The dafault toolbar items.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultToolbarItems = [
|
||||
'devel.cache_clear',
|
||||
'devel.container_info.service',
|
||||
'devel.admin_settings_link',
|
||||
'devel.execute_php',
|
||||
'devel.menu_rebuild',
|
||||
'devel.reinstall',
|
||||
'devel.route_info',
|
||||
'devel.run_cron',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$this->develUser = $this->drupalCreateUser([
|
||||
'administer site configuration',
|
||||
'access devel information',
|
||||
'execute php code',
|
||||
'access toolbar',
|
||||
]);
|
||||
$this->toolbarUser = $this->drupalCreateUser([
|
||||
'access toolbar',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration form.
|
||||
*/
|
||||
public function testConfigurationForm() {
|
||||
// Ensures that the page is accessible ony to users with the adequate
|
||||
// permissions.
|
||||
$this->drupalGet('admin/config/development/devel/toolbar');
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
||||
// Ensures that the config page is accessible for users with the adequate
|
||||
// permissions and exists the Devel toolbar local task.
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->drupalGet('admin/config/development/devel/toolbar');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->elementExists('css', '.tabs .primary a:contains("Toolbar Settings")');
|
||||
$this->assertSession()->pageTextContains('Devel Toolbar Settings');
|
||||
|
||||
// Ensures and that all devel menu links are listed in the configuration
|
||||
// page.
|
||||
foreach ($this->getMenuLinkInfos() as $link) {
|
||||
$this->assertSession()->fieldExists(sprintf('toolbar_items[%s]', $link['id']));
|
||||
}
|
||||
|
||||
// Ensures and that the default configuration items are selected by
|
||||
// default.
|
||||
foreach ($this->defaultToolbarItems as $item) {
|
||||
$this->assertSession()->checkboxChecked(sprintf('toolbar_items[%s]', $item));
|
||||
}
|
||||
|
||||
// Ensures that the configuration save works as expected.
|
||||
$edit = [
|
||||
'toolbar_items[devel.event_info]' => 'devel.event_info',
|
||||
'toolbar_items[devel.theme_registry]' => 'devel.theme_registry',
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel/toolbar', $edit, t('Save configuration'));
|
||||
$this->assertSession()->pageTextContains('The configuration options have been saved.');
|
||||
|
||||
$expected_items = array_merge($this->defaultToolbarItems, ['devel.event_info', 'devel.theme_registry']);
|
||||
sort($expected_items);
|
||||
$config_items = \Drupal::config('devel.toolbar.settings')->get('toolbar_items');
|
||||
sort($config_items);
|
||||
|
||||
$this->assertEquals($expected_items, $config_items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests cache metadata headers.
|
||||
*/
|
||||
public function testCacheHeaders() {
|
||||
// Disable user toolbar tab so we can test properly if the devel toolbar
|
||||
// implementation interferes with the page cacheability.
|
||||
\Drupal::service('module_installer')->install(['toolbar_disable_user_toolbar']);
|
||||
|
||||
// The menu is not loaded for users without the adequate permission,
|
||||
// so no cache tags for configuration are added.
|
||||
$this->drupalLogin($this->toolbarUser);
|
||||
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'config:devel.toolbar.settings');
|
||||
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'config:system.menu.devel');
|
||||
|
||||
// Make sure that the configuration cache tags are present for users with
|
||||
// the adequate permission.
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:devel.toolbar.settings');
|
||||
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:system.menu.devel');
|
||||
|
||||
// The Devel toolbar implementation should not interfere with the page
|
||||
// cacheability, so you expect a MISS value in the X-Drupal-Dynamic-Cache
|
||||
// header the first time.
|
||||
$this->assertSession()->responseHeaderContains('X-Drupal-Dynamic-Cache', 'MISS');
|
||||
|
||||
// Triggers a page reload and verify that the page is served from the
|
||||
// cache.
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->responseHeaderContains('X-Drupal-Dynamic-Cache', 'HIT');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests toolbar integration.
|
||||
*/
|
||||
public function testToolbarIntegration() {
|
||||
$library_css_url = 'devel/css/devel.toolbar.css';
|
||||
$toolbar_selector = '#toolbar-bar .toolbar-tab';
|
||||
$toolbar_tab_selector = '#toolbar-bar .toolbar-tab a.toolbar-icon-devel';
|
||||
$toolbar_tray_selector = '#toolbar-bar .toolbar-tab #toolbar-item-devel-tray';
|
||||
|
||||
// Ensures that devel toolbar item is accessible only for user with the
|
||||
// adequate permissions.
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->responseNotContains($library_css_url);
|
||||
$this->assertSession()->elementNotExists('css', $toolbar_selector);
|
||||
$this->assertSession()->elementNotExists('css', $toolbar_tab_selector);
|
||||
|
||||
$this->drupalLogin($this->toolbarUser);
|
||||
$this->assertSession()->responseNotContains($library_css_url);
|
||||
$this->assertSession()->elementExists('css', $toolbar_selector);
|
||||
$this->assertSession()->elementNotExists('css', $toolbar_tab_selector);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
$this->assertSession()->responseContains($library_css_url);
|
||||
$this->assertSession()->elementExists('css', $toolbar_selector);
|
||||
$this->assertSession()->elementExists('css', $toolbar_tab_selector);
|
||||
$this->assertSession()->elementTextContains('css', $toolbar_tab_selector, 'Devel');
|
||||
|
||||
// Ensures that the configure link in the toolbar is present and point to
|
||||
// the correct page.
|
||||
$this->clickLink('Configure');
|
||||
$this->assertSession()->addressEquals('admin/config/development/devel/toolbar');
|
||||
|
||||
// Ensures that the toolbar tray contains the all the menu links. To the
|
||||
// links not marked as always visible will be assigned a css class that
|
||||
// allow to hide they when the toolbar has horizontal orientation.
|
||||
$this->drupalGet('');
|
||||
$toolbar_tray = $this->assertSession()->elementExists('css', $toolbar_tray_selector);
|
||||
|
||||
$devel_menu_items = $this->getMenuLinkInfos();
|
||||
$toolbar_items = $toolbar_tray->findAll('css', 'ul.toolbar-menu a');
|
||||
$this->assertCount(count($devel_menu_items), $toolbar_items);
|
||||
|
||||
foreach ($devel_menu_items as $link) {
|
||||
$item_selector = sprintf('ul.toolbar-menu a:contains("%s")', $link['title']);
|
||||
$item = $this->assertSession()->elementExists('css', $item_selector, $toolbar_tray);
|
||||
// TODO: find a more correct way to test link url.
|
||||
$this->assertContains(strtok($link['url'], '?'), $item->getAttribute('href'));
|
||||
|
||||
$not_visible = !in_array($link['id'], $this->defaultToolbarItems);
|
||||
$this->assertTrue($not_visible === $item->hasClass('toolbar-horizontal-item-hidden'));
|
||||
}
|
||||
|
||||
// Ensures that changing the toolbar settings configuration the changes are
|
||||
// immediately visible.
|
||||
$saved_items = $this->config('devel.toolbar.settings')->get('toolbar_items');
|
||||
$saved_items[] = 'devel.event_info';
|
||||
|
||||
$this->config('devel.toolbar.settings')
|
||||
->set('toolbar_items', $saved_items)
|
||||
->save();
|
||||
|
||||
$this->drupalGet('');
|
||||
$toolbar_tray = $this->assertSession()->elementExists('css', $toolbar_tray_selector);
|
||||
$item = $this->assertSession()->elementExists('css', sprintf('ul.toolbar-menu a:contains("%s")', 'Events Info'), $toolbar_tray);
|
||||
$this->assertFalse($item->hasClass('toolbar-horizontal-item-hidden'));
|
||||
|
||||
// Ensures that disabling a menu link it will not more shown in the toolbar
|
||||
// and that the changes are immediately visible.
|
||||
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
|
||||
$menu_link_manager->updateDefinition('devel.event_info', ['enabled' => FALSE]);
|
||||
|
||||
$this->drupalGet('');
|
||||
$toolbar_tray = $this->assertSession()->elementExists('css', $toolbar_tray_selector);
|
||||
$this->assertSession()->elementNotExists('css', sprintf('ul.toolbar-menu a:contains("%s")', 'Events Info'), $toolbar_tray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel when toolbar module is not installed.
|
||||
*/
|
||||
public function testToolbarModuleNotInstalled() {
|
||||
// Ensures that when toolbar module is not installed all works properly.
|
||||
\Drupal::service('module_installer')->uninstall(['toolbar']);
|
||||
|
||||
$this->drupalLogin($this->develUser);
|
||||
|
||||
// Toolbar settings page should respond with 404.
|
||||
$this->drupalGet('admin/config/development/devel/toolbar');
|
||||
$this->assertSession()->statusCodeEquals(404);
|
||||
|
||||
// Primary local task should not contains toolbar tab.
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
$this->assertSession()->statusCodeEquals(200);
|
||||
$this->assertSession()->elementNotExists('css', '.tabs .primary a:contains("Toolbar Settings")');
|
||||
|
||||
// Toolbar setting config and devel menu cache tags sholud not present.
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'config:devel.toolbar.settings');
|
||||
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'config:system.menu.devel');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for retrieve the menu link informations.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the menu link informations.
|
||||
*/
|
||||
protected function getMenuLinkInfos() {
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->onlyEnabledLinks()->setTopLevelOnly();
|
||||
$tree = \Drupal::menuTree()->load('devel', $parameters);
|
||||
|
||||
$links = [];
|
||||
foreach ($tree as $element) {
|
||||
$links[] = [
|
||||
'id' => $element->link->getPluginId(),
|
||||
'title' => $element->link->getTitle(),
|
||||
'url' => $element->link->getUrlObject()->toString(),
|
||||
];
|
||||
}
|
||||
return $links;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides convenience methods for assertions in browser tests.
|
||||
*/
|
||||
trait DevelWebAssertHelper {
|
||||
|
||||
/**
|
||||
* Asserts local tasks in the page output.
|
||||
*
|
||||
* @param array $routes
|
||||
* A list of expected local tasks, prepared as an array of route names and
|
||||
* their associated route parameters, to assert on the page (in the given
|
||||
* order).
|
||||
* @param int $level
|
||||
* (optional) The local tasks level to assert; 0 for primary, 1 for
|
||||
* secondary. Defaults to 0.
|
||||
*/
|
||||
protected function assertLocalTasks(array $routes, $level = 0) {
|
||||
$type_class = $level == 0 ? 'tabs primary' : 'tabs secondary';
|
||||
$elements = $this->xpath('//*[contains(@class, :class)]//a', [':class' => $type_class]);
|
||||
$this->assertTrue(count($elements), 'Local tasks found.');
|
||||
foreach ($routes as $index => $route_info) {
|
||||
list($route_name, $route_parameters) = $route_info;
|
||||
$expected = Url::fromRoute($route_name, $route_parameters)->toString();
|
||||
$this->assertEquals($expected, $elements[$index]->getAttribute('href'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Kernel;
|
||||
|
||||
use Drupal\Core\Mail\Plugin\Mail\TestMailCollector;
|
||||
use Drupal\devel\Plugin\Mail\DevelMailLog;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests sending mails with debug interface.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelMailLogTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['devel', 'devel_test', 'system'];
|
||||
|
||||
/**
|
||||
* The mail manager.
|
||||
*
|
||||
* @var \Drupal\Core\Mail\MailManagerInterface
|
||||
*/
|
||||
protected $mailManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', 'mail');
|
||||
$this->installConfig(['system', 'devel']);
|
||||
|
||||
// Configure system.site mail settings.
|
||||
$this->config('system.site')->set('mail', 'devel-test@example.com')->save();
|
||||
|
||||
$this->mailManager = $this->container->get('plugin.manager.mail');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel_mail_log plugin as default mail backend.
|
||||
*/
|
||||
public function testDevelMailLogDefaultBackend() {
|
||||
// Configure devel_mail_log as default mail backends.
|
||||
$this->setDevelMailLogAsDefaultBackend();
|
||||
|
||||
// Ensures that devel_mail_log is the default mail plugin .
|
||||
$mail_backend = $this->mailManager->getInstance(['module' => 'default', 'key' => 'default']);
|
||||
$this->assertInstanceOf(DevelMailLog::class, $mail_backend);
|
||||
|
||||
$mail_backend = $this->mailManager->getInstance(['module' => 'somemodule', 'key' => 'default']);
|
||||
$this->assertInstanceOf(DevelMailLog::class, $mail_backend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel_mail_log plugin with multiple mail backend.
|
||||
*/
|
||||
public function testDevelMailLogMultipleBackend() {
|
||||
// Configure test_mail_collector as default mail backend.
|
||||
$this->config('system.mail')
|
||||
->set('interface.default', 'test_mail_collector')
|
||||
->save();
|
||||
|
||||
// Configure devel_mail_log as a module-specific mail backend.
|
||||
$this->config('system.mail')
|
||||
->set('interface.somemodule', 'devel_mail_log')
|
||||
->save();
|
||||
|
||||
// Ensures that devel_mail_log is not the default mail plugin.
|
||||
$mail_backend = $this->mailManager->getInstance(['module' => 'default', 'key' => 'default']);
|
||||
$this->assertInstanceOf(TestMailCollector::class, $mail_backend);
|
||||
|
||||
// Ensures that devel_mail_log is used as mail backend only for the
|
||||
// specified module.
|
||||
$mail_backend = $this->mailManager->getInstance(['module' => 'somemodule', 'key' => 'default']);
|
||||
$this->assertInstanceOf(DevelMailLog::class, $mail_backend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel_mail_log default settings.
|
||||
*/
|
||||
public function testDevelMailDefaultSettings() {
|
||||
$config = \Drupal::config('devel.settings');
|
||||
$this->assertEquals('temporary://devel-mails', $config->get('debug_mail_directory'));
|
||||
$this->assertEquals('%to-%subject-%datetime.mail.txt', $config->get('debug_mail_file_format'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel mail log output.
|
||||
*/
|
||||
public function testDevelMailLogOutput() {
|
||||
$config = \Drupal::config('devel.settings');
|
||||
|
||||
// Parameters used for send the email.
|
||||
$mail = [
|
||||
'module' => 'devel_test',
|
||||
'key' => 'devel_mail_log',
|
||||
'to' => 'drupal@example.com',
|
||||
'reply' => 'replyto@example.com',
|
||||
'lang' => \Drupal::languageManager()->getCurrentLanguage(),
|
||||
];
|
||||
|
||||
// Parameters used for compose the email in devel_test module.
|
||||
// @see devel_test_mail()
|
||||
$params = [
|
||||
'subject' => 'Devel mail log subject',
|
||||
'body' => 'Devel mail log body',
|
||||
'headers' => [
|
||||
'from' => 'postmaster@example.com',
|
||||
'additional' => [
|
||||
'X-stupid' => 'dumb',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Configure devel_mail_log as default mail backends.
|
||||
$this->setDevelMailLogAsDefaultBackend();
|
||||
|
||||
// Changes the default filename pattern removing the dynamic date
|
||||
// placeholder for a more predictable filename output.
|
||||
$random = $this->randomMachineName();
|
||||
$filename_pattern = '%to-%subject-' . $random . '.mail.txt';
|
||||
$this->config('devel.settings')
|
||||
->set('debug_mail_file_format', $filename_pattern)
|
||||
->save();
|
||||
|
||||
$expected_filename = 'drupal@example.com-Devel_mail_log_subject-' . $random . '.mail.txt';
|
||||
$expected_output = <<<EOF
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed; delsp=yes
|
||||
Content-Transfer-Encoding: 8Bit
|
||||
X-Mailer: Drupal
|
||||
Return-Path: devel-test@example.com
|
||||
Sender: devel-test@example.com
|
||||
From: postmaster@example.com
|
||||
Reply-to: replyto@example.com
|
||||
X-stupid: dumb
|
||||
To: drupal@example.com
|
||||
Subject: Devel mail log subject
|
||||
|
||||
Devel mail log body
|
||||
|
||||
EOF;
|
||||
|
||||
// Ensures that the mail is captured by devel_mail_log and the placeholders
|
||||
// in the filename are properly resolved.
|
||||
$default_output_directory = $config->get('debug_mail_directory');
|
||||
$expected_file_path = $default_output_directory . '/' . $expected_filename;
|
||||
|
||||
$this->mailManager->mail($mail['module'], $mail['key'], $mail['to'], $mail['lang'], $params, $mail['reply']);
|
||||
$this->assertFileExists($expected_file_path);
|
||||
$this->assertStringEqualsFile($expected_file_path, $expected_output);
|
||||
|
||||
// Ensures that even changing the default output directory devel_mail_log
|
||||
// works as expected.
|
||||
$changed_output_directory = 'temporary://my-folder';
|
||||
$expected_file_path = $changed_output_directory . '/' . $expected_filename;
|
||||
$this->config('devel.settings')
|
||||
->set('debug_mail_directory', $changed_output_directory)
|
||||
->save();
|
||||
|
||||
$result = $this->mailManager->mail($mail['module'], $mail['key'], $mail['to'], $mail['lang'], $params, $mail['reply']);
|
||||
$this->assertSame(TRUE, $result['result']);
|
||||
$this->assertFileExists($expected_file_path);
|
||||
$this->assertStringEqualsFile($expected_file_path, $expected_output);
|
||||
|
||||
// Ensures that if the default output directory is a public directory it
|
||||
// will be protected by adding an .htaccess.
|
||||
$public_output_directory = 'public://my-folder';
|
||||
$expected_file_path = $public_output_directory . '/' . $expected_filename;
|
||||
$this->config('devel.settings')
|
||||
->set('debug_mail_directory', $public_output_directory)
|
||||
->save();
|
||||
|
||||
$this->mailManager->mail($mail['module'], $mail['key'], $mail['to'], $mail['lang'], $params, $mail['reply']);
|
||||
$this->assertFileExists($expected_file_path);
|
||||
$this->assertStringEqualsFile($expected_file_path, $expected_output);
|
||||
$this->assertFileExists($public_output_directory . '/.htaccess');
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure devel_mail_log as default mail backend.
|
||||
*/
|
||||
private function setDevelMailLogAsDefaultBackend() {
|
||||
// TODO can this be avoided?
|
||||
// KernelTestBase enforce the usage of 'test_mail_collector' plugin for
|
||||
// collect the mails. Since we need to test devel mail plugin we manually
|
||||
// configure the mail implementation to use 'devel_mail_log'.
|
||||
$GLOBALS['config']['system.mail']['interface']['default'] = 'devel_mail_log';
|
||||
|
||||
// Configure devel_mail_log as default mail backend.
|
||||
$this->config('system.mail')
|
||||
->set('interface.default', 'devel_mail_log')
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\devel\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests query debug.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelQueryDebugTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['devel', 'system', 'user'];
|
||||
|
||||
/**
|
||||
* The user used in test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $develUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', 'sequences');
|
||||
$this->installConfig(['system', 'devel']);
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
$devel_role = Role::create([
|
||||
'id' => 'admin',
|
||||
'permissions' => ['access devel information'],
|
||||
]);
|
||||
$devel_role->save();
|
||||
|
||||
$this->develUser = User::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'roles' => [$devel_role->id()],
|
||||
]);
|
||||
$this->develUser->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel_query_debug_alter() for select queries.
|
||||
*/
|
||||
public function testSelectQueryDebugTag() {
|
||||
// Clear the messages stack.
|
||||
$this->getDrupalMessages();
|
||||
|
||||
// Ensures that no debug message is shown to user without the adequate
|
||||
// permissions.
|
||||
$query = \Drupal::database()->select('users', 'u');
|
||||
$query->fields('u', ['uid']);
|
||||
$query->addTag('debug');
|
||||
$query->execute();
|
||||
|
||||
$messages = $this->getDrupalMessages();
|
||||
$this->assertEmpty($messages);
|
||||
|
||||
// Ensures that the SQL debug message is shown to user with the adequate
|
||||
// permissions. We expect only one status message containing the SQL for
|
||||
// the debugged query.
|
||||
\Drupal::currentUser()->setAccount($this->develUser);
|
||||
$expected_message = "SELECT u.uid AS uid\nFROM \n{users} u";
|
||||
|
||||
$query = \Drupal::database()->select('users', 'u');
|
||||
$query->fields('u', ['uid']);
|
||||
$query->addTag('debug');
|
||||
$query->execute();
|
||||
|
||||
$messages = $this->getDrupalMessages();
|
||||
$this->assertTrue(!empty($messages['status']));
|
||||
$this->assertCount(1, $messages['status']);
|
||||
$this->assertEquals(strip_tags($messages['status'][0]), $expected_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests devel_query_debug_alter() for entity queries.
|
||||
*/
|
||||
public function testEntityQueryDebugTag() {
|
||||
// Clear the messages stack.
|
||||
$this->getDrupalMessages();
|
||||
|
||||
// Ensures that no debug message is shown to user without the adequate
|
||||
// permissions.
|
||||
$query = \Drupal::entityQuery('user');
|
||||
$query->addTag('debug');
|
||||
$query->execute();
|
||||
|
||||
$messages = $this->getDrupalMessages();
|
||||
$this->assertEmpty($messages);
|
||||
|
||||
// Ensures that the SQL debug message is shown to user with the adequate
|
||||
// permissions. We expect only one status message containing the SQL for
|
||||
// the debugged entity query.
|
||||
\Drupal::currentUser()->setAccount($this->develUser);
|
||||
$expected_message = "SELECT base_table.uid AS uid, base_table.uid AS base_table_uid\nFROM \n{users} base_table";
|
||||
|
||||
$query = \Drupal::entityQuery('user');
|
||||
$query->addTag('debug');
|
||||
$query->execute();
|
||||
|
||||
$messages = $this->getDrupalMessages();
|
||||
$this->assertTrue(!empty($messages['status']));
|
||||
$this->assertCount(1, $messages['status']);
|
||||
$this->assertEquals(strip_tags($messages['status'][0]), $expected_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the drupal messages.
|
||||
*
|
||||
* @return array
|
||||
* The messages
|
||||
*/
|
||||
protected function getDrupalMessages() {
|
||||
return drupal_get_messages();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +1,92 @@
|
||||
!! README.md is a work in progress !!
|
||||
|
||||
#Dependencies:
|
||||
# Dependencies
|
||||
|
||||
- d3.js: Webprofiler module requires D3 library 3.x (not 4.x) to render data.
|
||||
Download https://github.com/mbostock/d3 into /libraries/d3/d3.min.js
|
||||
|
||||
- highlight.js: Webprofiler module requires highlight library to syntax highlight collected queries.
|
||||
Download http://highlightjs.org into /libraries/highlight
|
||||
|
||||
#IDE link:
|
||||
- highlight.js: Webprofiler module requires highlight 9.7.x library to syntax highlight collected queries.
|
||||
|
||||
Every class name discovered while profiling (controller class, event class) are linked to an url for directly open in
|
||||
an IDE, you can configure the url of those link based on the IDE you are using:
|
||||
## Install using Composer (recommended)
|
||||
|
||||
- Sublime text (2 and 3): see https://github.com/dhoulb/subl for Mac OS X
|
||||
- Textmate: should be supported by default, use txmt://open?url=file://@file&line=@line as link
|
||||
- PhpStorm 8+: use phpstorm://open?file=@file&line=@line as link
|
||||
If you use Composer to manage dependencies, edit `/composer.json` as follows.
|
||||
|
||||
#Timeline:
|
||||
1\. Run `composer require --prefer-dist composer/installers` to ensure that you have the `composer/installers` package installed. This package facilitates the installation of packages into directories other than `/vendor` (e.g. `/libraries`) using Composer.
|
||||
|
||||
Now it is possible to also collect the time needed to instantiate every single service used in a request, to make it
|
||||
work you need to add these two lines to settings.php (or, event better, to settings.local.php):
|
||||
2\. Add the following to the "installer-paths" section of `composer.json`:
|
||||
|
||||
```
|
||||
"libraries/{$name}": ["type:drupal-library"],
|
||||
```
|
||||
|
||||
3\. Add the following to the "repositories" section of `composer.json`:
|
||||
|
||||
```
|
||||
"d3": {
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "d3/d3",
|
||||
"version": "v3.5.17",
|
||||
"type": "drupal-library",
|
||||
"dist": {
|
||||
"url": "https://github.com/d3/d3/archive/v3.5.17.zip",
|
||||
"type": "zip"
|
||||
}
|
||||
}
|
||||
},
|
||||
"highlightjs": {
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "components/highlightjs",
|
||||
"version": "9.7.0",
|
||||
"type": "drupal-library",
|
||||
"dist": {
|
||||
"url": "https://github.com/components/highlightjs/archive/9.7.0.zip",
|
||||
"type": "zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
4\. Run `composer require --prefer-dist d3/d3:3.5.* components/highlightjs:9.7.*` - you should find that new directories have been created
|
||||
under `/libraries`
|
||||
|
||||
## Install manually
|
||||
|
||||
- d3.js:
|
||||
|
||||
- Create a `/libraries/d3/` directory below your Drupal root directory
|
||||
- Download https://d3js.org/d3.v3.min.js
|
||||
- Rename it to `/libraries/d3/d3.min.js`
|
||||
|
||||
For further details on how to obtain D3.js, see https://github.com/d3/d3/
|
||||
|
||||
- highlight.js:
|
||||
|
||||
- Create `/libraries/highlightjs/` directory below your Drupal root directory
|
||||
- Download the library and CSS from http://highlightjs.org into it
|
||||
|
||||
# IDE link
|
||||
|
||||
Each class name discovered while profiling (controller class, event class) is specially linked to open the class in
|
||||
an IDE. You can configure the URLs for these links to work for your IDE.
|
||||
|
||||
## Sublime text (2 and 3) - macOS
|
||||
See https://github.com/dhoulb/subl
|
||||
|
||||
## Textmate
|
||||
Use txmt://open?url=file://@file&line=@line
|
||||
|
||||
## PhpStorm 8+
|
||||
Use phpstorm://open?file=@file&line=@line
|
||||
|
||||
# Timeline
|
||||
|
||||
It is also possible to collect the time needed to instantiate every single service used in a request.
|
||||
|
||||
Add the following two lines to `settings.php` (or, even better, to `settings.local.php`):
|
||||
|
||||
```
|
||||
$class_loader->addPsr4('Drupal\\webprofiler\\', [ __DIR__ . '/../../modules/contrib/devel/webprofiler/src']);
|
||||
$settings['container_base_class'] = '\Drupal\webprofiler\DependencyInjection\TraceableContainer';
|
||||
```
|
||||
|
||||
Check if the path from the Webprofiler module in your `settings.php` file matches the location of the installed Webprofiler module in your project.
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
"name": "drupal/webprofiler",
|
||||
"description": "Drupal Web Profiler.",
|
||||
"type": "drupal-module",
|
||||
"license": "GPL-2.0+",
|
||||
"require": {
|
||||
"oomphinc/composer-installers-extender" : "v1.1.*",
|
||||
"mbostock/d3": "v3.5.*",
|
||||
"components/highlightjs": "v9.5.*",
|
||||
"symfony/stopwatch": "2.4.*"
|
||||
},
|
||||
"extra": {
|
||||
"installer-types": ["library", "component"],
|
||||
"installer-paths": {
|
||||
"libraries/{$name}": ["mbostock/d3", "components/highlightjs"]
|
||||
"authors": [
|
||||
{
|
||||
"name": "Luca Lusso",
|
||||
"email": "lussoluca@gmail.com",
|
||||
"homepage": "https://github.com/lussoluca",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"license": "GPL-2.0+",
|
||||
"require": {}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user