Completed
Push — 8.x-1.x ( 3c8412...894b5c )
by Frédéric G.
01:50
created

WorkflowsReportController::getConnectivity()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 16
nc 7
nop 1
dl 0
loc 24
rs 8.5125
c 0
b 0
f 0
1
<?php
2
3
namespace Drupal\qa\Controller;
4
5
use Drupal\content_moderation\ContentModerationState;
6
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
7
use Drupal\Core\Controller\ControllerBase;
8
use Drupal\workflows\Entity\Workflow;
9
use Symfony\Component\DependencyInjection\ContainerInterface;
10
11
/**
12
 * WorkflowsReportController validates workflow connectivity.
13
 *
14
 * It considers workflows with fonts, sink, or islands as non-optimal:
15
 * - fonts are states where it is impossible to return once they've been left,
16
 * - sinks are states from which is is impossible to exit,
17
 * - islands are unreachable states.
18
 *
19
 * The first two are inconvenient, while instances of the latter are useless.
20
 *
21
 * Implementation note: some properties and methods are public to allow reuse by
22
 * an equivalent Drush command formatting the same date for CLI display.
23
 */
24
class WorkflowsReportController extends ControllerBase {
25
26
  /**
27
   * The workflow storage.
28
   *
29
   * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
30
   */
31
  public $storage;
32
33
  /**
34
   * WorkflowsReportController constructor.
35
   *
36
   * @param \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $storage
37
   *   The workflow storage.
38
   */
39
  public function __construct(ConfigEntityStorageInterface $storage) {
40
    $this->storage = $storage;
41
  }
42
43
  /**
44
   * {@inheritdoc}
45
   */
46
  public static function create(ContainerInterface $container) {
47
    $etm = $container->get('entity_type.manager');
48
49
    /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $storage */
50
    $storage = $etm->getStorage('workflow');
51
52
    return new static($storage);
53
  }
54
55
  /**
56
   * Count transitions, fonts, sinks, and islands in a workflow.
57
   *
58
   * @param \Drupal\workflows\Entity\Workflow $workflow
59
   *   The workflow to examine.
60
   *
61
   * @return array
62
   *   An information hash.
63
   */
64
  public static function getConnectivity(Workflow $workflow) {
65
    $result = [
66
      'islandNodes' => 0,
67
      'fontNodes' => 0,
68
      'sinkNodes' => 0,
69
    ];
70
71
    /** @var ContentModerationStateInterface $state */
72
    foreach ($workflow->getStates() as $state) {
73
      $id = $state->id();
74
      $maybeIsland = FALSE;
75
      if (empty($workflow->getTransitionsForState($id, 'from'))) {
76
        $result['sinkNodes']++;
77
        $maybeIsland = TRUE;
78
      }
79
      if (empty($workflow->getTransitionsForState($id, 'to'))) {
80
        $result['fontNodes']++;
81
        if ($maybeIsland) {
82
          $result['islandNodes']++;
83
        }
84
      }
85
    }
86
    return array_filter($result);
87
  }
88
89
  /**
90
   * Build an information summary for all workflows.
91
   *
92
   * @param array $workflows
93
   *   An array of Workflow entities to check.
94
   *
95
   * @return array
96
   *   A summary information hash, keyed and ordered by id.
97
   */
98
  public function getWorkflowSummary(array $workflows) {
99
    $list = array_map(function (Workflow $workflow) {
100
      $states = array_map(function (ContentModerationState &$state) {
101
        return $state->label();
102
      }, $workflow->getStates());
103
      $stateIds = array_keys($states);
104
      sort($stateIds);
105
      $cell = [
106
        'label' => $workflow->label(),
107
        'states' => $stateIds,
108
        'transitionCount' => count($workflow->getTransitions()),
109
      ] + self::getConnectivity($workflow);
110
      return $cell;
111
    }, $workflows);
112
113
    ksort($list);
114
    return $list;
115
  }
116
117
  /**
118
   * Build a table cell from a value, using the qa/results library classes.
119
   *
120
   * Assumes the cell is to be used within a ".qa-results" CSS element.
121
   *
122
   * @param array $workflow
123
   *   An information array about a workflow.
124
   * @param string $key
125
   *   The string to extract from the information array.
126
   *
127
   * @return array
128
   *   A table cell array.
129
   */
130
  protected function buildControlCell($workflow, $key) {
131
    $value = $workflow[$key] ?? 0;
132
    $cell = $value
133
      ? ['class' => 'qa-results__ko']
134
      : ['class' => 'qa-results__ok'];
135
    $cell['data'] = $value;
136
    return $cell;
137
  }
138
139
  /**
140
   * Build a table from a workflow summary analysis.
141
   *
142
   * @param array $list
143
   *   A workflow summary analysis, from ::getWorkflowSummary().
144
   *
145
   * @return array
146
   *   A render array for the table.
147
   */
148
  protected function build(array $list) {
149
    $header = [
150
      $this->t('ID'),
151
      $this->t('Label'),
152
      $this->t('States'),
153
      $this->t('# transitions'),
154
      $this->t('# fonts'),
155
      $this->t('# sinks'),
156
      $this->t('# islands'),
157
    ];
158
    $rows = [];
159
    foreach ($list as $id => $workflow) {
160
      $row = [];
161
      $row[] = $id;
162
      $row[] = $workflow['label'];
163
      $row[] = implode(', ', $workflow['states']);
164
      $row[] = $workflow['transitionCount'] ?? 0;
165
      $row[] = $this->buildControlCell($workflow, 'fontNodes');
166
      $row[] = $this->buildControlCell($workflow, 'sinkNodes');
167
      $row[] = $this->buildControlCell($workflow, 'islandNodes');
168
      $rows[] = $row;
169
    }
170
171
    $build = [
172
      '#type' => 'table',
173
      '#attributes' => ['class' => ['qa-results']],
174
      '#header' => $header,
175
      '#rows' => $rows,
176
      '#attached' => ['library' => ['qa/results']],
177
    ];
178
179
    return $build;
180
  }
181
182
  /**
183
   * Report on existing workflows.
184
   *
185
   * @return array
186
   *   The render array for the controller.
187
   */
188
  public function report() {
189
    $workflows = $this->storage->loadMultiple();
190
    $list = $this->getWorkflowSummary($workflows);
191
    $build = $this->build($list);
192
193
    return $build;
194
  }
195
196
}
197