Skip to content
Snippets Groups Projects
Commit e39f3b08 authored by Marcelo Vani's avatar Marcelo Vani
Browse files

Resolve #3275266 "Support depcalc"

parent b5c98469
Branches
No related tags found
1 merge request!5Resolve #3275266 "Support depcalc"
Showing
with 1004 additions and 37 deletions
.DS_Store
.idea
......@@ -109,6 +109,24 @@ test-9:
--version dev-1.x \
--dependencies ${DEPENDENCIES}
# Test in non-interactive mode
test-10:
docker run --name drupalci_${PROJECT_NAME} \
-v ~/artifacts:/artifacts \
--rm marcelovani/drupalci:10-apache \
--project ${PROJECT_NAME} \
--version dev-1.x \
--dependencies ${DEPENDENCIES}
# Test in non-interactive mode
test-11:
docker run --name drupalci_${PROJECT_NAME} \
-v ~/artifacts:/artifacts \
--rm marcelovani/drupalci:11-apache \
--project ${PROJECT_NAME} \
--version dev-1.x \
--dependencies ${DEPENDENCIES}
open:
open "http://$(PROJECT_BASE_URL):${PROJECT_PORT}"
......
......@@ -2,6 +2,12 @@
"name": "drupal/entity_dependency_visualizer",
"type": "drupal-module",
"description": "Displays a graphic output of the dependencies of entities.",
"authors": [
{
"name": "Marcelo Vani",
"homepage": "https://www.drupal.org/u/marcelovani"
}
],
"license": "GPL-2.0-or-later",
"extra": {
"branch-alias": {
......
dependency_calculator_plugin: 'native'
show_graphviz_object: false
ellipsis: true
ignore_fields:
......@@ -11,6 +12,8 @@ graph:
arrows:
display_order: false
display_content_type: true
display_field_name: true
display_entity_id: true
fontsize: 10
graph:
style: filled
......@@ -24,3 +27,4 @@ graph:
style: filled
fontname: Arial
fontsize: 11
caption: 'uuid'
......@@ -2,6 +2,9 @@ entity_dependency_visualizer.settings:
type: config_object
label: 'Entity Dependency Visualizer settings'
mapping:
dependency_calculator_plugin:
type: string
label: 'The plugin used for dependencies calculations'
show_graphviz_object:
type: boolean
label: 'Display graphviz object on the top of the page'
......@@ -71,6 +74,12 @@ entity_dependency_visualizer.graph.arrows:
display_content_type:
type: boolean
label: 'Display content type in the label'
display_field_name:
type: boolean
label: 'Display field name in the label'
display_entity_id:
type: boolean
label: 'Display entity Id in the label'
fontsize:
type: integer
label: 'Label font size'
......@@ -97,3 +106,6 @@ entity_dependency_visualizer.graph.node:
fontsize:
type: integer
label: 'Label font size'
caption:
type: string
label: 'Caption to display inside the shape'
\ No newline at end of file
......@@ -3,3 +3,4 @@ description : 'Displays a graphic output of the dependencies of entities'
core_version_requirement: ^8 || ^9 || ^10 || ^11
type: module
package: Content
configure: entity_dependency_visualizer.settings
entity_dependency_visualizer.entity_dependencies:
route_name: entity_dependency_visualizer.entity_dependencies
title: 'Content Tree'
entity_dependency_visualizer.node_dependencies:
route_name: entity_dependency_visualizer.node_dependencies
title: 'Entity dependencies'
base_route: entity.node.canonical
weight: 100
entity_dependency_visualizer.taxonomy_term_dependencies:
route_name: entity_dependency_visualizer.taxonomy_term_dependencies
title: 'Entity dependencies'
base_route: entity.taxonomy_term.canonical
weight: 100
entity_dependency_visualizer.entity_dependencies:
path: '/node/{node}/entity_dependencies'
defaults:
_controller: '\Drupal\entity_dependency_visualizer\Controller\EntityDependencies::getGraph'
_title_callback: '\Drupal\entity_dependency_visualizer\Controller\EntityDependencies::getTitle'
requirements:
_module_dependencies: 'node'
_permission: 'use entity dependencies'
node: \d+
options:
_admin_route: TRUE
parameters:
entity:
type: entity:{node}
entity_dependency_visualizer.settings:
path: '/admin/config/development/entity_dependency_visualizer/settings'
defaults:
......
services:
entity_dependency_visualizer.entity_dependencies:
class: '\Drupal\entity_dependency_visualizer\Controller\EntityDependencies'
entity_dependency_visualizer.graphviz:
class: '\Drupal\entity_dependency_visualizer\Controller\Graphviz'
entity_dependency_visualizer.dependency_stack:
class: Drupal\entity_dependency_visualizer\DependencyStack
entity_dependency_visualizer.dependency_collector:
class: Drupal\entity_dependency_visualizer\EventSubscriber\DependencyCollector
arguments: ['@config.manager']
tags:
- { name: event_subscriber, priority: -1 }
entity_dependency_visualizer.entity_dependencies_plugin:
class: '\Drupal\entity_dependency_visualizer\Plugin\DependenciesCalculator\DependenciesCalculatorAbstract'
entity_dependency_visualizer.route_subscriber:
class: Drupal\entity_dependency_visualizer\Routing\RouteSubscriber
arguments: ['@entity_type.manager']
tags:
- { name: event_subscriber }
plugin.manager.entity_dependency_visualizer:
class: 'Drupal\entity_dependency_visualizer\Plugin\DependenciesCalculator\DependenciesCalculatorManager'
parent: default_plugin_manager
<?php
namespace Drupal\entity_dependency_visualizer\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a Dependency Calculator annotation object.
*
* @Annotation
*/
class DependenciesCalculator extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The plugin name.
*
* @var string
*/
public $name;
}
......@@ -36,12 +36,14 @@ class Graphviz extends ControllerBase {
/**
* @var $max_depth The max level of nesting. This is useful to limit large
* amounts of data that will require more memory than the browser can handle.
* @todo make this configurable
*/
protected $max_depth = 100;
/**
* @var int Used to limit the size of node labels. Anything longer that this
* will be transformed to ellipsis ...
* @todo make this configurable
*/
protected $max_node_label_size = 30;
......@@ -52,11 +54,16 @@ class Graphviz extends ControllerBase {
* The array of dependencies;
*/
public function __construct($list) {
// @todo show config options on the top of graphviz on entities
$this->configuration = $this->config('entity_dependency_visualizer.settings');
$this->list = $list;
$this->checkDepth();
}
//@todo look at https://www.drupal.org/project/graphapi/ and https://www.drupal.org/project/field_tools and https://www.drupal.org/project/graphviz_filter
//@todo add module to https://graphviz.org/resources/ page
/**
* Gets the Graphviz object, see Graphviz http://www.webgraphviz.com
*
......@@ -95,9 +102,11 @@ class Graphviz extends ControllerBase {
$i++;
$label = $this->getArrowLabel($child_item, $i);
// Add node row.
$to = "$child_item [label=\"$label\"];\n";
$this->graph_obj .= "$from -> $to";
// @todo check why we have items without order coming from depcalc?
if (isset($this->list[$child_item]['info']['order'])) {
// Add node row.
$this->graph_obj .= "\"$from\" -> \"$child_item\" [label=\"$label\"];" . PHP_EOL;
}
}
}
......@@ -142,10 +151,11 @@ class Graphviz extends ControllerBase {
* @see getDefinitions()
*/
private function initGraph($definitions) {
$graph_obj = 'digraph tree {';
$graph_obj = 'digraph {' . PHP_EOL;
unset($definitions['size']);
foreach ($definitions as $key => $definition) {
if (is_array($definition)) {
$graph_obj .= "\t" . $key . ' [';
$graph_obj .= $key . ' [';
foreach ($definition as $dk => $item) {
$graph_obj .= $dk . '="' . $item . '" ';
}
......@@ -168,10 +178,17 @@ class Graphviz extends ControllerBase {
* The label element.
*/
private function getNodeLabel($item) {
$label = $item['info']['title'];
//@todo static cache $this->configuration
$caption = $this->configuration->get('graph.node.caption');
$label = $item['info'][$caption];
// Break UUID in to multiple lines.
if ($caption == 'uuid') {
$label = str_replace('-', '\n', $label);
}
// Add ellipsis if necessary.
if ($this->configuration->get('ellipsis')) {
if ($caption == 'name' && $this->configuration->get('ellipsis')) {
$label = str_replace(['"', '“', '`', '\''], '', $label);
if (strlen($label) > $this->max_node_label_size) {
$label = mb_substr($label, 0, $this->max_node_label_size) . '...';
......@@ -197,15 +214,26 @@ class Graphviz extends ControllerBase {
private function getArrowLabel($item, $i) {
$label = '';
// Display the order number.
if ($this->configuration->get('graph.arrows.display_order')) {
$label .= '#' . $i . ' ';
}
if (!empty($this->list[$item])) {
// Display the order number.
if ($this->configuration->get('graph.arrows.display_order')) {
$label .= 'order: #' . $this->list[$item]['info']['order'] . '\n';
}
// Display the node type.
if ($this->configuration->get('graph.arrows.display_content_type')) {
//todo use t()
$label .= 'bundle/name: ' . $this->list[$item]['info']['bundle'] . '\n';
}
// Display entity id.
if ($this->configuration->get('graph.arrows.display_entity_id')) {
$label .= 'url: ' . $this->list[$item]['info']['type'] . '/' . $this->list[$item]['info']['id'] . '\n';
}
// Display the node type.
if ($this->configuration->get('graph.arrows.display_content_type')) {
if (!empty($this->list[$item])) {
$label .= $this->list[$item]['info']['bundle'];
// Display the field name.
if ($this->configuration->get('graph.arrows.display_field_name')) {
$label .= 'field: ' . $this->list[$item]['info']['field'] . '\n';
}
}
......
<?php
namespace Drupal\entity_dependency_visualizer;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
use Drupal\Core\Site\Settings;
/**
* Swaps dependencies calculator class based on config.
*/
class DependencyCalculatorServiceProvider extends ServiceProviderBase {
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
$class = 'DependenciesCalculatorNative';
if ($alter_class = Settings::get('entity_dependency_visualizer.dependencies_calculator_class')) {
$class = $alter_class;
}
$class = "Drupal\entity_dependency_visualizer\Controller\\$class";
$container->getDefinition('entity_dependency_visualizer.dependencies_calculator_class')->setClass($class);
dump($class);exit;
}
}
<?php
namespace Drupal\entity_dependency_visualizer;
/**
* The dependencies stack.
*/
class DependencyStack {
/**
* The dependencies list.
*/
protected $dependencies = [];
/**
* Add a dependency to the stack.
*
* @param string $uuid
* The uuid of the entity.
* @param array $dependency
* The dependency to add to the stack.
*/
public function addDependency($uuid, $dependency) {
$this->dependencies[$uuid] = $dependency;
}
/**
* Get a specific dependency from the stack.
*
* @param string $uuid
* The uuid of the dependency to retrieve.
*
* @return array|null
* The dependent entity.
*/
public function getDependency($uuid) {
if ($this->hasDependency($uuid)) {
return $this->dependencies[$uuid];
}
}
/**
* Checks if a particular dependency exists in the stack.
*
* @param string $uuid
* The uuid of the dependency to check.
*
* @return bool
*/
public function hasDependency($uuid) {
return !empty($this->dependencies[$uuid]);
}
/**
* Get a specific set of dependencies.
*
* @param string[] $uuids
* The list of dependencies, by uuid, to retrieve.
*
* @return array
* The dependencies.
*
* @throws \Exception
*/
public function getDependenciesByUuid(array $uuids) {
$dependencies = [];
foreach ($uuids as $uuid) {
$dependency = $this->getDependency($uuid);
if(!$dependency) {
throw new \Exception(sprintf("Missing Dependency requested: %s.", $uuid));
}
$dependencies[$uuid] = $dependency;
}
return $dependencies;
}
/**
* * Get a list of dependencies within the stack.
*
* @return array
* The dependencies.
*/
public function getDependencies() {
return $this->dependencies;
}
}
<?php
namespace Drupal\entity_dependency_visualizer\EventSubscriber;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\depcalc\DependencyCalculatorEvents;
use Drupal\depcalc\Event\CalculateEntityDependenciesEvent;
use Drupal\depcalc\EventSubscriber\DependencyCollector\BaseDependencyCollector;
/**
* Subscribes to dependency collection to extract relationships.
*/
class DependencyCollector extends BaseDependencyCollector {
/**
* The controller base.
*
* @var \Drupal\entity_dependency_visualizer\Controller\DependenciesCalculator
*/
protected $entity_dependencies_controller;
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[DependencyCalculatorEvents::CALCULATE_DEPENDENCIES][] = ['onCalculateDependencies'];
return $events;
}
/**
* Calculates the referenced entities.
*
* @param \Drupal\depcalc\Event\CalculateEntityDependenciesEvent $event
* The dependency calculation event.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function onCalculateDependencies(CalculateEntityDependenciesEvent $event) {
//@todo fix bug of missing arrows from Tag a to Article 4
if (!$this->entity_dependencies_controller) {
$this->entity_dependencies_controller = \Drupal::service('entity_dependency_visualizer.entity_dependencies_plugin');
}
static $order;
$order++;
$wrapper = $event->getWrapper();
$entity = $event->getEntity();
if ($entity instanceof ContentEntityInterface) {
// @todo check if entity type is set in the configuration
// @temporarily removing users
if ($entity->getEntityTypeId() == 'user') {
return;
}
$deplist[$wrapper->getUuid()] = [
'info' => [
'id' => $entity->id(),
'type' => $entity->getEntityTypeId(),
'bundle' => '@todo bundle', //call $this->getEntityBundle($entity),
'label' => $this->entity_dependencies_controller->getEntityLabel($entity),
'color' => $this->entity_dependencies_controller->getColor($entity), //@todo use function from controller
'url' => $this->entity_dependencies_controller->getEntityUrl($entity),
'uuid' => $entity->uuid(),
'depth' => 0, //@todo fix this
'field' => '@todo add field',
'order' => $order,
],
'children' => array_values($wrapper->getChildDependencies()),
];
}
else {
// @todo make configurable to show only content, need also to remove targets with no from
return;
$deplist[$wrapper->getUuid()] = [
'info' => [
'id' => $wrapper->getId(),
'type' => $wrapper->getEntityTypeId(),
'bundle' => $wrapper->getId(), //@todo extract bundle,
'label' => $wrapper->getEntityTypeId(),
'url' => '/node/todo/entity_dependencies', //@todo add url
'uuid' => $wrapper->getUuid(),
'depth' => 0,
'field' => '@todo add field',
'color' => 'gray', //@todo this should not be added at this point
'order' => $order,
],
'children' => array_values($wrapper->getChildDependencies()),
];
}
$this->entity_dependencies_controller->getDependencyStack()->addDependency($wrapper->getUuid(), $deplist[$wrapper->getUuid()]);
}
}
......@@ -6,6 +6,7 @@ use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Config\Config;
use Drupal\Core\Url;
use Drupal\Core\Cache\Cache;
/**
* Settings form for Entity Dependency Visualizer.
......@@ -32,8 +33,23 @@ class ConfigForm extends ConfigFormBase {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
//@todo make colours configurable
$config = $this->config('entity_dependency_visualizer.settings');
$plugin_manager = \Drupal::service('plugin.manager.entity_dependency_visualizer');
$options = [];
foreach ($plugin_manager->getDefinitions() as $id => $item) {
// @todo only show the option if module exists i.e. depcalc
$options[$id] = $item['name'];
}
$form['dependency_calculator_plugin'] = [
'#type' => 'select',
'#options' => $options,
'#title' => $this->t('Dependency calculator plugin'),
'#default_value' => $config->get('dependency_calculator_plugin') ?? 'native',
'#description' => $this->t('Select which plugin to use for dependency calculations.'),
];
$form['show_graphviz_object'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show Graphviz Object'),
......@@ -48,10 +64,11 @@ class ConfigForm extends ConfigFormBase {
'#description' => $this->t('Ellipsis will be used to shorten the name of entities.'),
];
// @todo show only for native calculator
$form['ignore_fields'] = [
'#type' => 'textarea',
'#title' => $this->t('Ignore fields'),
'#default_value' => implode(PHP_EOL, $config->get('ignore_fields')),
'#default_value' => implode(PHP_EOL, $config->get('ignore_fields') ?? []),
'#size' => 40,
'#description' => $this->t('Format entity_name:field_name. One field per line. i.e. node:field_foo'),
];
......@@ -103,6 +120,13 @@ class ConfigForm extends ConfigFormBase {
'#description' => '',
];
$form['graph']['arrows']['arrow_display_field_name'] = [
'#type' => 'checkbox',
'#title' => $this->t('Display field name'),
'#default_value' => $graph['arrows']['display_field_name'],
'#description' => '',
];
$form['graph']['arrows']['arrow_display_content_type'] = [
'#type' => 'checkbox',
'#title' => $this->t('Display content type'),
......@@ -110,6 +134,13 @@ class ConfigForm extends ConfigFormBase {
'#description' => '',
];
$form['graph']['arrows']['arrow_display_entity_id'] = [
'#type' => 'checkbox',
'#title' => $this->t('Display entity Id'),
'#default_value' => $graph['arrows']['display_entity_id'],
'#description' => '',
];
$form['graph']['arrows']['arrow_fontsize'] = [
'#type' => 'textfield',
'#title' => $this->t('Font size'),
......@@ -186,7 +217,7 @@ class ConfigForm extends ConfigFormBase {
// See https://graphviz.org/doc/info/shapes.html
$form['graph']['node']['node_shape'] = [
'#type' => 'select',
'#options' => $this->getOptions(['box', 'polygon', 'ellipse', 'oval', 'circle', 'rect', 'rectangle', 'cds', 'note']),
'#options' => $this->getOptions(['box', 'polygon', 'ellipse', 'oval', 'circle', 'rect', 'rectangle', 'note']),
'#title' => $this->t('Node shape'),
'#default_value' => $graph['node']['shape'],
];
......@@ -198,6 +229,14 @@ class ConfigForm extends ConfigFormBase {
'#default_value' => $graph['node']['style'],
];
$form['graph']['node']['node_caption'] = [
'#type' => 'select',
'#options' => $this->getOptions(['uuid', 'id', 'label']),
'#title' => $this->t('Caption'),
'#default_value' => $graph['node']['caption'],
'#description' => $this->t('Select what you want to display inside the shapes.'),
];
$form['graph']['node']['node_fontname'] = [
'#type' => 'textfield',
'#title' => $this->t('Font name'),
......@@ -258,7 +297,9 @@ class ConfigForm extends ConfigFormBase {
$graph['rankdir'] = $values['graph_rankdir'];
$graph['arrows'] = [];
$graph['arrows']['display_order'] = $values['arrow_display_order'];
$graph['arrows']['display_field_name'] = $values['arrow_display_field_name'];
$graph['arrows']['display_content_type'] = $values['arrow_display_content_type'];
$graph['arrows']['display_entity_id'] = $values['arrow_display_entity_id'];
$graph['arrows']['fontsize'] = $values['arrow_fontsize'];
$graph['graph'] = [];
$graph['graph']['style'] = $values['graph_style'];
......@@ -272,13 +313,18 @@ class ConfigForm extends ConfigFormBase {
$graph['node']['style'] = $values['node_style'];
$graph['node']['fontname'] = $values['node_fontname'];
$graph['node']['fontsize'] = $values['node_fontsize'];
$graph['node']['caption'] = $values['node_caption'];
$config->set('show_graphviz_object', $values['show_graphviz_object'])
->set('dependency_calculator_plugin', $values['dependency_calculator_plugin'])
->set('ellipsis', $values['ellipsis'])
->set('ignore_fields', explode(PHP_EOL, $values['ignore_fields']))
->set('graph', $graph)
->save();
// @todo this is not working.
Cache::invalidateTags(['routes']);
parent::submitForm($form, $form_state);
}
}
<?php
/**
* @file Entity dependencies.
*/
namespace Drupal\entity_dependency_visualizer\Plugin\DependenciesCalculator;
use Drupal\Core\Controller\ControllerBase;
use Drupal\node\NodeInterface;
use Drupal\Taxonomy\TermInterface;
use Drupal\user\UserInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\file\Entity\File;
use Drupal\KernelTests\KernelTestBase;
use Drupal\taxonomy\Entity\Term;
use Drupal\user\Entity\User;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\entity_dependency_visualizer\Controller\Graphviz;
//class DependenciesCalculatorAbstract extends ControllerBase implements DependenciesCalculatorInterface { //@todo fix this
class DependenciesCalculatorAbstract extends ControllerBase {
/**
* Calculates all the dependencies of a given entity.
*
* @var \Drupal\depcalc\DependencyCalculator
*/
protected $calculator;
/**
* @var Store the config.
*/
protected $configuration;
/**
* Dependency stack.
*
* @var \Drupal\entity_dependency_visualizer\DependencyStack
*/
protected $dependency_stack;
/**
* The DependentEntityWrapper object.
*
* @var \Drupal\depcalc\DependentEntityWrapper
*/
protected $dependentEntityWrapper;
/**
* Constructor.
*/
public function __construct() {
$this->configuration = $this->config('entity_dependency_visualizer.settings');
// @todo inject this service
$this->dependency_stack = \Drupal::service('entity_dependency_visualizer.dependency_stack');
}
/**
* @inheritDoc
*/
public function getDependencyStack() {
return $this->dependency_stack;
}
/**
* @inheritDoc
*/
public function getTitle() {
return $this->t('Content Dependencies Graph');
}
/**
* @inheritDoc
*/
public function getUserGraph(UserInterface $user) {
// @todo inject this
return $this->getGraph(\Drupal::entityTypeManager()->getStorage('node')->load($user->id()));
}
/**
* @inheritDoc
*/
public function getNodeGraph(NodeInterface $node) {
return $this->getGraph(\Drupal::entityTypeManager()->getStorage('node')->load($node->id()));
}
/**
* @inheritDoc
*/
public function getTaxonomytermGraph(TermInterface $taxonomy_term) {
return $this->getGraph(\Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($taxonomy_term->id()));
}
/**
* @inheritDoc
*/
public function getGraph(EntityInterface $entity) {
//@todo some of this function should be in Graphviz.php
$this->getEntityDependencies($entity);
$graphviz = new Graphviz($this->getDependencyStack()->getDependencies());
$data = $graphviz->getGraphViz();
if ($this->configuration->get('show_graphviz_object')) {
$build['graphviz_object'] = [
'#title' => 'http://www.webgraphviz.com object', //@todo add link here
'#type' => 'textarea',
'#rows' => 4,
'#cols' => 60,
'#attributes' => ['style="width: 100%"'],
'#value' => $data,
];
}
$build['graph'] = [
'#title' => 'Container',
'#markup' => '<div id="graphviz_svg_div"></div>',
'#attached' => [
'library' => [
'entity_dependency_visualizer/graphviz',
'entity_dependency_visualizer/svg_zoom',
],
'drupalSettings' => [
'entity_dependency_visualizer' => [
'container' => 'graphviz_svg_div',
'data' => $data,
],
],
],
];
return $build;
}
/**
* @inheritDoc
*/
public function getEntityLabel($entity) {
$label = '';
if (method_exists($entity, 'getLabel')) {
$label = $entity->getLabel();
}
else if (method_exists($entity, 'label')) {
$label = $entity->label();
}
else {
$label = get_class($entity);
}
return $label;
}
/**
* @inheritDoc
*/
public function getColor($entity) {
switch ($entity->getEntityTypeId()) {
case 'user':
$color = 'lightpink2';
break;
case 'node':
$color = 'coral';
break;
case 'paragraph':
$color = 'deepskyblue';
break;
case 'taxonomy_term':
$color = 'green';
break;
default:
$color = 'gray';
}
return $color;
}
/**
* @inheritDoc
*/
public function getEntityUrl($entity) {
switch ($entity->getEntityTypeId()) {
case 'user':
$url = '/user/' . $entity->id() . '/entity_dependencies';
break;
case 'node':
$url = '/node/' . $entity->id() . '/entity_dependencies';
break;
case 'paragraph':
$url = '/node/' . $entity->getParentEntity()->id() . '/entity_dependencies';
break;
case 'taxonomy_term':
$url = '/taxonomy/term/' . $entity->id() . '/entity_dependencies';
break;
default:
$url = '';
}
return $url;
}
/**
* @inheritDoc
*/
public function getEntityBundle($entity) {
switch ($entity->getEntityTypeId()) {
case 'user':
$bundle = 'user';
break;
case 'node':
$bundle = $entity->getType();
break;
case 'paragraph':
$bundle = $entity->getParagraphType()->id();
break;
case 'taxonomy_term':
$bundle = $entity->bundle();
break;
default:
//@todo this->t()
$bundle = t('Unknown');
}
return $bundle;
}
}
<?php
/**
* @file Entity dependencies.
*/
namespace Drupal\entity_dependency_visualizer\Plugin\DependenciesCalculator;
use Drupal\Core\Controller\ControllerBase;
use Drupal\depcalc\DependencyStack;
use Drupal\depcalc\DependentEntityWrapper;
use Drupal\depcalc\DependentEntityWrapperInterface;
use Drupal\node\NodeInterface;
use Drupal\Taxonomy\TermInterface;
use Drupal\user\UserInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\file\Entity\File;
use Drupal\KernelTests\KernelTestBase;
use Drupal\taxonomy\Entity\Term;
use Drupal\user\Entity\User;
use Drupal\Core\Entity\ContentEntityInterface;
/**
* Define Depcalc dependencies calculator plugin.
*
* @DependenciesCalculator(
* id = "depcalc",
* name = @Translation("DepCalc")
* )
*/
class DependenciesCalculatorDepcalc extends DependenciesCalculatorAbstract {
/**
* Returns the list of entity dependencies.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
*
* @return array
* The list of UUIDs of dependencies (entities).
*/
protected function getEntityDependencies(EntityInterface $entity) {
$dependentEntityWrapper = new DependentEntityWrapper($entity);
$stack = new DependencyStack();
$stack->ignoreCache(true);
// @todo inject this
/** @var \Drupal\depcalc\DependencyCalculator $calculator */
$calculator = \Drupal::service('entity.dependency.calculator');
$calculator->calculateDependencies($dependentEntityWrapper, $stack);
$stack->getDependency($entity->uuid());
}
}
<?php
namespace Drupal\entity_dependency_visualizer\Plugin\DependenciesCalculator;
/**
* Define interface for native plugin.
*/
interface DependenciesCalculatorInterface {
/**
* Get page title.
*
* @return sting
* The title.
*/
public function getTitle();
/**
* Get the dependency stack.
*
* @return \Drupal\entity_dependency_visualizer\DependencyStack
*/
public function getDependencyStack();
/**
* Returns the color for the graph element.
*
* @param $entity
* The entity.
*
* @return string
* The color name.
*/
public function getColor($entity);
/**
* Get the entity url.
*
* @param $entity
* The entity.
*
* @return string
* The url.
*/
public function getEntityUrl($entity);
/**
* Get the bundle name.
*
* @param $entity
* The entity.
*
* @return string
* The bundle name.
*/
public function getEntityBundle($entity);
/**
* Get the label or name.
*
* @param $entity
* The entity.
*
* @return string
* The label or name.
*/
public function getEntityLabel($entity);
/**
* Get graphviz for an entity.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity object.
*
* @return array
* An array as expected by \Drupal\Core\Render\RendererInterface::render().
*/
public function getGraph(EntityInterface $entity);
/**
* Get graphviz for a user.
*
* @param \Drupal\user\UserInterface $user
* User object.
*
* @return array
* An array as expected by \Drupal\Core\Render\RendererInterface::render().
*/
public function getUserGraph(UserInterface $user);
/**
* Get graphviz for a node.
*
* @param \Drupal\node\NodeInterface $node
* Node object.
*
* @return array
* An array as expected by \Drupal\Core\Render\RendererInterface::render().
*/
public function getNodeGraph(NodeInterface $node);
/**
* Get graphviz for a taxonomy term.
*
* @param \Drupal\Taxonomy\TermInterface $taxonomy_term
* Term object.
*
* @return array
* An array as expected by \Drupal\Core\Render\RendererInterface::render().
*/
public function getTaxonomytermGraph(TermInterface $taxonomy_term);
}
<?php
namespace Drupal\entity_dependency_visualizer\Plugin\DependenciesCalculator;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Manages discovery and instantiation of Dependency calculator plugins.
*/
class DependenciesCalculatorManager extends DefaultPluginManager {
/**
* {@inheritDoc}
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct(
'Plugin/DependenciesCalculator',
$namespaces,
$module_handler,
'Drupal\entity_dependency_visualizer\Plugin\DependenciesCalculator\DependenciesCalculatorInterface',
'Drupal\entity_dependency_visualizer\Annotation\DependenciesCalculator'
);
$this->alterInfo('entity_dependency_visualizer_info');
$this->setCacheBackend($cache_backend, 'entity_dependency_visualizer_info_plugins');
}
}
\ No newline at end of file
<?php
/**
* @file Entity dependencies.
*/
namespace Drupal\entity_dependency_visualizer\Plugin\DependenciesCalculator;
use Drupal\Core\Controller\ControllerBase;
use Drupal\node\NodeInterface;
use Drupal\Taxonomy\TermInterface;
use Drupal\user\UserInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\file\Entity\File;
use Drupal\KernelTests\KernelTestBase;
use Drupal\taxonomy\Entity\Term;
use Drupal\user\Entity\User;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\entity_dependency_visualizer\Annotation\DependenciesCalculator;
/**
* Define Native dependencies calculator plugin.
*
* @DependenciesCalculator(
* id = "native",
* name = @Translation("Native")
* )
*/
class DependenciesCalculatorNative extends DependenciesCalculatorAbstract {
/**
* @var array List of supported entity reference types.
*/
protected $supported_entity_reference_types = [
'entity_reference',
'entity_reference_revisions',
];
/**
* @var array List of supported entity types.
* @todo do we need this now that we have check in routing?
*/
protected $supported_entity_types = [
'user',
'node',
'paragraph',
'taxonomy_term',
];
/**
* Get list.
*
* @param $entity
* The parent entity.
* @param $list
* The list of ids.
* @parm $depth
* The nesting depth.
*/
protected function getEntityDependencies(EntityInterface $entity, &$list = [], $depth = 0) {
static $order;
$order++;
$uuid = $entity->uuid();
// Prevent circular dependencies.
if (isset($list[$uuid])) {
return;
}
$list[$uuid]['info'] = [
'id' => $entity->id(),
'type' => $entity->getEntityTypeId(),
'bundle' => $this->getEntityBundle($entity),
'label' => $this->getEntityLabel($entity),
'color' => $this->getColor($entity),
'url' => $this->getEntityUrl($entity),
'uuid' => $entity->uuid(),
'depth' => $depth,
'order' => $order,
];
$this->dependency_stack->addDependency($uuid, $list[$uuid]);
// Get children.
foreach ($entity->getFieldDefinitions() as $field_name => $field_definition) {
if (is_null($field_definition->getTargetBundle())) {
continue;
}
if (!in_array($field_definition->getType(), $this->supported_entity_reference_types)) {
continue;
}
if ($this->ignoreField($entity->getEntityTypeId(), $field_name)) {
continue;
}
$list[$uuid]['info']['field'] = $field_name;
if ($referenced_entities = $this->getReferencedEntities($entity, $field_definition)) {
// Loop over the sections and get all pages.
foreach ($referenced_entities as $referenced_entity) {
// Add section to parent list.
$list[$uuid]['children'][] = $referenced_entity->uuid();
}
$depth++;
foreach ($referenced_entities as $referenced_entity) {
// Scan child items.
$this->getEntityDependencies($referenced_entity, $list, $depth);
}
}
$this->dependency_stack->addDependency($uuid, $list[$uuid]);
}
}
/**
* Check if field is in the list of ignored fields.
*
* @param $entity_type
* The entity type.
*
* @param $field_name
* The field name.
*
* @return bool
* Whether to ignore the field or not.
*/
private function ignoreField($entity_type, $field_name) {
$ignored_fields = $this->configuration->get('ignore_fields');
return in_array("$entity_type:$field_name", $ignored_fields);
}
/**
* Get child references.
*
* @param $entity
* The current entity.
*
* @param $field_definition
* The field definition.
*/
private function getReferencedEntities($entity, $field_definition) {
$entity_type = $field_definition->getTargetEntityTypeId();
if (!in_array($entity_type, $this->supported_entity_types)) {
$this->messenger()->addMessage(
$this->t('Entity type %type is not currently supported by Entity Dependencies Visualizer.',
['%type' => $entity_type]
),
'warning'
);
return;
}
$field_name = $field_definition->getName();
return $entity->{$field_name}->referencedEntities();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment