Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\content_translation\Tests;
|
Chris@0
|
4
|
Chris@18
|
5 @trigger_error(__NAMESPACE__ . '\ContentTranslationUITestBase is deprecated for removal before Drupal 9.0.0. Use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase instead. See https://www.drupal.org/node/2999939', E_USER_DEPRECATED);
|
Chris@18
|
6
|
Chris@0
|
7 use Drupal\Core\Cache\Cache;
|
Chris@0
|
8 use Drupal\Core\Entity\EntityChangedInterface;
|
Chris@0
|
9 use Drupal\Core\Entity\EntityInterface;
|
Chris@0
|
10 use Drupal\Core\Language\Language;
|
Chris@0
|
11 use Drupal\Core\Language\LanguageInterface;
|
Chris@0
|
12 use Drupal\Core\Url;
|
Chris@0
|
13 use Drupal\language\Entity\ConfigurableLanguage;
|
Chris@17
|
14 use Drupal\Component\Render\FormattableMarkup;
|
Chris@0
|
15 use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
|
Chris@0
|
16
|
Chris@0
|
17 /**
|
Chris@0
|
18 * Tests the Content Translation UI.
|
Chris@0
|
19 *
|
Chris@0
|
20 * @deprecated Scheduled for removal in Drupal 9.0.0.
|
Chris@0
|
21 * Use \Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase instead.
|
Chris@18
|
22 *
|
Chris@18
|
23 * @see https://www.drupal.org/node/2999939
|
Chris@0
|
24 */
|
Chris@0
|
25 abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
|
Chris@0
|
26
|
Chris@0
|
27 use AssertPageCacheContextsAndTagsTrait;
|
Chris@0
|
28
|
Chris@0
|
29 /**
|
Chris@0
|
30 * The id of the entity being translated.
|
Chris@0
|
31 *
|
Chris@0
|
32 * @var mixed
|
Chris@0
|
33 */
|
Chris@0
|
34 protected $entityId;
|
Chris@0
|
35
|
Chris@0
|
36 /**
|
Chris@0
|
37 * Whether the behavior of the language selector should be tested.
|
Chris@0
|
38 *
|
Chris@0
|
39 * @var bool
|
Chris@0
|
40 */
|
Chris@0
|
41 protected $testLanguageSelector = TRUE;
|
Chris@0
|
42
|
Chris@0
|
43 /**
|
Chris@17
|
44 * Flag to determine if "all languages" rendering is tested.
|
Chris@0
|
45 *
|
Chris@0
|
46 * @var bool
|
Chris@0
|
47 */
|
Chris@0
|
48 protected $testHTMLEscapeForAllLanguages = FALSE;
|
Chris@0
|
49
|
Chris@0
|
50 /**
|
Chris@0
|
51 * Default cache contexts expected on a non-translated entity.
|
Chris@0
|
52 *
|
Chris@0
|
53 * Cache contexts will not be checked if this list is empty.
|
Chris@0
|
54 *
|
Chris@0
|
55 * @var string[]
|
Chris@0
|
56 */
|
Chris@0
|
57 protected $defaultCacheContexts = ['languages:language_interface', 'theme', 'url.query_args:_wrapper_format', 'user.permissions'];
|
Chris@0
|
58
|
Chris@0
|
59 /**
|
Chris@0
|
60 * Tests the basic translation UI.
|
Chris@0
|
61 */
|
Chris@0
|
62 public function testTranslationUI() {
|
Chris@0
|
63 $this->doTestBasicTranslation();
|
Chris@0
|
64 $this->doTestTranslationOverview();
|
Chris@0
|
65 $this->doTestOutdatedStatus();
|
Chris@0
|
66 $this->doTestPublishedStatus();
|
Chris@0
|
67 $this->doTestAuthoringInfo();
|
Chris@0
|
68 $this->doTestTranslationEdit();
|
Chris@0
|
69 $this->doTestTranslationChanged();
|
Chris@0
|
70 $this->doTestChangedTimeAfterSaveWithoutChanges();
|
Chris@0
|
71 $this->doTestTranslationDeletion();
|
Chris@0
|
72 }
|
Chris@0
|
73
|
Chris@0
|
74 /**
|
Chris@0
|
75 * Tests the basic translation workflow.
|
Chris@0
|
76 */
|
Chris@0
|
77 protected function doTestBasicTranslation() {
|
Chris@0
|
78 // Create a new test entity with original values in the default language.
|
Chris@0
|
79 $default_langcode = $this->langcodes[0];
|
Chris@0
|
80 $values[$default_langcode] = $this->getNewEntityValues($default_langcode);
|
Chris@0
|
81 // Create the entity with the editor as owner, so that afterwards a new
|
Chris@0
|
82 // translation is created by the translator and the translation author is
|
Chris@0
|
83 // tested.
|
Chris@0
|
84 $this->drupalLogin($this->editor);
|
Chris@0
|
85 $this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
|
Chris@0
|
86 $this->drupalLogin($this->translator);
|
Chris@0
|
87 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
88 ->getStorage($this->entityTypeId);
|
Chris@0
|
89 $storage->resetCache([$this->entityId]);
|
Chris@0
|
90 $entity = $storage->load($this->entityId);
|
Chris@0
|
91 $this->assertTrue($entity, 'Entity found in the database.');
|
Chris@18
|
92 $this->drupalGet($entity->toUrl());
|
Chris@0
|
93 $this->assertResponse(200, 'Entity URL is valid.');
|
Chris@0
|
94
|
Chris@0
|
95 // Ensure that the content language cache context is not yet added to the
|
Chris@0
|
96 // page.
|
Chris@0
|
97 $this->assertCacheContexts($this->defaultCacheContexts);
|
Chris@0
|
98
|
Chris@18
|
99 $this->drupalGet($entity->toUrl('drupal:content-translation-overview'));
|
Chris@0
|
100 $this->assertNoText('Source language', 'Source language column correctly hidden.');
|
Chris@0
|
101
|
Chris@0
|
102 $translation = $this->getTranslation($entity, $default_langcode);
|
Chris@0
|
103 foreach ($values[$default_langcode] as $property => $value) {
|
Chris@0
|
104 $stored_value = $this->getValue($translation, $property, $default_langcode);
|
Chris@0
|
105 $value = is_array($value) ? $value[0]['value'] : $value;
|
Chris@0
|
106 $message = format_string('@property correctly stored in the default language.', ['@property' => $property]);
|
Chris@0
|
107 $this->assertEqual($stored_value, $value, $message);
|
Chris@0
|
108 }
|
Chris@0
|
109
|
Chris@0
|
110 // Add a content translation.
|
Chris@0
|
111 $langcode = 'it';
|
Chris@0
|
112 $language = ConfigurableLanguage::load($langcode);
|
Chris@0
|
113 $values[$langcode] = $this->getNewEntityValues($langcode);
|
Chris@0
|
114
|
Chris@0
|
115 $entity_type_id = $entity->getEntityTypeId();
|
Chris@0
|
116 $add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
|
Chris@0
|
117 $entity->getEntityTypeId() => $entity->id(),
|
Chris@0
|
118 'source' => $default_langcode,
|
Chris@17
|
119 'target' => $langcode,
|
Chris@0
|
120 ], ['language' => $language]);
|
Chris@0
|
121 $this->drupalPostForm($add_url, $this->getEditValues($values, $langcode), $this->getFormSubmitActionForNewTranslation($entity, $langcode));
|
Chris@0
|
122
|
Chris@17
|
123 // Assert that HTML is not escaped unexpectedly.
|
Chris@0
|
124 if ($this->testHTMLEscapeForAllLanguages) {
|
Chris@0
|
125 $this->assertNoRaw('<span class="translation-entity-all-languages">(all languages)</span>');
|
Chris@0
|
126 $this->assertRaw('<span class="translation-entity-all-languages">(all languages)</span>');
|
Chris@0
|
127 }
|
Chris@0
|
128
|
Chris@0
|
129 // Ensure that the content language cache context is not yet added to the
|
Chris@0
|
130 // page.
|
Chris@0
|
131 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
132 ->getStorage($this->entityTypeId);
|
Chris@0
|
133 $storage->resetCache([$this->entityId]);
|
Chris@0
|
134 $entity = $storage->load($this->entityId);
|
Chris@18
|
135 $this->drupalGet($entity->toUrl());
|
Chris@0
|
136 $this->assertCacheContexts(Cache::mergeContexts(['languages:language_content'], $this->defaultCacheContexts));
|
Chris@0
|
137
|
Chris@0
|
138 // Reset the cache of the entity, so that the new translation gets the
|
Chris@0
|
139 // updated values.
|
Chris@0
|
140 $metadata_source_translation = $this->manager->getTranslationMetadata($entity->getTranslation($default_langcode));
|
Chris@0
|
141 $metadata_target_translation = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
|
Chris@0
|
142
|
Chris@0
|
143 $author_field_name = $entity->hasField('content_translation_uid') ? 'content_translation_uid' : 'uid';
|
Chris@0
|
144 if ($entity->getFieldDefinition($author_field_name)->isTranslatable()) {
|
Chris@0
|
145 $this->assertEqual($metadata_target_translation->getAuthor()->id(), $this->translator->id(),
|
Chris@17
|
146 new FormattableMarkup('Author of the target translation @langcode correctly stored for translatable owner field.', ['@langcode' => $langcode]));
|
Chris@0
|
147
|
Chris@0
|
148 $this->assertNotEqual($metadata_target_translation->getAuthor()->id(), $metadata_source_translation->getAuthor()->id(),
|
Chris@17
|
149 new FormattableMarkup('Author of the target translation @target different from the author of the source translation @source for translatable owner field.',
|
Chris@0
|
150 ['@target' => $langcode, '@source' => $default_langcode]));
|
Chris@0
|
151 }
|
Chris@0
|
152 else {
|
Chris@0
|
153 $this->assertEqual($metadata_target_translation->getAuthor()->id(), $this->editor->id(), 'Author of the entity remained untouched after translation for non translatable owner field.');
|
Chris@0
|
154 }
|
Chris@0
|
155
|
Chris@0
|
156 $created_field_name = $entity->hasField('content_translation_created') ? 'content_translation_created' : 'created';
|
Chris@0
|
157 if ($entity->getFieldDefinition($created_field_name)->isTranslatable()) {
|
Chris@0
|
158 $this->assertTrue($metadata_target_translation->getCreatedTime() > $metadata_source_translation->getCreatedTime(),
|
Chris@17
|
159 new FormattableMarkup('Translation creation timestamp of the target translation @target is newer than the creation timestamp of the source translation @source for translatable created field.',
|
Chris@0
|
160 ['@target' => $langcode, '@source' => $default_langcode]));
|
Chris@0
|
161 }
|
Chris@0
|
162 else {
|
Chris@0
|
163 $this->assertEqual($metadata_target_translation->getCreatedTime(), $metadata_source_translation->getCreatedTime(), 'Creation timestamp of the entity remained untouched after translation for non translatable created field.');
|
Chris@0
|
164 }
|
Chris@0
|
165
|
Chris@0
|
166 if ($this->testLanguageSelector) {
|
Chris@0
|
167 $this->assertNoFieldByXPath('//select[@id="edit-langcode-0-value"]', NULL, 'Language selector correctly disabled on translations.');
|
Chris@0
|
168 }
|
Chris@0
|
169 $storage->resetCache([$this->entityId]);
|
Chris@0
|
170 $entity = $storage->load($this->entityId);
|
Chris@18
|
171 $this->drupalGet($entity->toUrl('drupal:content-translation-overview'));
|
Chris@0
|
172 $this->assertNoText('Source language', 'Source language column correctly hidden.');
|
Chris@0
|
173
|
Chris@0
|
174 // Switch the source language.
|
Chris@0
|
175 $langcode = 'fr';
|
Chris@0
|
176 $language = ConfigurableLanguage::load($langcode);
|
Chris@0
|
177 $source_langcode = 'it';
|
Chris@0
|
178 $edit = ['source_langcode[source]' => $source_langcode];
|
Chris@0
|
179 $entity_type_id = $entity->getEntityTypeId();
|
Chris@0
|
180 $add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
|
Chris@0
|
181 $entity->getEntityTypeId() => $entity->id(),
|
Chris@0
|
182 'source' => $default_langcode,
|
Chris@17
|
183 'target' => $langcode,
|
Chris@0
|
184 ], ['language' => $language]);
|
Chris@0
|
185 // This does not save anything, it merely reloads the form and fills in the
|
Chris@0
|
186 // fields with the values from the different source language.
|
Chris@0
|
187 $this->drupalPostForm($add_url, $edit, t('Change'));
|
Chris@0
|
188 $this->assertFieldByXPath("//input[@name=\"{$this->fieldName}[0][value]\"]", $values[$source_langcode][$this->fieldName][0]['value'], 'Source language correctly switched.');
|
Chris@0
|
189
|
Chris@0
|
190 // Add another translation and mark the other ones as outdated.
|
Chris@0
|
191 $values[$langcode] = $this->getNewEntityValues($langcode);
|
Chris@0
|
192 $edit = $this->getEditValues($values, $langcode) + ['content_translation[retranslate]' => TRUE];
|
Chris@0
|
193 $entity_type_id = $entity->getEntityTypeId();
|
Chris@0
|
194 $add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
|
Chris@0
|
195 $entity->getEntityTypeId() => $entity->id(),
|
Chris@0
|
196 'source' => $source_langcode,
|
Chris@17
|
197 'target' => $langcode,
|
Chris@0
|
198 ], ['language' => $language]);
|
Chris@0
|
199 $this->drupalPostForm($add_url, $edit, $this->getFormSubmitActionForNewTranslation($entity, $langcode));
|
Chris@0
|
200 $storage->resetCache([$this->entityId]);
|
Chris@0
|
201 $entity = $storage->load($this->entityId);
|
Chris@18
|
202 $this->drupalGet($entity->toUrl('drupal:content-translation-overview'));
|
Chris@0
|
203 $this->assertText('Source language', 'Source language column correctly shown.');
|
Chris@0
|
204
|
Chris@0
|
205 // Check that the entered values have been correctly stored.
|
Chris@0
|
206 foreach ($values as $langcode => $property_values) {
|
Chris@0
|
207 $translation = $this->getTranslation($entity, $langcode);
|
Chris@0
|
208 foreach ($property_values as $property => $value) {
|
Chris@0
|
209 $stored_value = $this->getValue($translation, $property, $langcode);
|
Chris@0
|
210 $value = is_array($value) ? $value[0]['value'] : $value;
|
Chris@0
|
211 $message = format_string('%property correctly stored with language %language.', ['%property' => $property, '%language' => $langcode]);
|
Chris@0
|
212 $this->assertEqual($stored_value, $value, $message);
|
Chris@0
|
213 }
|
Chris@0
|
214 }
|
Chris@0
|
215 }
|
Chris@0
|
216
|
Chris@0
|
217 /**
|
Chris@0
|
218 * Tests that the translation overview shows the correct values.
|
Chris@0
|
219 */
|
Chris@0
|
220 protected function doTestTranslationOverview() {
|
Chris@0
|
221 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
222 ->getStorage($this->entityTypeId);
|
Chris@0
|
223 $storage->resetCache([$this->entityId]);
|
Chris@0
|
224 $entity = $storage->load($this->entityId);
|
Chris@18
|
225 $translate_url = $entity->toUrl('drupal:content-translation-overview');
|
Chris@0
|
226 $this->drupalGet($translate_url);
|
Chris@0
|
227 $translate_url->setAbsolute(FALSE);
|
Chris@0
|
228
|
Chris@0
|
229 foreach ($this->langcodes as $langcode) {
|
Chris@0
|
230 if ($entity->hasTranslation($langcode)) {
|
Chris@0
|
231 $language = new Language(['id' => $langcode]);
|
Chris@18
|
232 $view_url = $entity->toUrl('canonical', ['language' => $language])->toString();
|
Chris@0
|
233 $elements = $this->xpath('//table//a[@href=:href]', [':href' => $view_url]);
|
Chris@0
|
234 $this->assertEqual((string) $elements[0], $entity->getTranslation($langcode)->label(), format_string('Label correctly shown for %language translation.', ['%language' => $langcode]));
|
Chris@18
|
235 $edit_path = $entity->toUrl('edit-form', ['language' => $language])->toString();
|
Chris@0
|
236 $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', [':href' => $edit_path]);
|
Chris@0
|
237 $this->assertEqual((string) $elements[0], t('Edit'), format_string('Edit link correct for %language translation.', ['%language' => $langcode]));
|
Chris@0
|
238 }
|
Chris@0
|
239 }
|
Chris@0
|
240 }
|
Chris@0
|
241
|
Chris@0
|
242 /**
|
Chris@0
|
243 * Tests up-to-date status tracking.
|
Chris@0
|
244 */
|
Chris@0
|
245 protected function doTestOutdatedStatus() {
|
Chris@0
|
246 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
247 ->getStorage($this->entityTypeId);
|
Chris@0
|
248 $storage->resetCache([$this->entityId]);
|
Chris@0
|
249 $entity = $storage->load($this->entityId);
|
Chris@0
|
250 $langcode = 'fr';
|
Chris@0
|
251 $languages = \Drupal::languageManager()->getLanguages();
|
Chris@0
|
252
|
Chris@0
|
253 // Mark translations as outdated.
|
Chris@0
|
254 $edit = ['content_translation[retranslate]' => TRUE];
|
Chris@18
|
255 $edit_path = $entity->toUrl('edit-form', ['language' => $languages[$langcode]]);
|
Chris@0
|
256 $this->drupalPostForm($edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
|
Chris@0
|
257 $storage->resetCache([$this->entityId]);
|
Chris@0
|
258 $entity = $storage->load($this->entityId);
|
Chris@0
|
259
|
Chris@0
|
260 // Check that every translation has the correct "outdated" status, and that
|
Chris@0
|
261 // the Translation fieldset is open if the translation is "outdated".
|
Chris@0
|
262 foreach ($this->langcodes as $added_langcode) {
|
Chris@18
|
263 $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($added_langcode)]);
|
Chris@0
|
264 $this->drupalGet($url);
|
Chris@0
|
265 if ($added_langcode == $langcode) {
|
Chris@0
|
266 $this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is not checked by default.');
|
Chris@0
|
267 $this->assertFalse($this->xpath('//details[@id="edit-content-translation" and @open="open"]'), 'The translation tab should be collapsed by default.');
|
Chris@0
|
268 }
|
Chris@0
|
269 else {
|
Chris@0
|
270 $this->assertFieldByXPath('//input[@name="content_translation[outdated]"]', TRUE, 'The translate flag is checked by default.');
|
Chris@0
|
271 $this->assertTrue($this->xpath('//details[@id="edit-content-translation" and @open="open"]'), 'The translation tab is correctly expanded when the translation is outdated.');
|
Chris@0
|
272 $edit = ['content_translation[outdated]' => FALSE];
|
Chris@0
|
273 $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $added_langcode));
|
Chris@0
|
274 $this->drupalGet($url);
|
Chris@0
|
275 $this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is now shown.');
|
Chris@0
|
276 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
277 ->getStorage($this->entityTypeId);
|
Chris@0
|
278 $storage->resetCache([$this->entityId]);
|
Chris@0
|
279 $entity = $storage->load($this->entityId);
|
Chris@0
|
280 $this->assertFalse($this->manager->getTranslationMetadata($entity->getTranslation($added_langcode))->isOutdated(), 'The "outdated" status has been correctly stored.');
|
Chris@0
|
281 }
|
Chris@0
|
282 }
|
Chris@0
|
283 }
|
Chris@0
|
284
|
Chris@0
|
285 /**
|
Chris@0
|
286 * Tests the translation publishing status.
|
Chris@0
|
287 */
|
Chris@0
|
288 protected function doTestPublishedStatus() {
|
Chris@0
|
289 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
290 ->getStorage($this->entityTypeId);
|
Chris@0
|
291 $storage->resetCache([$this->entityId]);
|
Chris@0
|
292 $entity = $storage->load($this->entityId);
|
Chris@0
|
293
|
Chris@0
|
294 // Unpublish translations.
|
Chris@0
|
295 foreach ($this->langcodes as $index => $langcode) {
|
Chris@0
|
296 if ($index > 0) {
|
Chris@18
|
297 $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
|
Chris@0
|
298 $edit = ['content_translation[status]' => FALSE];
|
Chris@0
|
299 $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
|
Chris@0
|
300 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
301 ->getStorage($this->entityTypeId);
|
Chris@0
|
302 $storage->resetCache([$this->entityId]);
|
Chris@0
|
303 $entity = $storage->load($this->entityId);
|
Chris@0
|
304 $this->assertFalse($this->manager->getTranslationMetadata($entity->getTranslation($langcode))->isPublished(), 'The translation has been correctly unpublished.');
|
Chris@0
|
305 }
|
Chris@0
|
306 }
|
Chris@0
|
307
|
Chris@0
|
308 // Check that the last published translation cannot be unpublished.
|
Chris@18
|
309 $this->drupalGet($entity->toUrl('edit-form'));
|
Chris@0
|
310 $this->assertFieldByXPath('//input[@name="content_translation[status]" and @disabled="disabled"]', TRUE, 'The last translation is published and cannot be unpublished.');
|
Chris@0
|
311 }
|
Chris@0
|
312
|
Chris@0
|
313 /**
|
Chris@0
|
314 * Tests the translation authoring information.
|
Chris@0
|
315 */
|
Chris@0
|
316 protected function doTestAuthoringInfo() {
|
Chris@0
|
317 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
318 ->getStorage($this->entityTypeId);
|
Chris@0
|
319 $storage->resetCache([$this->entityId]);
|
Chris@0
|
320 $entity = $storage->load($this->entityId);
|
Chris@0
|
321 $values = [];
|
Chris@0
|
322
|
Chris@0
|
323 // Post different authoring information for each translation.
|
Chris@0
|
324 foreach ($this->langcodes as $index => $langcode) {
|
Chris@0
|
325 $user = $this->drupalCreateUser();
|
Chris@0
|
326 $values[$langcode] = [
|
Chris@0
|
327 'uid' => $user->id(),
|
Chris@0
|
328 'created' => REQUEST_TIME - mt_rand(0, 1000),
|
Chris@0
|
329 ];
|
Chris@0
|
330 $edit = [
|
Chris@18
|
331 'content_translation[uid]' => $user->getAccountName(),
|
Chris@18
|
332 'content_translation[created]' => $this->container->get('date.formatter')->format($values[$langcode]['created'], 'custom', 'Y-m-d H:i:s O'),
|
Chris@0
|
333 ];
|
Chris@18
|
334 $url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
|
Chris@0
|
335 $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
|
Chris@0
|
336 }
|
Chris@0
|
337
|
Chris@0
|
338 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
339 ->getStorage($this->entityTypeId);
|
Chris@0
|
340 $storage->resetCache([$this->entityId]);
|
Chris@0
|
341 $entity = $storage->load($this->entityId);
|
Chris@0
|
342 foreach ($this->langcodes as $langcode) {
|
Chris@0
|
343 $metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
|
Chris@0
|
344 $this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.');
|
Chris@0
|
345 $this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly stored.');
|
Chris@0
|
346 }
|
Chris@0
|
347
|
Chris@0
|
348 // Try to post non valid values and check that they are rejected.
|
Chris@0
|
349 $langcode = 'en';
|
Chris@0
|
350 $edit = [
|
Chris@0
|
351 // User names have by default length 8.
|
Chris@0
|
352 'content_translation[uid]' => $this->randomMachineName(12),
|
Chris@0
|
353 'content_translation[created]' => '19/11/1978',
|
Chris@0
|
354 ];
|
Chris@18
|
355 $this->drupalPostForm($entity->toUrl('edit-form'), $edit, $this->getFormSubmitAction($entity, $langcode));
|
Chris@0
|
356 $this->assertTrue($this->xpath('//div[contains(@class, "error")]//ul'), 'Invalid values generate a list of form errors.');
|
Chris@0
|
357 $metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
|
Chris@0
|
358 $this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly kept.');
|
Chris@0
|
359 $this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly kept.');
|
Chris@0
|
360 }
|
Chris@0
|
361
|
Chris@0
|
362 /**
|
Chris@0
|
363 * Tests translation deletion.
|
Chris@0
|
364 */
|
Chris@0
|
365 protected function doTestTranslationDeletion() {
|
Chris@0
|
366 // Confirm and delete a translation.
|
Chris@0
|
367 $this->drupalLogin($this->translator);
|
Chris@0
|
368 $langcode = 'fr';
|
Chris@0
|
369 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
370 ->getStorage($this->entityTypeId);
|
Chris@0
|
371 $storage->resetCache([$this->entityId]);
|
Chris@0
|
372 $entity = $storage->load($this->entityId);
|
Chris@0
|
373 $language = ConfigurableLanguage::load($langcode);
|
Chris@18
|
374 $url = $entity->toUrl('edit-form', ['language' => $language]);
|
Chris@0
|
375 $this->drupalPostForm($url, [], t('Delete translation'));
|
Chris@0
|
376 $this->drupalPostForm(NULL, [], t('Delete @language translation', ['@language' => $language->getName()]));
|
Chris@0
|
377 $storage->resetCache([$this->entityId]);
|
Chris@0
|
378 $entity = $storage->load($this->entityId, TRUE);
|
Chris@0
|
379 if ($this->assertTrue(is_object($entity), 'Entity found')) {
|
Chris@0
|
380 $translations = $entity->getTranslationLanguages();
|
Chris@0
|
381 $this->assertTrue(count($translations) == 2 && empty($translations[$langcode]), 'Translation successfully deleted.');
|
Chris@0
|
382 }
|
Chris@0
|
383
|
Chris@0
|
384 // Check that the translator cannot delete the original translation.
|
Chris@0
|
385 $args = [$this->entityTypeId => $entity->id(), 'language' => 'en'];
|
Chris@0
|
386 $this->drupalGet(Url::fromRoute("entity.$this->entityTypeId.content_translation_delete", $args));
|
Chris@0
|
387 $this->assertResponse(403);
|
Chris@0
|
388 }
|
Chris@0
|
389
|
Chris@0
|
390 /**
|
Chris@0
|
391 * Returns an array of entity field values to be tested.
|
Chris@0
|
392 */
|
Chris@0
|
393 protected function getNewEntityValues($langcode) {
|
Chris@0
|
394 return [$this->fieldName => [['value' => $this->randomMachineName(16)]]];
|
Chris@0
|
395 }
|
Chris@0
|
396
|
Chris@0
|
397 /**
|
Chris@0
|
398 * Returns an edit array containing the values to be posted.
|
Chris@0
|
399 */
|
Chris@0
|
400 protected function getEditValues($values, $langcode, $new = FALSE) {
|
Chris@0
|
401 $edit = $values[$langcode];
|
Chris@0
|
402 $langcode = $new ? LanguageInterface::LANGCODE_NOT_SPECIFIED : $langcode;
|
Chris@0
|
403 foreach ($values[$langcode] as $property => $value) {
|
Chris@0
|
404 if (is_array($value)) {
|
Chris@0
|
405 $edit["{$property}[0][value]"] = $value[0]['value'];
|
Chris@0
|
406 unset($edit[$property]);
|
Chris@0
|
407 }
|
Chris@0
|
408 }
|
Chris@0
|
409 return $edit;
|
Chris@0
|
410 }
|
Chris@0
|
411
|
Chris@0
|
412 /**
|
Chris@0
|
413 * Returns the form action value when submitting a new translation.
|
Chris@0
|
414 *
|
Chris@0
|
415 * @param \Drupal\Core\Entity\EntityInterface $entity
|
Chris@0
|
416 * The entity being tested.
|
Chris@0
|
417 * @param string $langcode
|
Chris@0
|
418 * Language code for the form.
|
Chris@0
|
419 *
|
Chris@0
|
420 * @return string
|
Chris@0
|
421 * Name of the button to hit.
|
Chris@0
|
422 */
|
Chris@0
|
423 protected function getFormSubmitActionForNewTranslation(EntityInterface $entity, $langcode) {
|
Chris@0
|
424 $entity->addTranslation($langcode, $entity->toArray());
|
Chris@0
|
425 return $this->getFormSubmitAction($entity, $langcode);
|
Chris@0
|
426 }
|
Chris@0
|
427
|
Chris@0
|
428 /**
|
Chris@0
|
429 * Returns the form action value to be used to submit the entity form.
|
Chris@0
|
430 *
|
Chris@0
|
431 * @param \Drupal\Core\Entity\EntityInterface $entity
|
Chris@0
|
432 * The entity being tested.
|
Chris@0
|
433 * @param string $langcode
|
Chris@0
|
434 * Language code for the form.
|
Chris@0
|
435 *
|
Chris@0
|
436 * @return string
|
Chris@0
|
437 * Name of the button to hit.
|
Chris@0
|
438 */
|
Chris@0
|
439 protected function getFormSubmitAction(EntityInterface $entity, $langcode) {
|
Chris@0
|
440 return t('Save') . $this->getFormSubmitSuffix($entity, $langcode);
|
Chris@0
|
441 }
|
Chris@0
|
442
|
Chris@0
|
443 /**
|
Chris@0
|
444 * Returns appropriate submit button suffix based on translatability.
|
Chris@0
|
445 *
|
Chris@0
|
446 * @param \Drupal\Core\Entity\EntityInterface $entity
|
Chris@0
|
447 * The entity being tested.
|
Chris@0
|
448 * @param string $langcode
|
Chris@0
|
449 * Language code for the form.
|
Chris@0
|
450 *
|
Chris@0
|
451 * @return string
|
Chris@0
|
452 * Submit button suffix based on translatability.
|
Chris@0
|
453 */
|
Chris@0
|
454 protected function getFormSubmitSuffix(EntityInterface $entity, $langcode) {
|
Chris@0
|
455 return '';
|
Chris@0
|
456 }
|
Chris@0
|
457
|
Chris@0
|
458 /**
|
Chris@0
|
459 * Returns the translation object to use to retrieve the translated values.
|
Chris@0
|
460 *
|
Chris@0
|
461 * @param \Drupal\Core\Entity\EntityInterface $entity
|
Chris@0
|
462 * The entity being tested.
|
Chris@0
|
463 * @param string $langcode
|
Chris@0
|
464 * The language code identifying the translation to be retrieved.
|
Chris@0
|
465 *
|
Chris@0
|
466 * @return \Drupal\Core\TypedData\TranslatableInterface
|
Chris@0
|
467 * The translation object to act on.
|
Chris@0
|
468 */
|
Chris@0
|
469 protected function getTranslation(EntityInterface $entity, $langcode) {
|
Chris@0
|
470 return $entity->getTranslation($langcode);
|
Chris@0
|
471 }
|
Chris@0
|
472
|
Chris@0
|
473 /**
|
Chris@0
|
474 * Returns the value for the specified property in the given language.
|
Chris@0
|
475 *
|
Chris@0
|
476 * @param \Drupal\Core\Entity\EntityInterface $translation
|
Chris@0
|
477 * The translation object the property value should be retrieved from.
|
Chris@0
|
478 * @param string $property
|
Chris@0
|
479 * The property name.
|
Chris@0
|
480 * @param string $langcode
|
Chris@0
|
481 * The property value.
|
Chris@0
|
482 *
|
Chris@0
|
483 * @return
|
Chris@0
|
484 * The property value.
|
Chris@0
|
485 */
|
Chris@0
|
486 protected function getValue(EntityInterface $translation, $property, $langcode) {
|
Chris@0
|
487 $key = $property == 'user_id' ? 'target_id' : 'value';
|
Chris@0
|
488 return $translation->get($property)->{$key};
|
Chris@0
|
489 }
|
Chris@0
|
490
|
Chris@0
|
491 /**
|
Chris@0
|
492 * Returns the name of the field that implements the changed timestamp.
|
Chris@0
|
493 *
|
Chris@0
|
494 * @param \Drupal\Core\Entity\EntityInterface $entity
|
Chris@0
|
495 * The entity being tested.
|
Chris@0
|
496 *
|
Chris@0
|
497 * @return string
|
Chris@0
|
498 * The field name.
|
Chris@0
|
499 */
|
Chris@0
|
500 protected function getChangedFieldName($entity) {
|
Chris@0
|
501 return $entity->hasField('content_translation_changed') ? 'content_translation_changed' : 'changed';
|
Chris@0
|
502 }
|
Chris@0
|
503
|
Chris@0
|
504 /**
|
Chris@0
|
505 * Tests edit content translation.
|
Chris@0
|
506 */
|
Chris@0
|
507 protected function doTestTranslationEdit() {
|
Chris@0
|
508 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
509 ->getStorage($this->entityTypeId);
|
Chris@0
|
510 $storage->resetCache([$this->entityId]);
|
Chris@0
|
511 $entity = $storage->load($this->entityId);
|
Chris@0
|
512 $languages = $this->container->get('language_manager')->getLanguages();
|
Chris@0
|
513
|
Chris@0
|
514 foreach ($this->langcodes as $langcode) {
|
Chris@0
|
515 // We only want to test the title for non-english translations.
|
Chris@0
|
516 if ($langcode != 'en') {
|
Chris@0
|
517 $options = ['language' => $languages[$langcode]];
|
Chris@18
|
518 $url = $entity->toUrl('edit-form', $options);
|
Chris@0
|
519 $this->drupalGet($url);
|
Chris@0
|
520
|
Chris@0
|
521 $this->assertRaw($entity->getTranslation($langcode)->label());
|
Chris@0
|
522 }
|
Chris@0
|
523 }
|
Chris@0
|
524 }
|
Chris@0
|
525
|
Chris@0
|
526 /**
|
Chris@0
|
527 * Tests the basic translation workflow.
|
Chris@0
|
528 */
|
Chris@0
|
529 protected function doTestTranslationChanged() {
|
Chris@0
|
530 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
531 ->getStorage($this->entityTypeId);
|
Chris@0
|
532 $storage->resetCache([$this->entityId]);
|
Chris@0
|
533 $entity = $storage->load($this->entityId);
|
Chris@0
|
534 $changed_field_name = $this->getChangedFieldName($entity);
|
Chris@0
|
535 $definition = $entity->getFieldDefinition($changed_field_name);
|
Chris@0
|
536 $config = $definition->getConfig($entity->bundle());
|
Chris@0
|
537
|
Chris@0
|
538 foreach ([FALSE, TRUE] as $translatable_changed_field) {
|
Chris@0
|
539 if ($definition->isTranslatable()) {
|
Chris@0
|
540 // For entities defining a translatable changed field we want to test
|
Chris@0
|
541 // the correct behavior of that field even if the translatability is
|
Chris@0
|
542 // revoked. In that case the changed timestamp should be synchronized
|
Chris@0
|
543 // across all translations.
|
Chris@0
|
544 $config->setTranslatable($translatable_changed_field);
|
Chris@0
|
545 $config->save();
|
Chris@0
|
546 }
|
Chris@0
|
547 elseif ($translatable_changed_field) {
|
Chris@0
|
548 // For entities defining a non-translatable changed field we cannot
|
Chris@0
|
549 // declare the field as translatable on the fly by modifying its config
|
Chris@0
|
550 // because the schema doesn't support this.
|
Chris@0
|
551 break;
|
Chris@0
|
552 }
|
Chris@0
|
553
|
Chris@0
|
554 foreach ($entity->getTranslationLanguages() as $language) {
|
Chris@0
|
555 // Ensure different timestamps.
|
Chris@0
|
556 sleep(1);
|
Chris@0
|
557
|
Chris@0
|
558 $langcode = $language->getId();
|
Chris@0
|
559
|
Chris@0
|
560 $edit = [
|
Chris@0
|
561 $this->fieldName . '[0][value]' => $this->randomString(),
|
Chris@0
|
562 ];
|
Chris@18
|
563 $edit_path = $entity->toUrl('edit-form', ['language' => $language]);
|
Chris@0
|
564 $this->drupalPostForm($edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
|
Chris@0
|
565
|
Chris@0
|
566 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
567 ->getStorage($this->entityTypeId);
|
Chris@0
|
568 $storage->resetCache([$this->entityId]);
|
Chris@0
|
569 $entity = $storage->load($this->entityId);
|
Chris@0
|
570 $this->assertEqual(
|
Chris@0
|
571 $entity->getChangedTimeAcrossTranslations(), $entity->getTranslation($langcode)->getChangedTime(),
|
Chris@0
|
572 format_string('Changed time for language %language is the latest change over all languages.', ['%language' => $language->getName()])
|
Chris@0
|
573 );
|
Chris@0
|
574 }
|
Chris@0
|
575
|
Chris@0
|
576 $timestamps = [];
|
Chris@0
|
577 foreach ($entity->getTranslationLanguages() as $language) {
|
Chris@0
|
578 $next_timestamp = $entity->getTranslation($language->getId())->getChangedTime();
|
Chris@0
|
579 if (!in_array($next_timestamp, $timestamps)) {
|
Chris@0
|
580 $timestamps[] = $next_timestamp;
|
Chris@0
|
581 }
|
Chris@0
|
582 }
|
Chris@0
|
583
|
Chris@0
|
584 if ($translatable_changed_field) {
|
Chris@0
|
585 $this->assertEqual(
|
Chris@0
|
586 count($timestamps), count($entity->getTranslationLanguages()),
|
Chris@0
|
587 'All timestamps from all languages are different.'
|
Chris@0
|
588 );
|
Chris@0
|
589 }
|
Chris@0
|
590 else {
|
Chris@0
|
591 $this->assertEqual(
|
Chris@0
|
592 count($timestamps), 1,
|
Chris@0
|
593 'All timestamps from all languages are identical.'
|
Chris@0
|
594 );
|
Chris@0
|
595 }
|
Chris@0
|
596 }
|
Chris@0
|
597 }
|
Chris@0
|
598
|
Chris@0
|
599 /**
|
Chris@0
|
600 * Test the changed time after API and FORM save without changes.
|
Chris@0
|
601 */
|
Chris@0
|
602 public function doTestChangedTimeAfterSaveWithoutChanges() {
|
Chris@0
|
603 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
604 ->getStorage($this->entityTypeId);
|
Chris@0
|
605 $storage->resetCache([$this->entityId]);
|
Chris@0
|
606 $entity = $storage->load($this->entityId);
|
Chris@0
|
607 // Test only entities, which implement the EntityChangedInterface.
|
Chris@0
|
608 if ($entity instanceof EntityChangedInterface) {
|
Chris@0
|
609 $changed_timestamp = $entity->getChangedTime();
|
Chris@0
|
610
|
Chris@0
|
611 $entity->save();
|
Chris@0
|
612 $storage = $this->container->get('entity_type.manager')
|
Chris@0
|
613 ->getStorage($this->entityTypeId);
|
Chris@0
|
614 $storage->resetCache([$this->entityId]);
|
Chris@0
|
615 $entity = $storage->load($this->entityId);
|
Chris@0
|
616 $this->assertEqual($changed_timestamp, $entity->getChangedTime(), 'The entity\'s changed time wasn\'t updated after API save without changes.');
|
Chris@0
|
617
|
Chris@0
|
618 // Ensure different save timestamps.
|
Chris@0
|
619 sleep(1);
|
Chris@0
|
620
|
Chris@0
|
621 // Save the entity on the regular edit form.
|
Chris@0
|
622 $language = $entity->language();
|
Chris@18
|
623 $edit_path = $entity->toUrl('edit-form', ['language' => $language]);
|
Chris@0
|
624 $this->drupalPostForm($edit_path, [], $this->getFormSubmitAction($entity, $language->getId()));
|
Chris@0
|
625
|
Chris@0
|
626 $storage->resetCache([$this->entityId]);
|
Chris@0
|
627 $entity = $storage->load($this->entityId);
|
Chris@0
|
628 $this->assertNotEqual($changed_timestamp, $entity->getChangedTime(), 'The entity\'s changed time was updated after form save without changes.');
|
Chris@0
|
629 }
|
Chris@0
|
630 }
|
Chris@0
|
631
|
Chris@0
|
632 }
|