759bd6dbbad6368661b6d280ce641a91719e0849
[yaffs-website] / web / core / modules / content_translation / tests / src / Functional / ContentTranslationWorkflowsTest.php
1 <?php
2
3 namespace Drupal\Tests\content_translation\Functional;
4
5 use Drupal\Component\Utility\SafeMarkup;
6 use Drupal\Core\Language\LanguageInterface;
7 use Drupal\Core\Url;
8 use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
9 use Drupal\user\UserInterface;
10
11 /**
12  * Tests the content translation workflows for the test entity.
13  *
14  * @group content_translation
15  */
16 class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
17
18   use AssertPageCacheContextsAndTagsTrait;
19
20   /**
21    * The entity used for testing.
22    *
23    * @var \Drupal\Core\Entity\EntityInterface
24    */
25   protected $entity;
26
27   /**
28    * Modules to enable.
29    *
30    * @var array
31    */
32   public static $modules = ['language', 'content_translation', 'entity_test'];
33
34   protected function setUp() {
35     parent::setUp();
36     $this->setupEntity();
37   }
38
39   /**
40    * {@inheritdoc}
41    */
42   protected function getTranslatorPermissions() {
43     $permissions = parent::getTranslatorPermissions();
44     $permissions[] = 'view test entity';
45
46     return $permissions;
47   }
48
49   /**
50    * {@inheritdoc}
51    */
52   protected function getEditorPermissions() {
53     return ['administer entity_test content'];
54   }
55
56   /**
57    * Creates a test entity and translate it.
58    */
59   protected function setupEntity() {
60     $default_langcode = $this->langcodes[0];
61
62     // Create a test entity.
63     $user = $this->drupalCreateUser();
64     $values = [
65       'name' => $this->randomMachineName(),
66       'user_id' => $user->id(),
67       $this->fieldName => [['value' => $this->randomMachineName(16)]],
68     ];
69     $id = $this->createEntity($values, $default_langcode);
70     $storage = $this->container->get('entity_type.manager')
71       ->getStorage($this->entityTypeId);
72     $storage->resetCache([$id]);
73     $this->entity = $storage->load($id);
74
75     // Create a translation.
76     $this->drupalLogin($this->translator);
77     $add_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_add", [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $this->langcodes[2]]);
78     $this->drupalPostForm($add_translation_url, [], t('Save'));
79     $this->rebuildContainer();
80   }
81
82   /**
83    * Test simple and editorial translation workflows.
84    */
85   public function testWorkflows() {
86     // Test workflows for the editor.
87     $expected_status = [
88       'edit' => 200,
89       'delete' => 200,
90       'overview' => 403,
91       'add_translation' => 403,
92       'edit_translation' => 403,
93       'delete_translation' => 403,
94     ];
95     $this->doTestWorkflows($this->editor, $expected_status);
96
97     // Test workflows for the translator.
98     $expected_status = [
99       'edit' => 403,
100       'delete' => 403,
101       'overview' => 200,
102       'add_translation' => 200,
103       'edit_translation' => 200,
104       'delete_translation' => 200,
105     ];
106     $this->doTestWorkflows($this->translator, $expected_status);
107
108     // Test workflows for the admin.
109     $expected_status = [
110       'edit' => 200,
111       'delete' => 200,
112       'overview' => 200,
113       'add_translation' => 200,
114       'edit_translation' => 403,
115       'delete_translation' => 403,
116     ];
117     $this->doTestWorkflows($this->administrator, $expected_status);
118
119     // Check that translation permissions allow the associated operations.
120     $ops = ['create' => t('Add'), 'update' => t('Edit'), 'delete' => t('Delete')];
121     $translations_url = $this->entity->urlInfo('drupal:content-translation-overview');
122     foreach ($ops as $current_op => $item) {
123       $user = $this->drupalCreateUser([$this->getTranslatePermission(), "$current_op content translations", 'view test entity']);
124       $this->drupalLogin($user);
125       $this->drupalGet($translations_url);
126
127       // Make sure that the user.permissions cache context and the cache tags
128       // for the entity are present.
129       $this->assertCacheContext('user.permissions');
130       foreach ($this->entity->getCacheTags() as $cache_tag) {
131         $this->assertCacheTag($cache_tag);
132       }
133
134       foreach ($ops as $op => $label) {
135         if ($op != $current_op) {
136           $this->assertNoLink($label, format_string('No %op link found.', ['%op' => $label]));
137         }
138         else {
139           $this->assertLink($label, 0, format_string('%op link found.', ['%op' => $label]));
140         }
141       }
142     }
143   }
144
145   /**
146    * Checks that workflows have the expected behaviors for the given user.
147    *
148    * @param \Drupal\user\UserInterface $user
149    *   The user to test the workflow behavior against.
150    * @param array $expected_status
151    *   The an associative array with the operation name as key and the expected
152    *   status as value.
153    */
154   protected function doTestWorkflows(UserInterface $user, $expected_status) {
155     $default_langcode = $this->langcodes[0];
156     $languages = $this->container->get('language_manager')->getLanguages();
157     $args = ['@user_label' => $user->getUsername()];
158     $options = ['language' => $languages[$default_langcode], 'absolute' => TRUE];
159     $this->drupalLogin($user);
160
161     // Check whether the user is allowed to access the entity form in edit mode.
162     $edit_url = $this->entity->urlInfo('edit-form', $options);
163     $this->drupalGet($edit_url, $options);
164     $this->assertResponse($expected_status['edit'], SafeMarkup::format('The @user_label has the expected edit access.', $args));
165
166     // Check whether the user is allowed to access the entity delete form.
167     $delete_url = $this->entity->urlInfo('delete-form', $options);
168     $this->drupalGet($delete_url, $options);
169     $this->assertResponse($expected_status['delete'], SafeMarkup::format('The @user_label has the expected delete access.', $args));
170
171     // Check whether the user is allowed to access the translation overview.
172     $langcode = $this->langcodes[1];
173     $options['language'] = $languages[$langcode];
174     $translations_url = $this->entity->url('drupal:content-translation-overview', $options);
175     $this->drupalGet($translations_url);
176     $this->assertResponse($expected_status['overview'], SafeMarkup::format('The @user_label has the expected translation overview access.', $args));
177
178     // Check whether the user is allowed to create a translation.
179     $add_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_add", [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $langcode], $options);
180     if ($expected_status['add_translation'] == 200) {
181       $this->clickLink('Add');
182       $this->assertUrl($add_translation_url->toString(), [], 'The translation overview points to the translation form when creating translations.');
183       // Check that the translation form does not contain shared elements for
184       // translators.
185       if ($expected_status['edit'] == 403) {
186         $this->assertNoSharedElements();
187       }
188     }
189     else {
190       $this->drupalGet($add_translation_url);
191     }
192     $this->assertResponse($expected_status['add_translation'], SafeMarkup::format('The @user_label has the expected translation creation access.', $args));
193
194     // Check whether the user is allowed to edit a translation.
195     $langcode = $this->langcodes[2];
196     $options['language'] = $languages[$langcode];
197     $edit_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_edit", [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
198     if ($expected_status['edit_translation'] == 200) {
199       $this->drupalGet($translations_url);
200       $editor = $expected_status['edit'] == 200;
201
202       if ($editor) {
203         $this->clickLink('Edit', 2);
204         // An editor should be pointed to the entity form in multilingual mode.
205         // We need a new expected edit path with a new language.
206         $expected_edit_path = $this->entity->url('edit-form', $options);
207         $this->assertUrl($expected_edit_path, [], 'The translation overview points to the edit form for editors when editing translations.');
208       }
209       else {
210         $this->clickLink('Edit');
211         // While a translator should be pointed to the translation form.
212         $this->assertUrl($edit_translation_url->toString(), [], 'The translation overview points to the translation form for translators when editing translations.');
213         // Check that the translation form does not contain shared elements.
214         $this->assertNoSharedElements();
215       }
216     }
217     else {
218       $this->drupalGet($edit_translation_url);
219     }
220     $this->assertResponse($expected_status['edit_translation'], SafeMarkup::format('The @user_label has the expected translation edit access.', $args));
221
222     // Check whether the user is allowed to delete a translation.
223     $langcode = $this->langcodes[2];
224     $options['language'] = $languages[$langcode];
225     $delete_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_delete", [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
226     if ($expected_status['delete_translation'] == 200) {
227       $this->drupalGet($translations_url);
228       $editor = $expected_status['delete'] == 200;
229
230       if ($editor) {
231         $this->clickLink('Delete', 2);
232         // An editor should be pointed to the entity deletion form in
233         // multilingual mode. We need a new expected delete path with a new
234         // language.
235         $expected_delete_path = $this->entity->url('delete-form', $options);
236         $this->assertUrl($expected_delete_path, [], 'The translation overview points to the delete form for editors when deleting translations.');
237       }
238       else {
239         $this->clickLink('Delete');
240         // While a translator should be pointed to the translation deletion
241         // form.
242         $this->assertUrl($delete_translation_url->toString(), [], 'The translation overview points to the translation deletion form for translators when deleting translations.');
243       }
244     }
245     else {
246       $this->drupalGet($delete_translation_url);
247     }
248     $this->assertResponse($expected_status['delete_translation'], SafeMarkup::format('The @user_label has the expected translation deletion access.', $args));
249   }
250
251   /**
252    * Assert that the current page does not contain shared form elements.
253    */
254   protected function assertNoSharedElements() {
255     $language_none = LanguageInterface::LANGCODE_NOT_SPECIFIED;
256     return $this->assertNoFieldByXPath("//input[@name='field_test_text[$language_none][0][value]']", NULL, 'Shared elements are not available on the translation form.');
257   }
258
259 }