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