ViewsModerationStateFilterTest.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. <?php
  2. namespace Drupal\Tests\content_moderation\Kernel;
  3. use Drupal\entity_test\Entity\EntityTestNoBundle;
  4. use Drupal\language\Entity\ConfigurableLanguage;
  5. use Drupal\node\Entity\Node;
  6. use Drupal\node\Entity\NodeType;
  7. use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
  8. use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
  9. use Drupal\views\Views;
  10. use Drupal\workflows\Entity\Workflow;
  11. /**
  12. * Tests the views 'moderation_state_filter' filter plugin.
  13. *
  14. * @coversDefaultClass \Drupal\content_moderation\Plugin\views\filter\ModerationStateFilter
  15. *
  16. * @group content_moderation
  17. */
  18. class ViewsModerationStateFilterTest extends ViewsKernelTestBase {
  19. use ContentModerationTestTrait;
  20. /**
  21. * {@inheritdoc}
  22. */
  23. public static $modules = [
  24. 'content_moderation_test_views',
  25. 'node',
  26. 'content_moderation',
  27. 'workflows',
  28. 'workflow_type_test',
  29. 'entity_test',
  30. 'language',
  31. 'content_translation',
  32. ];
  33. /**
  34. * {@inheritdoc}
  35. */
  36. protected function setUp($import_test_views = TRUE) {
  37. parent::setUp(FALSE);
  38. $this->installEntitySchema('user');
  39. $this->installEntitySchema('node');
  40. $this->installEntitySchema('content_moderation_state');
  41. $this->installEntitySchema('entity_test_no_bundle');
  42. $this->installSchema('node', 'node_access');
  43. $this->installConfig('content_moderation_test_views');
  44. $this->installConfig('content_moderation');
  45. $node_type = NodeType::create([
  46. 'type' => 'example',
  47. ]);
  48. $node_type->save();
  49. $node_type = NodeType::create([
  50. 'type' => 'another_example',
  51. ]);
  52. $node_type->save();
  53. $node_type = NodeType::create([
  54. 'type' => 'example_non_moderated',
  55. ]);
  56. $node_type->save();
  57. ConfigurableLanguage::createFromLangcode('fr')->save();
  58. }
  59. /**
  60. * Tests the content moderation state filter.
  61. */
  62. public function testStateFilterViewsRelationship() {
  63. $workflow = $this->createEditorialWorkflow();
  64. $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
  65. $workflow->getTypePlugin()->addState('translated_draft', 'Bar');
  66. $configuration = $workflow->getTypePlugin()->getConfiguration();
  67. $configuration['states']['translated_draft'] += [
  68. 'published' => FALSE,
  69. 'default_revision' => FALSE,
  70. ];
  71. $workflow->getTypePlugin()->setConfiguration($configuration);
  72. $workflow->save();
  73. // Create a published default revision and one forward draft revision.
  74. $node = Node::create([
  75. 'type' => 'example',
  76. 'title' => 'Test Node',
  77. 'moderation_state' => 'published',
  78. ]);
  79. $node->save();
  80. $node->setNewRevision();
  81. $node->moderation_state = 'draft';
  82. $node->save();
  83. // Create a draft default revision.
  84. $second_node = Node::create([
  85. 'type' => 'example',
  86. 'title' => 'Second Node',
  87. 'moderation_state' => 'draft',
  88. ]);
  89. $second_node->save();
  90. // Create a published default revision.
  91. $third_node = Node::create([
  92. 'type' => 'example',
  93. 'title' => 'Third node',
  94. 'moderation_state' => 'published',
  95. ]);
  96. $third_node->save();
  97. // Add a non-moderated node.
  98. $fourth_node = Node::create([
  99. 'type' => 'example_non_moderated',
  100. 'title' => 'Fourth node',
  101. ]);
  102. $fourth_node->save();
  103. // Create a translated published revision.
  104. $translated_forward_revision = $third_node->addTranslation('fr');
  105. $translated_forward_revision->title = 'Translated Node';
  106. $translated_forward_revision->setNewRevision(TRUE);
  107. $translated_forward_revision->moderation_state = 'translated_draft';
  108. $translated_forward_revision->save();
  109. // The three default revisions are listed when no filter is specified.
  110. $this->assertNodesWithFilters([$node, $second_node, $third_node], []);
  111. // The default revision of node one and three are published.
  112. $this->assertNodesWithFilters([$node, $third_node], [
  113. 'default_revision_state' => 'editorial-published',
  114. ]);
  115. // The default revision of node two is draft.
  116. $this->assertNodesWithFilters([$second_node], [
  117. 'default_revision_state' => 'editorial-draft',
  118. ]);
  119. // Test the same three revisions on a view displaying content revisions.
  120. // Both nodes have one draft revision.
  121. $this->assertNodesWithFilters([$node, $second_node], [
  122. 'moderation_state' => 'editorial-draft',
  123. ], 'test_content_moderation_state_filter_revision_table');
  124. // Creating a new forward revision of node three, creates a second published
  125. // revision of of the original language, hence there are two published
  126. // revisions of node three.
  127. $this->assertNodesWithFilters([$node, $third_node, $third_node], [
  128. 'moderation_state' => 'editorial-published',
  129. ], 'test_content_moderation_state_filter_revision_table');
  130. // There is a single forward translated revision with a new state, which is
  131. // also filterable.
  132. $this->assertNodesWithFilters([$translated_forward_revision], [
  133. 'moderation_state' => 'editorial-translated_draft',
  134. ], 'test_content_moderation_state_filter_revision_table');
  135. }
  136. /**
  137. * Test the moderation filter with a non-translatable entity type.
  138. */
  139. public function testNonTranslatableEntityType() {
  140. $workflow = $this->createEditorialWorkflow();
  141. $workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_no_bundle', 'entity_test_no_bundle');
  142. $workflow->save();
  143. $test_entity = EntityTestNoBundle::create([
  144. 'moderation_state' => 'draft',
  145. ]);
  146. $test_entity->save();
  147. $view = Views::getView('test_content_moderation_state_filter_entity_test');
  148. $view->setExposedInput([
  149. 'moderation_state' => 'editorial-draft',
  150. ]);
  151. $view->execute();
  152. $this->assertIdenticalResultset($view, [['id' => $test_entity->id()]], ['id' => 'id']);
  153. }
  154. /**
  155. * Tests the list of states in the filter plugin.
  156. */
  157. public function testStateFilterStatesList() {
  158. // By default a view of nodes will not have states to filter.
  159. $this->assertPluginStates([]);
  160. // Adding a content type to the editorial workflow will enable all of the
  161. // editorial states.
  162. $workflow = $this->createEditorialWorkflow();
  163. $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
  164. $workflow->save();
  165. $this->assertPluginStates([
  166. 'Editorial' => [
  167. 'editorial-draft' => 'Draft',
  168. 'editorial-published' => 'Published',
  169. 'editorial-archived' => 'Archived',
  170. ],
  171. ]);
  172. // Adding a workflow which is not content moderation will not add any
  173. // additional states to the views filter.
  174. $workflow = Workflow::create(['id' => 'test', 'type' => 'workflow_type_complex_test']);
  175. $workflow->getTypePlugin()->addState('draft', 'Draft');
  176. $workflow->save();
  177. $this->assertPluginStates([
  178. 'Editorial' => [
  179. 'editorial-draft' => 'Draft',
  180. 'editorial-published' => 'Published',
  181. 'editorial-archived' => 'Archived',
  182. ],
  183. ]);
  184. // Adding a new content moderation workflow will add additional states to
  185. // filter.
  186. $workflow = Workflow::create(['id' => 'moderation_test', 'type' => 'content_moderation', 'label' => 'Moderation test']);
  187. $workflow->getTypePlugin()->addState('foo', 'Foo State');
  188. $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'example');
  189. $workflow->save();
  190. $this->assertPluginStates([
  191. 'Editorial' => [
  192. 'editorial-draft' => 'Draft',
  193. 'editorial-published' => 'Published',
  194. 'editorial-archived' => 'Archived',
  195. ],
  196. 'Moderation test' => [
  197. 'moderation_test-foo' => 'Foo State',
  198. 'moderation_test-draft' => 'Draft',
  199. 'moderation_test-published' => 'Published',
  200. ],
  201. ]);
  202. // Deleting a workflow will remove the states from the filter.
  203. $workflow = Workflow::load('moderation_test');
  204. $workflow->delete();
  205. $this->assertPluginStates([
  206. 'Editorial' => [
  207. 'editorial-draft' => 'Draft',
  208. 'editorial-published' => 'Published',
  209. 'editorial-archived' => 'Archived',
  210. ],
  211. ]);
  212. // Deleting a state from a workflow will remove the state from the filter.
  213. $workflow = Workflow::load('editorial');
  214. $workflow->getTypePlugin()->deleteState('archived');
  215. $workflow->save();
  216. $this->assertPluginStates([
  217. 'Editorial' => [
  218. 'editorial-draft' => 'Draft',
  219. 'editorial-published' => 'Published',
  220. ],
  221. ]);
  222. }
  223. /**
  224. * Assert the plugin states.
  225. *
  226. * @param string[] $states
  227. * The states which should appear in the filter.
  228. */
  229. protected function assertPluginStates($states) {
  230. $plugin = Views::pluginManager('filter')->createInstance('moderation_state_filter', []);
  231. $view = Views::getView('test_content_moderation_state_filter_base_table');
  232. $plugin->init($view, $view->getDisplay());
  233. $this->assertEquals($states, $plugin->getValueOptions());
  234. }
  235. /**
  236. * Assert the nodes appear when the test view is executed.
  237. *
  238. * @param \Drupal\node\NodeInterface[] $nodes
  239. * Nodes to assert are in the views result.
  240. * @param array $filters
  241. * An array of filters to apply to the view.
  242. * @param string $view_id
  243. * The view to execute for the results.
  244. */
  245. protected function assertNodesWithFilters(array $nodes, array $filters, $view_id = 'test_content_moderation_state_filter_base_table') {
  246. $view = Views::getView($view_id);
  247. $view->setExposedInput($filters);
  248. $view->execute();
  249. // Verify the join configuration.
  250. $query = $view->getQuery();
  251. $join = $query->getTableInfo('content_moderation_state')['join'];
  252. $configuration = $join->configuration;
  253. $this->assertEquals('content_moderation_state_field_revision', $configuration['table']);
  254. $this->assertEquals('content_entity_revision_id', $configuration['field']);
  255. $this->assertEquals('vid', $configuration['left_field']);
  256. $this->assertEquals('content_entity_type_id', $configuration['extra'][0]['field']);
  257. $this->assertEquals('node', $configuration['extra'][0]['value']);
  258. $this->assertEquals('langcode', $configuration['extra'][1]['field']);
  259. $this->assertEquals('langcode', $configuration['extra'][1]['left_field']);
  260. $expected_result = [];
  261. foreach ($nodes as $node) {
  262. $expected_result[] = ['nid' => $node->id()];
  263. }
  264. $this->assertIdenticalResultset($view, $expected_result, ['nid' => 'nid']);
  265. }
  266. }