Updated to Drupal 8.6.4, which is PHP 7.3 friendly. Also updated HTMLaw library....
[yaffs-website] / web / core / modules / filter / tests / src / Functional / FilterFormatAccessTest.php
1 <?php
2
3 namespace Drupal\Tests\filter\Functional;
4
5 use Drupal\Core\Access\AccessResult;
6 use Drupal\filter\Entity\FilterFormat;
7 use Drupal\Tests\BrowserTestBase;
8
9 /**
10  * Tests access to text formats.
11  *
12  * @group Access
13  * @group filter
14  */
15 class FilterFormatAccessTest extends BrowserTestBase {
16
17   /**
18    * Modules to enable.
19    *
20    * @var array
21    */
22   public static $modules = ['block', 'filter', 'node'];
23
24   /**
25    * A user with administrative permissions.
26    *
27    * @var \Drupal\user\UserInterface
28    */
29   protected $adminUser;
30
31   /**
32    * A user with 'administer filters' permission.
33    *
34    * @var \Drupal\user\UserInterface
35    */
36   protected $filterAdminUser;
37
38   /**
39    * A user with permission to create and edit own content.
40    *
41    * @var \Drupal\user\UserInterface
42    */
43   protected $webUser;
44
45   /**
46    * An object representing an allowed text format.
47    *
48    * @var object
49    */
50   protected $allowedFormat;
51
52   /**
53    * An object representing a secondary allowed text format.
54    *
55    * @var object
56    */
57   protected $secondAllowedFormat;
58
59   /**
60    * An object representing a disallowed text format.
61    *
62    * @var object
63    */
64   protected $disallowedFormat;
65
66   protected function setUp() {
67     parent::setUp();
68
69     $this->drupalPlaceBlock('page_title_block');
70
71     $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
72
73     // Create a user who can administer text formats, but does not have
74     // specific permission to use any of them.
75     $this->filterAdminUser = $this->drupalCreateUser([
76       'administer filters',
77       'create page content',
78       'edit any page content',
79     ]);
80
81     // Create three text formats. Two text formats are created for all users so
82     // that the drop-down list appears for all tests.
83     $this->drupalLogin($this->filterAdminUser);
84     $formats = [];
85     for ($i = 0; $i < 3; $i++) {
86       $edit = [
87         'format' => mb_strtolower($this->randomMachineName()),
88         'name' => $this->randomMachineName(),
89       ];
90       $this->drupalPostForm('admin/config/content/formats/add', $edit, t('Save configuration'));
91       $this->resetFilterCaches();
92       $formats[] = FilterFormat::load($edit['format']);
93     }
94     list($this->allowedFormat, $this->secondAllowedFormat, $this->disallowedFormat) = $formats;
95     $this->drupalLogout();
96
97     // Create a regular user with access to two of the formats.
98     $this->webUser = $this->drupalCreateUser([
99       'create page content',
100       'edit any page content',
101       $this->allowedFormat->getPermissionName(),
102       $this->secondAllowedFormat->getPermissionName(),
103     ]);
104
105     // Create an administrative user who has access to use all three formats.
106     $this->adminUser = $this->drupalCreateUser([
107       'administer filters',
108       'create page content',
109       'edit any page content',
110       $this->allowedFormat->getPermissionName(),
111       $this->secondAllowedFormat->getPermissionName(),
112       $this->disallowedFormat->getPermissionName(),
113     ]);
114     $this->drupalPlaceBlock('local_tasks_block');
115   }
116
117   /**
118    * Tests the Filter format access permissions functionality.
119    */
120   public function testFormatPermissions() {
121     // Make sure that a regular user only has access to the text formats for
122     // which they were granted access.
123     $fallback_format = FilterFormat::load(filter_fallback_format());
124     $disallowed_format_name = $this->disallowedFormat->getPermissionName();
125     $this->assertTrue($this->allowedFormat->access('use', $this->webUser), 'A regular user has access to use a text format they were granted access to.');
126     $this->assertEqual(AccessResult::allowed()->addCacheContexts(['user.permissions']), $this->allowedFormat->access('use', $this->webUser, TRUE), 'A regular user has access to use a text format they were granted access to.');
127     $this->assertFalse($this->disallowedFormat->access('use', $this->webUser), 'A regular user does not have access to use a text format they were not granted access to.');
128     $this->assertEqual(AccessResult::neutral("The '$disallowed_format_name' permission is required.")->cachePerPermissions(), $this->disallowedFormat->access('use', $this->webUser, TRUE), 'A regular user does not have access to use a text format they were not granted access to.');
129     $this->assertTrue($fallback_format->access('use', $this->webUser), 'A regular user has access to use the fallback format.');
130     $this->assertEqual(AccessResult::allowed(), $fallback_format->access('use', $this->webUser, TRUE), 'A regular user has access to use the fallback format.');
131
132     // Perform similar checks as above, but now against the entire list of
133     // available formats for this user.
134     $this->assertTrue(in_array($this->allowedFormat->id(), array_keys(filter_formats($this->webUser))), 'The allowed format appears in the list of available formats for a regular user.');
135     $this->assertFalse(in_array($this->disallowedFormat->id(), array_keys(filter_formats($this->webUser))), 'The disallowed format does not appear in the list of available formats for a regular user.');
136     $this->assertTrue(in_array(filter_fallback_format(), array_keys(filter_formats($this->webUser))), 'The fallback format appears in the list of available formats for a regular user.');
137
138     // Make sure that a regular user only has permission to use the format
139     // they were granted access to.
140     $this->assertTrue($this->webUser->hasPermission($this->allowedFormat->getPermissionName()), 'A regular user has permission to use the allowed text format.');
141     $this->assertFalse($this->webUser->hasPermission($this->disallowedFormat->getPermissionName()), 'A regular user does not have permission to use the disallowed text format.');
142
143     // Make sure that the allowed format appears on the node form and that
144     // the disallowed format does not.
145     $this->drupalLogin($this->webUser);
146     $this->drupalGet('node/add/page');
147     $elements = $this->xpath('//select[@name=:name]/option', [
148       ':name' => 'body[0][format]',
149       ':option' => $this->allowedFormat->id(),
150     ]);
151     $options = [];
152     foreach ($elements as $element) {
153       $options[$element->getValue()] = $element;
154     }
155     $this->assertTrue(isset($options[$this->allowedFormat->id()]), 'The allowed text format appears as an option when adding a new node.');
156     $this->assertFalse(isset($options[$this->disallowedFormat->id()]), 'The disallowed text format does not appear as an option when adding a new node.');
157     $this->assertFalse(isset($options[filter_fallback_format()]), 'The fallback format does not appear as an option when adding a new node.');
158
159     // Check regular user access to the filter tips pages.
160     $this->drupalGet('filter/tips/' . $this->allowedFormat->id());
161     $this->assertResponse(200);
162     $this->drupalGet('filter/tips/' . $this->disallowedFormat->id());
163     $this->assertResponse(403);
164     $this->drupalGet('filter/tips/' . filter_fallback_format());
165     $this->assertResponse(200);
166     $this->drupalGet('filter/tips/invalid-format');
167     $this->assertResponse(404);
168
169     // Check admin user access to the filter tips pages.
170     $this->drupalLogin($this->adminUser);
171     $this->drupalGet('filter/tips/' . $this->allowedFormat->id());
172     $this->assertResponse(200);
173     $this->drupalGet('filter/tips/' . $this->disallowedFormat->id());
174     $this->assertResponse(200);
175     $this->drupalGet('filter/tips/' . filter_fallback_format());
176     $this->assertResponse(200);
177     $this->drupalGet('filter/tips/invalid-format');
178     $this->assertResponse(404);
179   }
180
181   /**
182    * Tests if text format is available to a role.
183    */
184   public function testFormatRoles() {
185     // Get the role ID assigned to the regular user.
186     $roles = $this->webUser->getRoles(TRUE);
187     $rid = $roles[0];
188
189     // Check that this role appears in the list of roles that have access to an
190     // allowed text format, but does not appear in the list of roles that have
191     // access to a disallowed text format.
192     $this->assertTrue(in_array($rid, array_keys(filter_get_roles_by_format($this->allowedFormat))), 'A role which has access to a text format appears in the list of roles that have access to that format.');
193     $this->assertFalse(in_array($rid, array_keys(filter_get_roles_by_format($this->disallowedFormat))), 'A role which does not have access to a text format does not appear in the list of roles that have access to that format.');
194
195     // Check that the correct text format appears in the list of formats
196     // available to that role.
197     $this->assertTrue(in_array($this->allowedFormat->id(), array_keys(filter_get_formats_by_role($rid))), 'A text format which a role has access to appears in the list of formats available to that role.');
198     $this->assertFalse(in_array($this->disallowedFormat->id(), array_keys(filter_get_formats_by_role($rid))), 'A text format which a role does not have access to does not appear in the list of formats available to that role.');
199
200     // Check that the fallback format is always allowed.
201     $this->assertEqual(filter_get_roles_by_format(FilterFormat::load(filter_fallback_format())), user_role_names(), 'All roles have access to the fallback format.');
202     $this->assertTrue(in_array(filter_fallback_format(), array_keys(filter_get_formats_by_role($rid))), 'The fallback format appears in the list of allowed formats for any role.');
203   }
204
205   /**
206    * Tests editing a page using a disallowed text format.
207    *
208    * Verifies that regular users and administrators are able to edit a page, but
209    * not allowed to change the fields which use an inaccessible text format.
210    * Also verifies that fields which use a text format that does not exist can
211    * be edited by administrators only, but that the administrator is forced to
212    * choose a new format before saving the page.
213    */
214   public function testFormatWidgetPermissions() {
215     $body_value_key = 'body[0][value]';
216     $body_format_key = 'body[0][format]';
217
218     // Create node to edit.
219     $this->drupalLogin($this->adminUser);
220     $edit = [];
221     $edit['title[0][value]'] = $this->randomMachineName(8);
222     $edit[$body_value_key] = $this->randomMachineName(16);
223     $edit[$body_format_key] = $this->disallowedFormat->id();
224     $this->drupalPostForm('node/add/page', $edit, t('Save'));
225     $node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
226
227     // Try to edit with a less privileged user.
228     $this->drupalLogin($this->webUser);
229     $this->drupalGet('node/' . $node->id());
230     $this->clickLink(t('Edit'));
231
232     // Verify that body field is read-only and contains replacement value.
233     $this->assertFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", t('This field has been disabled because you do not have sufficient permissions to edit it.'), 'Text format access denied message found.');
234
235     // Verify that title can be changed, but preview displays original body.
236     $new_edit = [];
237     $new_edit['title[0][value]'] = $this->randomMachineName(8);
238     $this->drupalPostForm(NULL, $new_edit, t('Preview'));
239     $this->assertText($edit[$body_value_key], 'Old body found in preview.');
240
241     // Save and verify that only the title was changed.
242     $this->drupalPostForm('node/' . $node->id() . '/edit', $new_edit, t('Save'));
243     $this->assertNoText($edit['title[0][value]'], 'Old title not found.');
244     $this->assertText($new_edit['title[0][value]'], 'New title found.');
245     $this->assertText($edit[$body_value_key], 'Old body found.');
246
247     // Check that even an administrator with "administer filters" permission
248     // cannot edit the body field if they do not have specific permission to
249     // use its stored format. (This must be disallowed so that the
250     // administrator is never forced to switch the text format to something
251     // else.)
252     $this->drupalLogin($this->filterAdminUser);
253     $this->drupalGet('node/' . $node->id() . '/edit');
254     $this->assertFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", t('This field has been disabled because you do not have sufficient permissions to edit it.'), 'Text format access denied message found.');
255
256     // Disable the text format used above.
257     $this->disallowedFormat->disable()->save();
258     $this->resetFilterCaches();
259
260     // Log back in as the less privileged user and verify that the body field
261     // is still disabled, since the less privileged user should not be able to
262     // edit content that does not have an assigned format.
263     $this->drupalLogin($this->webUser);
264     $this->drupalGet('node/' . $node->id() . '/edit');
265     $this->assertFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", t('This field has been disabled because you do not have sufficient permissions to edit it.'), 'Text format access denied message found.');
266
267     // Log back in as the filter administrator and verify that the body field
268     // can be edited.
269     $this->drupalLogin($this->filterAdminUser);
270     $this->drupalGet('node/' . $node->id() . '/edit');
271     $this->assertNoFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", NULL, 'Text format access denied message not found.');
272     $this->assertFieldByXPath("//select[@name='$body_format_key']", NULL, 'Text format selector found.');
273
274     // Verify that trying to save the node without selecting a new text format
275     // produces an error message, and does not result in the node being saved.
276     $old_title = $new_edit['title[0][value]'];
277     $new_title = $this->randomMachineName(8);
278     $edit = [];
279     $edit['title[0][value]'] = $new_title;
280     $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
281     $this->assertText(t('@name field is required.', ['@name' => t('Text format')]), 'Error message is displayed.');
282     $this->drupalGet('node/' . $node->id());
283     $this->assertText($old_title, 'Old title found.');
284     $this->assertNoText($new_title, 'New title not found.');
285
286     // Now select a new text format and make sure the node can be saved.
287     $edit[$body_format_key] = filter_fallback_format();
288     $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
289     $this->assertUrl('node/' . $node->id());
290     $this->assertText($new_title, 'New title found.');
291     $this->assertNoText($old_title, 'Old title not found.');
292
293     // Switch the text format to a new one, then disable that format and all
294     // other formats on the site (leaving only the fallback format).
295     $this->drupalLogin($this->adminUser);
296     $edit = [$body_format_key => $this->allowedFormat->id()];
297     $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
298     $this->assertUrl('node/' . $node->id());
299     foreach (filter_formats() as $format) {
300       if (!$format->isFallbackFormat()) {
301         $format->disable()->save();
302       }
303     }
304
305     // Since there is now only one available text format, the widget for
306     // selecting a text format would normally not display when the content is
307     // edited. However, we need to verify that the filter administrator still
308     // is forced to make a conscious choice to reassign the text to a different
309     // format.
310     $this->drupalLogin($this->filterAdminUser);
311     $old_title = $new_title;
312     $new_title = $this->randomMachineName(8);
313     $edit = [];
314     $edit['title[0][value]'] = $new_title;
315     $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
316     $this->assertText(t('@name field is required.', ['@name' => t('Text format')]), 'Error message is displayed.');
317     $this->drupalGet('node/' . $node->id());
318     $this->assertText($old_title, 'Old title found.');
319     $this->assertNoText($new_title, 'New title not found.');
320     $edit[$body_format_key] = filter_fallback_format();
321     $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
322     $this->assertUrl('node/' . $node->id());
323     $this->assertText($new_title, 'New title found.');
324     $this->assertNoText($old_title, 'Old title not found.');
325   }
326
327   /**
328    * Rebuilds text format and permission caches in the thread running the tests.
329    */
330   protected function resetFilterCaches() {
331     filter_formats_reset();
332   }
333
334 }