Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\Tests\link\Functional;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Component\Utility\Html;
|
Chris@0
|
6 use Drupal\Component\Utility\Unicode;
|
Chris@0
|
7 use Drupal\Core\Url;
|
Chris@0
|
8 use Drupal\entity_test\Entity\EntityTest;
|
Chris@0
|
9 use Drupal\field\Entity\FieldConfig;
|
Chris@0
|
10 use Drupal\link\LinkItemInterface;
|
Chris@0
|
11 use Drupal\node\NodeInterface;
|
Chris@0
|
12 use Drupal\Tests\BrowserTestBase;
|
Chris@0
|
13 use Drupal\field\Entity\FieldStorageConfig;
|
Chris@0
|
14
|
Chris@0
|
15 /**
|
Chris@0
|
16 * Tests link field widgets and formatters.
|
Chris@0
|
17 *
|
Chris@0
|
18 * @group link
|
Chris@0
|
19 */
|
Chris@0
|
20 class LinkFieldTest extends BrowserTestBase {
|
Chris@0
|
21
|
Chris@0
|
22 /**
|
Chris@0
|
23 * Modules to enable.
|
Chris@0
|
24 *
|
Chris@0
|
25 * @var array
|
Chris@0
|
26 */
|
Chris@14
|
27 public static $modules = [
|
Chris@14
|
28 'entity_test',
|
Chris@14
|
29 'link',
|
Chris@14
|
30 'node',
|
Chris@14
|
31 'link_test_base_field',
|
Chris@14
|
32 ];
|
Chris@0
|
33
|
Chris@0
|
34 /**
|
Chris@0
|
35 * A field to use in this test class.
|
Chris@0
|
36 *
|
Chris@0
|
37 * @var \Drupal\field\Entity\FieldStorageConfig
|
Chris@0
|
38 */
|
Chris@0
|
39 protected $fieldStorage;
|
Chris@0
|
40
|
Chris@0
|
41 /**
|
Chris@0
|
42 * The instance used in this test class.
|
Chris@0
|
43 *
|
Chris@0
|
44 * @var \Drupal\field\Entity\FieldConfig
|
Chris@0
|
45 */
|
Chris@0
|
46 protected $field;
|
Chris@0
|
47
|
Chris@0
|
48 protected function setUp() {
|
Chris@0
|
49 parent::setUp();
|
Chris@0
|
50
|
Chris@0
|
51 $this->drupalLogin($this->drupalCreateUser([
|
Chris@0
|
52 'view test entity',
|
Chris@0
|
53 'administer entity_test content',
|
Chris@0
|
54 'link to any page',
|
Chris@0
|
55 ]));
|
Chris@0
|
56 }
|
Chris@0
|
57
|
Chris@0
|
58 /**
|
Chris@0
|
59 * Tests link field URL validation.
|
Chris@0
|
60 */
|
Chris@0
|
61 public function testURLValidation() {
|
Chris@0
|
62 $field_name = Unicode::strtolower($this->randomMachineName());
|
Chris@0
|
63 // Create a field with settings to validate.
|
Chris@0
|
64 $this->fieldStorage = FieldStorageConfig::create([
|
Chris@0
|
65 'field_name' => $field_name,
|
Chris@0
|
66 'entity_type' => 'entity_test',
|
Chris@0
|
67 'type' => 'link',
|
Chris@0
|
68 ]);
|
Chris@0
|
69 $this->fieldStorage->save();
|
Chris@0
|
70 $this->field = FieldConfig::create([
|
Chris@0
|
71 'field_storage' => $this->fieldStorage,
|
Chris@0
|
72 'bundle' => 'entity_test',
|
Chris@0
|
73 'settings' => [
|
Chris@0
|
74 'title' => DRUPAL_DISABLED,
|
Chris@0
|
75 'link_type' => LinkItemInterface::LINK_GENERIC,
|
Chris@0
|
76 ],
|
Chris@0
|
77 ]);
|
Chris@0
|
78 $this->field->save();
|
Chris@0
|
79 entity_get_form_display('entity_test', 'entity_test', 'default')
|
Chris@0
|
80 ->setComponent($field_name, [
|
Chris@0
|
81 'type' => 'link_default',
|
Chris@0
|
82 'settings' => [
|
Chris@0
|
83 'placeholder_url' => 'http://example.com',
|
Chris@0
|
84 ],
|
Chris@0
|
85 ])
|
Chris@0
|
86 ->save();
|
Chris@0
|
87 entity_get_display('entity_test', 'entity_test', 'full')
|
Chris@0
|
88 ->setComponent($field_name, [
|
Chris@0
|
89 'type' => 'link',
|
Chris@0
|
90 ])
|
Chris@0
|
91 ->save();
|
Chris@0
|
92
|
Chris@0
|
93 // Display creation form.
|
Chris@0
|
94 $this->drupalGet('entity_test/add');
|
Chris@0
|
95 $this->assertFieldByName("{$field_name}[0][uri]", '', 'Link URL field is displayed');
|
Chris@0
|
96 $this->assertRaw('placeholder="http://example.com"');
|
Chris@0
|
97
|
Chris@0
|
98 // Create a path alias.
|
Chris@0
|
99 \Drupal::service('path.alias_storage')->save('/admin', '/a/path/alias');
|
Chris@0
|
100
|
Chris@0
|
101 // Create a node to test the link widget.
|
Chris@0
|
102 $node = $this->drupalCreateNode();
|
Chris@0
|
103
|
Chris@0
|
104 $restricted_node = $this->drupalCreateNode(['status' => NodeInterface::NOT_PUBLISHED]);
|
Chris@0
|
105
|
Chris@0
|
106 // Define some valid URLs (keys are the entered values, values are the
|
Chris@0
|
107 // strings displayed to the user).
|
Chris@0
|
108 $valid_external_entries = [
|
Chris@0
|
109 'http://www.example.com/' => 'http://www.example.com/',
|
Chris@0
|
110 // Strings within parenthesis without leading space char.
|
Chris@0
|
111 'http://www.example.com/strings_(string_within_parenthesis)' => 'http://www.example.com/strings_(string_within_parenthesis)',
|
Chris@0
|
112 // Numbers within parenthesis without leading space char.
|
Chris@0
|
113 'http://www.example.com/numbers_(9999)' => 'http://www.example.com/numbers_(9999)',
|
Chris@0
|
114 ];
|
Chris@0
|
115 $valid_internal_entries = [
|
Chris@0
|
116 '/entity_test/add' => '/entity_test/add',
|
Chris@0
|
117 '/a/path/alias' => '/a/path/alias',
|
Chris@0
|
118
|
Chris@0
|
119 // Front page, with query string and fragment.
|
Chris@0
|
120 '/' => '<front>',
|
Chris@0
|
121 '/?example=llama' => '<front>?example=llama',
|
Chris@0
|
122 '/#example' => '<front>#example',
|
Chris@0
|
123
|
Chris@0
|
124 // @todo '<front>' is valid input for BC reasons, may be removed by
|
Chris@0
|
125 // https://www.drupal.org/node/2421941
|
Chris@0
|
126 '<front>' => '<front>',
|
Chris@0
|
127 '<front>#example' => '<front>#example',
|
Chris@0
|
128 '<front>?example=llama' => '<front>?example=llama',
|
Chris@0
|
129
|
Chris@0
|
130 // Query string and fragment.
|
Chris@0
|
131 '?example=llama' => '?example=llama',
|
Chris@0
|
132 '#example' => '#example',
|
Chris@0
|
133
|
Chris@0
|
134 // Entity reference autocomplete value.
|
Chris@0
|
135 $node->label() . ' (1)' => $node->label() . ' (1)',
|
Chris@0
|
136 // Entity URI displayed as ER autocomplete value when displayed in a form.
|
Chris@0
|
137 'entity:node/1' => $node->label() . ' (1)',
|
Chris@0
|
138 // URI for an entity that exists, but is not accessible by the user.
|
Chris@0
|
139 'entity:node/' . $restricted_node->id() => '- Restricted access - (' . $restricted_node->id() . ')',
|
Chris@0
|
140 // URI for an entity that doesn't exist, but with a valid ID.
|
Chris@0
|
141 'entity:user/999999' => 'entity:user/999999',
|
Chris@0
|
142 ];
|
Chris@0
|
143
|
Chris@0
|
144 // Define some invalid URLs.
|
Chris@0
|
145 $validation_error_1 = "The path '@link_path' is invalid.";
|
Chris@0
|
146 $validation_error_2 = 'Manually entered paths should start with /, ? or #.';
|
Chris@0
|
147 $validation_error_3 = "The path '@link_path' is inaccessible.";
|
Chris@0
|
148 $invalid_external_entries = [
|
Chris@0
|
149 // Invalid protocol
|
Chris@0
|
150 'invalid://not-a-valid-protocol' => $validation_error_1,
|
Chris@0
|
151 // Missing host name
|
Chris@0
|
152 'http://' => $validation_error_1,
|
Chris@0
|
153 ];
|
Chris@0
|
154 $invalid_internal_entries = [
|
Chris@0
|
155 'no-leading-slash' => $validation_error_2,
|
Chris@0
|
156 'entity:non_existing_entity_type/yar' => $validation_error_1,
|
Chris@0
|
157 // URI for an entity that doesn't exist, with an invalid ID.
|
Chris@0
|
158 'entity:user/invalid-parameter' => $validation_error_1,
|
Chris@0
|
159 ];
|
Chris@0
|
160
|
Chris@0
|
161 // Test external and internal URLs for 'link_type' = LinkItemInterface::LINK_GENERIC.
|
Chris@0
|
162 $this->assertValidEntries($field_name, $valid_external_entries + $valid_internal_entries);
|
Chris@0
|
163 $this->assertInvalidEntries($field_name, $invalid_external_entries + $invalid_internal_entries);
|
Chris@0
|
164
|
Chris@0
|
165 // Test external URLs for 'link_type' = LinkItemInterface::LINK_EXTERNAL.
|
Chris@0
|
166 $this->field->setSetting('link_type', LinkItemInterface::LINK_EXTERNAL);
|
Chris@0
|
167 $this->field->save();
|
Chris@0
|
168 $this->assertValidEntries($field_name, $valid_external_entries);
|
Chris@0
|
169 $this->assertInvalidEntries($field_name, $valid_internal_entries + $invalid_external_entries);
|
Chris@0
|
170
|
Chris@0
|
171 // Test external URLs for 'link_type' = LinkItemInterface::LINK_INTERNAL.
|
Chris@0
|
172 $this->field->setSetting('link_type', LinkItemInterface::LINK_INTERNAL);
|
Chris@0
|
173 $this->field->save();
|
Chris@0
|
174 $this->assertValidEntries($field_name, $valid_internal_entries);
|
Chris@0
|
175 $this->assertInvalidEntries($field_name, $valid_external_entries + $invalid_internal_entries);
|
Chris@0
|
176
|
Chris@0
|
177 // Ensure that users with 'link to any page', don't apply access checking.
|
Chris@0
|
178 $this->drupalLogin($this->drupalCreateUser([
|
Chris@0
|
179 'view test entity',
|
Chris@0
|
180 'administer entity_test content',
|
Chris@0
|
181 ]));
|
Chris@0
|
182 $this->assertValidEntries($field_name, ['/entity_test/add' => '/entity_test/add']);
|
Chris@0
|
183 $this->assertInValidEntries($field_name, ['/admin' => $validation_error_3]);
|
Chris@0
|
184 }
|
Chris@0
|
185
|
Chris@0
|
186 /**
|
Chris@0
|
187 * Asserts that valid URLs can be submitted.
|
Chris@0
|
188 *
|
Chris@0
|
189 * @param string $field_name
|
Chris@0
|
190 * The field name.
|
Chris@0
|
191 * @param array $valid_entries
|
Chris@0
|
192 * An array of valid URL entries.
|
Chris@0
|
193 */
|
Chris@0
|
194 protected function assertValidEntries($field_name, array $valid_entries) {
|
Chris@0
|
195 foreach ($valid_entries as $uri => $string) {
|
Chris@0
|
196 $edit = [
|
Chris@0
|
197 "{$field_name}[0][uri]" => $uri,
|
Chris@0
|
198 ];
|
Chris@0
|
199 $this->drupalPostForm('entity_test/add', $edit, t('Save'));
|
Chris@0
|
200 preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
|
Chris@0
|
201 $id = $match[1];
|
Chris@0
|
202 $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
|
Chris@0
|
203 $this->assertRaw($string);
|
Chris@0
|
204 }
|
Chris@0
|
205 }
|
Chris@0
|
206
|
Chris@0
|
207 /**
|
Chris@0
|
208 * Asserts that invalid URLs cannot be submitted.
|
Chris@0
|
209 *
|
Chris@0
|
210 * @param string $field_name
|
Chris@0
|
211 * The field name.
|
Chris@0
|
212 * @param array $invalid_entries
|
Chris@0
|
213 * An array of invalid URL entries.
|
Chris@0
|
214 */
|
Chris@0
|
215 protected function assertInvalidEntries($field_name, array $invalid_entries) {
|
Chris@0
|
216 foreach ($invalid_entries as $invalid_value => $error_message) {
|
Chris@0
|
217 $edit = [
|
Chris@0
|
218 "{$field_name}[0][uri]" => $invalid_value,
|
Chris@0
|
219 ];
|
Chris@0
|
220 $this->drupalPostForm('entity_test/add', $edit, t('Save'));
|
Chris@0
|
221 $this->assertText(t($error_message, ['@link_path' => $invalid_value]));
|
Chris@0
|
222 }
|
Chris@0
|
223 }
|
Chris@0
|
224
|
Chris@0
|
225 /**
|
Chris@0
|
226 * Tests the link title settings of a link field.
|
Chris@0
|
227 */
|
Chris@0
|
228 public function testLinkTitle() {
|
Chris@0
|
229 $field_name = Unicode::strtolower($this->randomMachineName());
|
Chris@0
|
230 // Create a field with settings to validate.
|
Chris@0
|
231 $this->fieldStorage = FieldStorageConfig::create([
|
Chris@0
|
232 'field_name' => $field_name,
|
Chris@0
|
233 'entity_type' => 'entity_test',
|
Chris@0
|
234 'type' => 'link',
|
Chris@0
|
235 ]);
|
Chris@0
|
236 $this->fieldStorage->save();
|
Chris@0
|
237 $this->field = FieldConfig::create([
|
Chris@0
|
238 'field_storage' => $this->fieldStorage,
|
Chris@0
|
239 'bundle' => 'entity_test',
|
Chris@0
|
240 'label' => 'Read more about this entity',
|
Chris@0
|
241 'settings' => [
|
Chris@0
|
242 'title' => DRUPAL_OPTIONAL,
|
Chris@0
|
243 'link_type' => LinkItemInterface::LINK_GENERIC,
|
Chris@0
|
244 ],
|
Chris@0
|
245 ]);
|
Chris@0
|
246 $this->field->save();
|
Chris@0
|
247 entity_get_form_display('entity_test', 'entity_test', 'default')
|
Chris@0
|
248 ->setComponent($field_name, [
|
Chris@0
|
249 'type' => 'link_default',
|
Chris@0
|
250 'settings' => [
|
Chris@0
|
251 'placeholder_url' => 'http://example.com',
|
Chris@0
|
252 'placeholder_title' => 'Enter the text for this link',
|
Chris@0
|
253 ],
|
Chris@0
|
254 ])
|
Chris@0
|
255 ->save();
|
Chris@0
|
256 entity_get_display('entity_test', 'entity_test', 'full')
|
Chris@0
|
257 ->setComponent($field_name, [
|
Chris@0
|
258 'type' => 'link',
|
Chris@0
|
259 'label' => 'hidden',
|
Chris@0
|
260 ])
|
Chris@0
|
261 ->save();
|
Chris@0
|
262
|
Chris@0
|
263 // Verify that the link text field works according to the field setting.
|
Chris@0
|
264 foreach ([DRUPAL_DISABLED, DRUPAL_REQUIRED, DRUPAL_OPTIONAL] as $title_setting) {
|
Chris@0
|
265 // Update the link title field setting.
|
Chris@0
|
266 $this->field->setSetting('title', $title_setting);
|
Chris@0
|
267 $this->field->save();
|
Chris@0
|
268
|
Chris@0
|
269 // Display creation form.
|
Chris@0
|
270 $this->drupalGet('entity_test/add');
|
Chris@0
|
271 // Assert label is shown.
|
Chris@0
|
272 $this->assertText('Read more about this entity');
|
Chris@0
|
273 $this->assertFieldByName("{$field_name}[0][uri]", '', 'URL field found.');
|
Chris@0
|
274 $this->assertRaw('placeholder="http://example.com"');
|
Chris@0
|
275
|
Chris@0
|
276 if ($title_setting === DRUPAL_DISABLED) {
|
Chris@0
|
277 $this->assertNoFieldByName("{$field_name}[0][title]", '', 'Link text field not found.');
|
Chris@0
|
278 $this->assertNoRaw('placeholder="Enter the text for this link"');
|
Chris@0
|
279 }
|
Chris@0
|
280 else {
|
Chris@0
|
281 $this->assertRaw('placeholder="Enter the text for this link"');
|
Chris@0
|
282
|
Chris@0
|
283 $this->assertFieldByName("{$field_name}[0][title]", '', 'Link text field found.');
|
Chris@0
|
284 if ($title_setting === DRUPAL_REQUIRED) {
|
Chris@0
|
285 // Verify that the link text is required, if the URL is non-empty.
|
Chris@0
|
286 $edit = [
|
Chris@0
|
287 "{$field_name}[0][uri]" => 'http://www.example.com',
|
Chris@0
|
288 ];
|
Chris@0
|
289 $this->drupalPostForm(NULL, $edit, t('Save'));
|
Chris@14
|
290 $this->assertText(t('@title field is required if there is @uri input.', ['@title' => t('Link text'), '@uri' => t('URL')]));
|
Chris@0
|
291
|
Chris@0
|
292 // Verify that the link text is not required, if the URL is empty.
|
Chris@0
|
293 $edit = [
|
Chris@0
|
294 "{$field_name}[0][uri]" => '',
|
Chris@0
|
295 ];
|
Chris@0
|
296 $this->drupalPostForm(NULL, $edit, t('Save'));
|
Chris@0
|
297 $this->assertNoText(t('@name field is required.', ['@name' => t('Link text')]));
|
Chris@0
|
298
|
Chris@0
|
299 // Verify that a URL and link text meets requirements.
|
Chris@0
|
300 $this->drupalGet('entity_test/add');
|
Chris@0
|
301 $edit = [
|
Chris@0
|
302 "{$field_name}[0][uri]" => 'http://www.example.com',
|
Chris@0
|
303 "{$field_name}[0][title]" => 'Example',
|
Chris@0
|
304 ];
|
Chris@0
|
305 $this->drupalPostForm(NULL, $edit, t('Save'));
|
Chris@0
|
306 $this->assertNoText(t('@name field is required.', ['@name' => t('Link text')]));
|
Chris@0
|
307 }
|
Chris@0
|
308 }
|
Chris@0
|
309 }
|
Chris@0
|
310
|
Chris@0
|
311 // Verify that a link without link text is rendered using the URL as text.
|
Chris@0
|
312 $value = 'http://www.example.com/';
|
Chris@0
|
313 $edit = [
|
Chris@0
|
314 "{$field_name}[0][uri]" => $value,
|
Chris@0
|
315 "{$field_name}[0][title]" => '',
|
Chris@0
|
316 ];
|
Chris@0
|
317 $this->drupalPostForm(NULL, $edit, t('Save'));
|
Chris@0
|
318 preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
|
Chris@0
|
319 $id = $match[1];
|
Chris@0
|
320 $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
|
Chris@0
|
321
|
Chris@0
|
322 $output = $this->renderTestEntity($id);
|
Chris@0
|
323 $expected_link = (string) \Drupal::l($value, Url::fromUri($value));
|
Chris@0
|
324 $this->assertContains($expected_link, $output);
|
Chris@0
|
325
|
Chris@0
|
326 // Verify that a link with text is rendered using the link text.
|
Chris@0
|
327 $title = $this->randomMachineName();
|
Chris@0
|
328 $edit = [
|
Chris@0
|
329 "{$field_name}[0][title]" => $title,
|
Chris@0
|
330 ];
|
Chris@0
|
331 $this->drupalPostForm("entity_test/manage/$id/edit", $edit, t('Save'));
|
Chris@0
|
332 $this->assertText(t('entity_test @id has been updated.', ['@id' => $id]));
|
Chris@0
|
333
|
Chris@0
|
334 $output = $this->renderTestEntity($id);
|
Chris@0
|
335 $expected_link = (string) \Drupal::l($title, Url::fromUri($value));
|
Chris@0
|
336 $this->assertContains($expected_link, $output);
|
Chris@0
|
337 }
|
Chris@0
|
338
|
Chris@0
|
339 /**
|
Chris@0
|
340 * Tests the default 'link' formatter.
|
Chris@0
|
341 */
|
Chris@0
|
342 public function testLinkFormatter() {
|
Chris@0
|
343 $field_name = Unicode::strtolower($this->randomMachineName());
|
Chris@0
|
344 // Create a field with settings to validate.
|
Chris@0
|
345 $this->fieldStorage = FieldStorageConfig::create([
|
Chris@0
|
346 'field_name' => $field_name,
|
Chris@0
|
347 'entity_type' => 'entity_test',
|
Chris@0
|
348 'type' => 'link',
|
Chris@0
|
349 'cardinality' => 3,
|
Chris@0
|
350 ]);
|
Chris@0
|
351 $this->fieldStorage->save();
|
Chris@0
|
352 FieldConfig::create([
|
Chris@0
|
353 'field_storage' => $this->fieldStorage,
|
Chris@0
|
354 'label' => 'Read more about this entity',
|
Chris@0
|
355 'bundle' => 'entity_test',
|
Chris@0
|
356 'settings' => [
|
Chris@0
|
357 'title' => DRUPAL_OPTIONAL,
|
Chris@0
|
358 'link_type' => LinkItemInterface::LINK_GENERIC,
|
Chris@0
|
359 ],
|
Chris@0
|
360 ])->save();
|
Chris@0
|
361 entity_get_form_display('entity_test', 'entity_test', 'default')
|
Chris@0
|
362 ->setComponent($field_name, [
|
Chris@0
|
363 'type' => 'link_default',
|
Chris@0
|
364 ])
|
Chris@0
|
365 ->save();
|
Chris@0
|
366 $display_options = [
|
Chris@0
|
367 'type' => 'link',
|
Chris@0
|
368 'label' => 'hidden',
|
Chris@0
|
369 ];
|
Chris@0
|
370 entity_get_display('entity_test', 'entity_test', 'full')
|
Chris@0
|
371 ->setComponent($field_name, $display_options)
|
Chris@0
|
372 ->save();
|
Chris@0
|
373
|
Chris@0
|
374 // Create an entity with three link field values:
|
Chris@0
|
375 // - The first field item uses a URL only.
|
Chris@0
|
376 // - The second field item uses a URL and link text.
|
Chris@0
|
377 // - The third field item uses a fragment-only URL with text.
|
Chris@0
|
378 // For consistency in assertion code below, the URL is assigned to the title
|
Chris@0
|
379 // variable for the first field.
|
Chris@0
|
380 $this->drupalGet('entity_test/add');
|
Chris@0
|
381 $url1 = 'http://www.example.com/content/articles/archive?author=John&year=2012#com';
|
Chris@0
|
382 $url2 = 'http://www.example.org/content/articles/archive?author=John&year=2012#org';
|
Chris@0
|
383 $url3 = '#net';
|
Chris@0
|
384 $title1 = $url1;
|
Chris@0
|
385 // Intentionally contains an ampersand that needs sanitization on output.
|
Chris@0
|
386 $title2 = 'A very long & strange example title that could break the nice layout of the site';
|
Chris@0
|
387 $title3 = 'Fragment only';
|
Chris@0
|
388 $edit = [
|
Chris@0
|
389 "{$field_name}[0][uri]" => $url1,
|
Chris@0
|
390 // Note that $title1 is not submitted.
|
Chris@0
|
391 "{$field_name}[0][title]" => '',
|
Chris@0
|
392 "{$field_name}[1][uri]" => $url2,
|
Chris@0
|
393 "{$field_name}[1][title]" => $title2,
|
Chris@0
|
394 "{$field_name}[2][uri]" => $url3,
|
Chris@0
|
395 "{$field_name}[2][title]" => $title3,
|
Chris@0
|
396 ];
|
Chris@0
|
397 // Assert label is shown.
|
Chris@0
|
398 $this->assertText('Read more about this entity');
|
Chris@0
|
399 $this->drupalPostForm(NULL, $edit, t('Save'));
|
Chris@0
|
400 preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
|
Chris@0
|
401 $id = $match[1];
|
Chris@0
|
402 $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
|
Chris@0
|
403
|
Chris@0
|
404 // Verify that the link is output according to the formatter settings.
|
Chris@0
|
405 // Not using generatePermutations(), since that leads to 32 cases, which
|
Chris@0
|
406 // would not test actual link field formatter functionality but rather
|
Chris@0
|
407 // the link generator and options/attributes. Only 'url_plain' has a
|
Chris@0
|
408 // dependency on 'url_only', so we have a total of ~10 cases.
|
Chris@0
|
409 $options = [
|
Chris@0
|
410 'trim_length' => [NULL, 6],
|
Chris@0
|
411 'rel' => [NULL, 'nofollow'],
|
Chris@0
|
412 'target' => [NULL, '_blank'],
|
Chris@0
|
413 'url_only' => [
|
Chris@0
|
414 ['url_only' => FALSE],
|
Chris@0
|
415 ['url_only' => FALSE, 'url_plain' => TRUE],
|
Chris@0
|
416 ['url_only' => TRUE],
|
Chris@0
|
417 ['url_only' => TRUE, 'url_plain' => TRUE],
|
Chris@0
|
418 ],
|
Chris@0
|
419 ];
|
Chris@0
|
420 foreach ($options as $setting => $values) {
|
Chris@0
|
421 foreach ($values as $new_value) {
|
Chris@0
|
422 // Update the field formatter settings.
|
Chris@0
|
423 if (!is_array($new_value)) {
|
Chris@0
|
424 $display_options['settings'] = [$setting => $new_value];
|
Chris@0
|
425 }
|
Chris@0
|
426 else {
|
Chris@0
|
427 $display_options['settings'] = $new_value;
|
Chris@0
|
428 }
|
Chris@0
|
429 entity_get_display('entity_test', 'entity_test', 'full')
|
Chris@0
|
430 ->setComponent($field_name, $display_options)
|
Chris@0
|
431 ->save();
|
Chris@0
|
432
|
Chris@0
|
433 $output = $this->renderTestEntity($id);
|
Chris@0
|
434 switch ($setting) {
|
Chris@0
|
435 case 'trim_length':
|
Chris@0
|
436 $url = $url1;
|
Chris@0
|
437 $title = isset($new_value) ? Unicode::truncate($title1, $new_value, FALSE, TRUE) : $title1;
|
Chris@0
|
438 $this->assertContains('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>', $output);
|
Chris@0
|
439
|
Chris@0
|
440 $url = $url2;
|
Chris@0
|
441 $title = isset($new_value) ? Unicode::truncate($title2, $new_value, FALSE, TRUE) : $title2;
|
Chris@0
|
442 $this->assertContains('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>', $output);
|
Chris@0
|
443
|
Chris@0
|
444 $url = $url3;
|
Chris@0
|
445 $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3;
|
Chris@0
|
446 $this->assertContains('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>', $output);
|
Chris@0
|
447 break;
|
Chris@0
|
448
|
Chris@0
|
449 case 'rel':
|
Chris@0
|
450 $rel = isset($new_value) ? ' rel="' . $new_value . '"' : '';
|
Chris@0
|
451 $this->assertContains('<a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($title1) . '</a>', $output);
|
Chris@0
|
452 $this->assertContains('<a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($title2) . '</a>', $output);
|
Chris@0
|
453 $this->assertContains('<a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($title3) . '</a>', $output);
|
Chris@0
|
454 break;
|
Chris@0
|
455
|
Chris@0
|
456 case 'target':
|
Chris@0
|
457 $target = isset($new_value) ? ' target="' . $new_value . '"' : '';
|
Chris@0
|
458 $this->assertContains('<a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($title1) . '</a>', $output);
|
Chris@0
|
459 $this->assertContains('<a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($title2) . '</a>', $output);
|
Chris@0
|
460 $this->assertContains('<a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($title3) . '</a>', $output);
|
Chris@0
|
461 break;
|
Chris@0
|
462
|
Chris@0
|
463 case 'url_only':
|
Chris@0
|
464 // In this case, $new_value is an array.
|
Chris@0
|
465 if (!$new_value['url_only']) {
|
Chris@0
|
466 $this->assertContains('<a href="' . Html::escape($url1) . '">' . Html::escape($title1) . '</a>', $output);
|
Chris@0
|
467 $this->assertContains('<a href="' . Html::escape($url2) . '">' . Html::escape($title2) . '</a>', $output);
|
Chris@0
|
468 $this->assertContains('<a href="' . Html::escape($url3) . '">' . Html::escape($title3) . '</a>', $output);
|
Chris@0
|
469 }
|
Chris@0
|
470 else {
|
Chris@0
|
471 if (empty($new_value['url_plain'])) {
|
Chris@0
|
472 $this->assertContains('<a href="' . Html::escape($url1) . '">' . Html::escape($url1) . '</a>', $output);
|
Chris@0
|
473 $this->assertContains('<a href="' . Html::escape($url2) . '">' . Html::escape($url2) . '</a>', $output);
|
Chris@0
|
474 $this->assertContains('<a href="' . Html::escape($url3) . '">' . Html::escape($url3) . '</a>', $output);
|
Chris@0
|
475 }
|
Chris@0
|
476 else {
|
Chris@0
|
477 $this->assertNotContains('<a href="' . Html::escape($url1) . '">' . Html::escape($url1) . '</a>', $output);
|
Chris@0
|
478 $this->assertNotContains('<a href="' . Html::escape($url2) . '">' . Html::escape($url2) . '</a>', $output);
|
Chris@0
|
479 $this->assertNotContains('<a href="' . Html::escape($url3) . '">' . Html::escape($url3) . '</a>', $output);
|
Chris@0
|
480 $this->assertContains(Html::escape($url1), $output);
|
Chris@0
|
481 $this->assertContains(Html::escape($url2), $output);
|
Chris@0
|
482 $this->assertContains(Html::escape($url3), $output);
|
Chris@0
|
483 }
|
Chris@0
|
484 }
|
Chris@0
|
485 break;
|
Chris@0
|
486 }
|
Chris@0
|
487 }
|
Chris@0
|
488 }
|
Chris@0
|
489 }
|
Chris@0
|
490
|
Chris@0
|
491 /**
|
Chris@0
|
492 * Tests the 'link_separate' formatter.
|
Chris@0
|
493 *
|
Chris@0
|
494 * This test is mostly the same as testLinkFormatter(), but they cannot be
|
Chris@0
|
495 * merged, since they involve different configuration and output.
|
Chris@0
|
496 */
|
Chris@0
|
497 public function testLinkSeparateFormatter() {
|
Chris@0
|
498 $field_name = Unicode::strtolower($this->randomMachineName());
|
Chris@0
|
499 // Create a field with settings to validate.
|
Chris@0
|
500 $this->fieldStorage = FieldStorageConfig::create([
|
Chris@0
|
501 'field_name' => $field_name,
|
Chris@0
|
502 'entity_type' => 'entity_test',
|
Chris@0
|
503 'type' => 'link',
|
Chris@0
|
504 'cardinality' => 3,
|
Chris@0
|
505 ]);
|
Chris@0
|
506 $this->fieldStorage->save();
|
Chris@0
|
507 FieldConfig::create([
|
Chris@0
|
508 'field_storage' => $this->fieldStorage,
|
Chris@0
|
509 'bundle' => 'entity_test',
|
Chris@0
|
510 'settings' => [
|
Chris@0
|
511 'title' => DRUPAL_OPTIONAL,
|
Chris@0
|
512 'link_type' => LinkItemInterface::LINK_GENERIC,
|
Chris@0
|
513 ],
|
Chris@0
|
514 ])->save();
|
Chris@0
|
515 $display_options = [
|
Chris@0
|
516 'type' => 'link_separate',
|
Chris@0
|
517 'label' => 'hidden',
|
Chris@0
|
518 ];
|
Chris@0
|
519 entity_get_form_display('entity_test', 'entity_test', 'default')
|
Chris@0
|
520 ->setComponent($field_name, [
|
Chris@0
|
521 'type' => 'link_default',
|
Chris@0
|
522 ])
|
Chris@0
|
523 ->save();
|
Chris@0
|
524 entity_get_display('entity_test', 'entity_test', 'full')
|
Chris@0
|
525 ->setComponent($field_name, $display_options)
|
Chris@0
|
526 ->save();
|
Chris@0
|
527
|
Chris@0
|
528 // Create an entity with three link field values:
|
Chris@0
|
529 // - The first field item uses a URL only.
|
Chris@0
|
530 // - The second field item uses a URL and link text.
|
Chris@0
|
531 // - The third field item uses a fragment-only URL with text.
|
Chris@0
|
532 // For consistency in assertion code below, the URL is assigned to the title
|
Chris@0
|
533 // variable for the first field.
|
Chris@0
|
534 $this->drupalGet('entity_test/add');
|
Chris@0
|
535 $url1 = 'http://www.example.com/content/articles/archive?author=John&year=2012#com';
|
Chris@0
|
536 $url2 = 'http://www.example.org/content/articles/archive?author=John&year=2012#org';
|
Chris@0
|
537 $url3 = '#net';
|
Chris@0
|
538 // Intentionally contains an ampersand that needs sanitization on output.
|
Chris@0
|
539 $title2 = 'A very long & strange example title that could break the nice layout of the site';
|
Chris@0
|
540 $title3 = 'Fragment only';
|
Chris@0
|
541 $edit = [
|
Chris@0
|
542 "{$field_name}[0][uri]" => $url1,
|
Chris@0
|
543 "{$field_name}[1][uri]" => $url2,
|
Chris@0
|
544 "{$field_name}[1][title]" => $title2,
|
Chris@0
|
545 "{$field_name}[2][uri]" => $url3,
|
Chris@0
|
546 "{$field_name}[2][title]" => $title3,
|
Chris@0
|
547 ];
|
Chris@0
|
548 $this->drupalPostForm(NULL, $edit, t('Save'));
|
Chris@0
|
549 preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
|
Chris@0
|
550 $id = $match[1];
|
Chris@0
|
551 $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
|
Chris@0
|
552
|
Chris@0
|
553 // Verify that the link is output according to the formatter settings.
|
Chris@0
|
554 $options = [
|
Chris@0
|
555 'trim_length' => [NULL, 6],
|
Chris@0
|
556 'rel' => [NULL, 'nofollow'],
|
Chris@0
|
557 'target' => [NULL, '_blank'],
|
Chris@0
|
558 ];
|
Chris@0
|
559 foreach ($options as $setting => $values) {
|
Chris@0
|
560 foreach ($values as $new_value) {
|
Chris@0
|
561 // Update the field formatter settings.
|
Chris@0
|
562 $display_options['settings'] = [$setting => $new_value];
|
Chris@0
|
563 entity_get_display('entity_test', 'entity_test', 'full')
|
Chris@0
|
564 ->setComponent($field_name, $display_options)
|
Chris@0
|
565 ->save();
|
Chris@0
|
566
|
Chris@0
|
567 $output = $this->renderTestEntity($id);
|
Chris@0
|
568 switch ($setting) {
|
Chris@0
|
569 case 'trim_length':
|
Chris@0
|
570 $url = $url1;
|
Chris@0
|
571 $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
|
Chris@0
|
572 $expected = '<div class="link-item">';
|
Chris@0
|
573 $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
|
Chris@0
|
574 $expected .= '</div>';
|
Chris@0
|
575 $this->assertContains($expected, $output);
|
Chris@0
|
576
|
Chris@0
|
577 $url = $url2;
|
Chris@0
|
578 $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
|
Chris@0
|
579 $title = isset($new_value) ? Unicode::truncate($title2, $new_value, FALSE, TRUE) : $title2;
|
Chris@0
|
580 $expected = '<div class="link-item">';
|
Chris@0
|
581 $expected .= '<div class="link-title">' . Html::escape($title) . '</div>';
|
Chris@0
|
582 $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
|
Chris@0
|
583 $expected .= '</div>';
|
Chris@0
|
584 $this->assertContains($expected, $output);
|
Chris@0
|
585
|
Chris@0
|
586 $url = $url3;
|
Chris@0
|
587 $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
|
Chris@0
|
588 $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3;
|
Chris@0
|
589 $expected = '<div class="link-item">';
|
Chris@0
|
590 $expected .= '<div class="link-title">' . Html::escape($title) . '</div>';
|
Chris@0
|
591 $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
|
Chris@0
|
592 $expected .= '</div>';
|
Chris@0
|
593 $this->assertContains($expected, $output);
|
Chris@0
|
594 break;
|
Chris@0
|
595
|
Chris@0
|
596 case 'rel':
|
Chris@0
|
597 $rel = isset($new_value) ? ' rel="' . $new_value . '"' : '';
|
Chris@0
|
598 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($url1) . '</a></div>', $output);
|
Chris@0
|
599 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($url2) . '</a></div>', $output);
|
Chris@0
|
600 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($url3) . '</a></div>', $output);
|
Chris@0
|
601 break;
|
Chris@0
|
602
|
Chris@0
|
603 case 'target':
|
Chris@0
|
604 $target = isset($new_value) ? ' target="' . $new_value . '"' : '';
|
Chris@0
|
605 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($url1) . '</a></div>', $output);
|
Chris@0
|
606 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($url2) . '</a></div>', $output);
|
Chris@0
|
607 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($url3) . '</a></div>', $output);
|
Chris@0
|
608 break;
|
Chris@0
|
609 }
|
Chris@0
|
610 }
|
Chris@0
|
611 }
|
Chris@0
|
612 }
|
Chris@0
|
613
|
Chris@0
|
614 /**
|
Chris@0
|
615 * Test '#link_type' property exists on 'link_default' widget.
|
Chris@0
|
616 *
|
Chris@0
|
617 * Make sure the 'link_default' widget exposes a '#link_type' property on
|
Chris@0
|
618 * its element. Modules can use it to understand if a text form element is
|
Chris@0
|
619 * a link and also which LinkItemInterface::LINK_* is (EXTERNAL, GENERIC,
|
Chris@0
|
620 * INTERNAL).
|
Chris@0
|
621 */
|
Chris@0
|
622 public function testLinkTypeOnLinkWidget() {
|
Chris@0
|
623
|
Chris@0
|
624 $link_type = LinkItemInterface::LINK_EXTERNAL;
|
Chris@0
|
625 $field_name = Unicode::strtolower($this->randomMachineName());
|
Chris@0
|
626
|
Chris@0
|
627 // Create a field with settings to validate.
|
Chris@0
|
628 $this->fieldStorage = FieldStorageConfig::create([
|
Chris@0
|
629 'field_name' => $field_name,
|
Chris@0
|
630 'entity_type' => 'entity_test',
|
Chris@0
|
631 'type' => 'link',
|
Chris@0
|
632 'cardinality' => 1,
|
Chris@0
|
633 ]);
|
Chris@0
|
634 $this->fieldStorage->save();
|
Chris@0
|
635 FieldConfig::create([
|
Chris@0
|
636 'field_storage' => $this->fieldStorage,
|
Chris@0
|
637 'label' => 'Read more about this entity',
|
Chris@0
|
638 'bundle' => 'entity_test',
|
Chris@0
|
639 'settings' => [
|
Chris@0
|
640 'title' => DRUPAL_OPTIONAL,
|
Chris@0
|
641 'link_type' => $link_type,
|
Chris@0
|
642 ],
|
Chris@0
|
643 ])->save();
|
Chris@0
|
644
|
Chris@0
|
645 $this->container->get('entity.manager')
|
Chris@0
|
646 ->getStorage('entity_form_display')
|
Chris@0
|
647 ->load('entity_test.entity_test.default')
|
Chris@0
|
648 ->setComponent($field_name, [
|
Chris@0
|
649 'type' => 'link_default',
|
Chris@0
|
650 ])
|
Chris@0
|
651 ->save();
|
Chris@0
|
652
|
Chris@0
|
653 $form = \Drupal::service('entity.form_builder')->getForm(EntityTest::create());
|
Chris@0
|
654 $this->assertEqual($form[$field_name]['widget'][0]['uri']['#link_type'], $link_type);
|
Chris@0
|
655 }
|
Chris@0
|
656
|
Chris@0
|
657
|
Chris@0
|
658 /**
|
Chris@0
|
659 * Tests editing a link to a non-node entity.
|
Chris@0
|
660 */
|
Chris@0
|
661 public function testEditNonNodeEntityLink() {
|
Chris@0
|
662
|
Chris@0
|
663 $entity_type_manager = \Drupal::entityTypeManager();
|
Chris@0
|
664 $entity_test_storage = $entity_type_manager->getStorage('entity_test');
|
Chris@0
|
665
|
Chris@0
|
666 // Create a field with settings to validate.
|
Chris@0
|
667 $this->fieldStorage = FieldStorageConfig::create([
|
Chris@0
|
668 'field_name' => 'field_link',
|
Chris@0
|
669 'entity_type' => 'entity_test',
|
Chris@0
|
670 'type' => 'link',
|
Chris@0
|
671 'cardinality' => 1,
|
Chris@0
|
672 ]);
|
Chris@0
|
673 $this->fieldStorage->save();
|
Chris@0
|
674 FieldConfig::create([
|
Chris@0
|
675 'field_storage' => $this->fieldStorage,
|
Chris@0
|
676 'label' => 'Read more about this entity',
|
Chris@0
|
677 'bundle' => 'entity_test',
|
Chris@0
|
678 'settings' => [
|
Chris@0
|
679 'title' => DRUPAL_OPTIONAL,
|
Chris@0
|
680 ],
|
Chris@0
|
681 ])->save();
|
Chris@0
|
682
|
Chris@0
|
683 $entity_type_manager
|
Chris@0
|
684 ->getStorage('entity_form_display')
|
Chris@0
|
685 ->load('entity_test.entity_test.default')
|
Chris@0
|
686 ->setComponent('field_link', [
|
Chris@0
|
687 'type' => 'link_default',
|
Chris@0
|
688 ])
|
Chris@0
|
689 ->save();
|
Chris@0
|
690
|
Chris@0
|
691 // Create a node and a test entity to have a possibly valid reference for
|
Chris@0
|
692 // both. Create another test entity that references the first test entity.
|
Chris@0
|
693 $entity_test_link = $entity_test_storage->create(['name' => 'correct link target']);
|
Chris@0
|
694 $entity_test_link->save();
|
Chris@0
|
695
|
Chris@0
|
696 $node = $this->drupalCreateNode(['wrong link target']);
|
Chris@0
|
697
|
Chris@0
|
698 $correct_link = 'entity:entity_test/' . $entity_test_link->id();
|
Chris@0
|
699 $entity_test = $entity_test_storage->create([
|
Chris@0
|
700 'name' => 'correct link target',
|
Chris@0
|
701 'field_link' => $correct_link,
|
Chris@0
|
702 ]);
|
Chris@0
|
703 $entity_test->save();
|
Chris@0
|
704
|
Chris@0
|
705 // Edit the entity and save it, verify the correct link is kept and not
|
Chris@0
|
706 // changed to point to a node. Currently, widget does not support non-node
|
Chris@0
|
707 // autocomplete and therefore must show the link unaltered.
|
Chris@0
|
708 $this->drupalGet($entity_test->toUrl('edit-form'));
|
Chris@0
|
709 $this->assertSession()->fieldValueEquals('field_link[0][uri]', $correct_link);
|
Chris@0
|
710 $this->drupalPostForm(NULL, [], 'Save');
|
Chris@0
|
711
|
Chris@0
|
712 $entity_test_storage->resetCache();
|
Chris@0
|
713 $entity_test = $entity_test_storage->load($entity_test->id());
|
Chris@0
|
714
|
Chris@0
|
715 $this->assertEquals($correct_link, $entity_test->get('field_link')->uri);
|
Chris@0
|
716 }
|
Chris@0
|
717
|
Chris@0
|
718 /**
|
Chris@0
|
719 * Renders a test_entity and returns the output.
|
Chris@0
|
720 *
|
Chris@0
|
721 * @param int $id
|
Chris@0
|
722 * The test_entity ID to render.
|
Chris@0
|
723 * @param string $view_mode
|
Chris@0
|
724 * (optional) The view mode to use for rendering.
|
Chris@0
|
725 * @param bool $reset
|
Chris@0
|
726 * (optional) Whether to reset the entity_test storage cache. Defaults to
|
Chris@0
|
727 * TRUE to simplify testing.
|
Chris@0
|
728 *
|
Chris@0
|
729 * @return string
|
Chris@0
|
730 * The rendered HTML output.
|
Chris@0
|
731 */
|
Chris@0
|
732 protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) {
|
Chris@0
|
733 if ($reset) {
|
Chris@0
|
734 $this->container->get('entity.manager')->getStorage('entity_test')->resetCache([$id]);
|
Chris@0
|
735 }
|
Chris@0
|
736 $entity = EntityTest::load($id);
|
Chris@0
|
737 $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), $view_mode);
|
Chris@0
|
738 $content = $display->build($entity);
|
Chris@0
|
739 $output = \Drupal::service('renderer')->renderRoot($content);
|
Chris@0
|
740 $output = (string) $output;
|
Chris@0
|
741 $this->verbose($output);
|
Chris@0
|
742 return $output;
|
Chris@0
|
743 }
|
Chris@0
|
744
|
Chris@0
|
745 }
|