Pull merge.
[yaffs-website] / web / core / modules / layout_builder / tests / src / FunctionalJavascript / InlineBlockPrivateFilesTest.php
1 <?php
2
3 namespace Drupal\Tests\layout_builder\FunctionalJavascript;
4
5 use Drupal\file\Entity\File;
6 use Drupal\file\FileInterface;
7 use Drupal\node\Entity\Node;
8 use Drupal\node\Entity\NodeType;
9 use Drupal\Tests\file\Functional\FileFieldCreationTrait;
10 use Drupal\Tests\TestFileCreationTrait;
11
12 /**
13  * Test access to private files in block fields on the Layout Builder.
14  *
15  * @group layout_builder
16  */
17 class InlineBlockPrivateFilesTest extends InlineBlockTestBase {
18
19   use FileFieldCreationTrait;
20   use TestFileCreationTrait;
21
22   /**
23    * {@inheritdoc}
24    */
25   public static $modules = [
26     'file',
27   ];
28
29   /**
30    * The file system service.
31    *
32    * @var \Drupal\Core\File\FileSystemInterface
33    */
34   protected $fileSystem;
35
36   /**
37    * {@inheritdoc}
38    */
39   protected function setUp() {
40     parent::setUp();
41
42     // Update the test node type to not create new revisions by default. This
43     // allows testing for cases when a new revision is made and when it isn't.
44     $node_type = NodeType::load('bundle_with_section_field');
45     $node_type->setNewRevision(FALSE);
46     $node_type->save();
47     $field_settings = [
48       'file_extensions' => 'txt',
49       'uri_scheme' => 'private',
50     ];
51     $this->createFileField('field_file', 'block_content', 'basic', $field_settings);
52     $this->fileSystem = $this->container->get('file_system');
53   }
54
55   /**
56    * Test access to private files added via inline blocks in the layout builder.
57    */
58   public function testPrivateFiles() {
59     $assert_session = $this->assertSession();
60     $this->drupalLogin($this->drupalCreateUser([
61       'access contextual links',
62       'configure any layout',
63       'administer node display',
64       'administer node fields',
65     ]));
66
67     // Enable layout builder and overrides.
68     $this->drupalPostForm(
69       static::FIELD_UI_PREFIX . '/display/default',
70       ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
71       'Save'
72     );
73     $this->drupalLogout();
74
75     // Log in as user you can only configure layouts and access content.
76     $this->drupalLogin($this->drupalCreateUser([
77       'access contextual links',
78       'configure any layout',
79       'access content',
80     ]));
81     $this->drupalGet('node/1/layout');
82     $file = $this->createPrivateFile('drupal.txt');
83
84     $file_real_path = $this->fileSystem->realpath($file->getFileUri());
85     $this->assertFileExists($file_real_path);
86     $this->addInlineFileBlockToLayout('The file', $file);
87     $this->assertSaveLayout();
88
89     $this->drupalGet('node/1');
90     $private_href1 = $this->assertFileAccessibleOnNode($file);
91
92     // Remove the inline block with the private file.
93     $this->drupalGet('node/1/layout');
94     $this->removeInlineBlockFromLayout();
95     $this->assertSaveLayout();
96
97     $this->drupalGet('node/1');
98     $assert_session->pageTextNotContains($file->label());
99     // Try to access file directly after it has been removed. Since a new
100     // revision was not created for the node the inline block is not in the
101     // layout of a previous revision of the node.
102     $this->drupalGet($private_href1);
103     $assert_session->pageTextContains('You are not authorized to access this page');
104     $assert_session->pageTextNotContains($this->getFileSecret($file));
105     $this->assertFileExists($file_real_path);
106
107     $file2 = $this->createPrivateFile('2ndFile.txt');
108
109     $this->drupalGet('node/1/layout');
110     $this->addInlineFileBlockToLayout('Number2', $file2);
111     $this->assertSaveLayout();
112
113     $this->drupalGet('node/1');
114     $private_href2 = $this->assertFileAccessibleOnNode($file2);
115
116     $this->createNewNodeRevision(1);
117
118     $file3 = $this->createPrivateFile('3rdFile.txt');
119     $this->drupalGet('node/1/layout');
120     $this->replaceFileInBlock($file3);
121     $this->assertSaveLayout();
122
123     $this->drupalGet('node/1');
124     $private_href3 = $this->assertFileAccessibleOnNode($file3);
125
126     // $file2 is on a previous revision of the block which is on a previous
127     // revision of the node. The user does not have access to view the previous
128     // revision of the node.
129     $this->drupalGet($private_href2);
130     $assert_session->pageTextContains('You are not authorized to access this page');
131
132     $node = Node::load(1);
133     $node->setUnpublished();
134     $node->save();
135     $this->drupalGet('node/1');
136     $assert_session->pageTextContains('You are not authorized to access this page');
137     $this->drupalGet($private_href3);
138     $assert_session->pageTextNotContains($this->getFileSecret($file3));
139     $assert_session->pageTextContains('You are not authorized to access this page');
140
141     $this->drupalGet('node/2/layout');
142     $file4 = $this->createPrivateFile('drupal.txt');
143     $this->addInlineFileBlockToLayout('The file', $file4);
144     $this->assertSaveLayout();
145
146     $this->drupalGet('node/2');
147     $private_href4 = $this->assertFileAccessibleOnNode($file4);
148
149     $this->createNewNodeRevision(2);
150
151     // Remove the inline block with the private file.
152     // The inline block will still be attached to the previous revision of the
153     // node.
154     $this->drupalGet('node/2/layout');
155     $this->removeInlineBlockFromLayout();
156     $this->assertSaveLayout();
157
158     // Ensure that since the user cannot view the previous revision of the node
159     // they can not view the file which is only used on that revision.
160     $this->drupalGet($private_href4);
161     $assert_session->pageTextContains('You are not authorized to access this page');
162   }
163
164   /**
165    * Replaces the file in the block with another one.
166    *
167    * @param \Drupal\file\FileInterface $file
168    *   The file entity.
169    */
170   protected function replaceFileInBlock(FileInterface $file) {
171     $assert_session = $this->assertSession();
172     $page = $this->getSession()->getPage();
173     $this->clickContextualLink(static::INLINE_BLOCK_LOCATOR, 'Configure');
174     $assert_session->assertWaitOnAjaxRequest();
175     $page->pressButton('Remove');
176     $assert_session->assertWaitOnAjaxRequest();
177     $this->attachFileToBlockForm($file);
178     $page->pressButton('Update');
179     $this->assertDialogClosedAndTextVisible($file->label(), static::INLINE_BLOCK_LOCATOR);
180   }
181
182   /**
183    * Adds an entity block with a file.
184    *
185    * @param string $title
186    *   The title field value.
187    * @param \Drupal\file\Entity\File $file
188    *   The file entity.
189    */
190   protected function addInlineFileBlockToLayout($title, File $file) {
191     $assert_session = $this->assertSession();
192     $page = $this->getSession()->getPage();
193     $page->clickLink('Add Block');
194     $assert_session->assertWaitOnAjaxRequest();
195     $this->assertNotEmpty($assert_session->waitForLink('Create custom block'));
196     $this->clickLink('Create custom block');
197     $assert_session->assertWaitOnAjaxRequest();
198     $assert_session->fieldValueEquals('Title', '');
199     $page->findField('Title')->setValue($title);
200     $this->attachFileToBlockForm($file);
201     $page->pressButton('Add Block');
202     $this->assertDialogClosedAndTextVisible($file->label(), static::INLINE_BLOCK_LOCATOR);
203   }
204
205   /**
206    * Creates a private file.
207    *
208    * @param string $file_name
209    *   The file name.
210    *
211    * @return \Drupal\Core\Entity\EntityInterface|\Drupal\file\Entity\File
212    *   The file entity.
213    */
214   protected function createPrivateFile($file_name) {
215     // Create a new file entity.
216     $file = File::create([
217       'uid' => 1,
218       'filename' => $file_name,
219       'uri' => "private://$file_name",
220       'filemime' => 'text/plain',
221       'status' => FILE_STATUS_PERMANENT,
222     ]);
223     file_put_contents($file->getFileUri(), $this->getFileSecret($file));
224     $file->save();
225     return $file;
226   }
227
228   /**
229    * Asserts a file is accessible on the page.
230    *
231    * @param \Drupal\file\FileInterface $file
232    *   The file entity.
233    *
234    * @return string
235    *   The file href.
236    */
237   protected function assertFileAccessibleOnNode(FileInterface $file) {
238     $assert_session = $this->assertSession();
239     $page = $this->getSession()->getPage();
240     $assert_session->linkExists($file->label());
241     $private_href = $page->findLink($file->label())->getAttribute('href');
242     $page->clickLink($file->label());
243     $assert_session->pageTextContains($this->getFileSecret($file));
244
245     // Access file directly.
246     $this->drupalGet($private_href);
247     $assert_session->pageTextContains($this->getFileSecret($file));
248     return $private_href;
249   }
250
251   /**
252    * Gets the text secret for a file.
253    *
254    * @param \Drupal\file\FileInterface $file
255    *   The file entity.
256    *
257    * @return string
258    *   The text secret.
259    */
260   protected function getFileSecret(FileInterface $file) {
261     return "The secret in {$file->label()}";
262   }
263
264   /**
265    * Attaches a file to the block edit form.
266    *
267    * @param \Drupal\file\FileInterface $file
268    *   The file to be attached.
269    */
270   protected function attachFileToBlockForm(FileInterface $file) {
271     $assert_session = $this->assertSession();
272     $page = $this->getSession()->getPage();
273     $page->attachFileToField("files[settings_block_form_field_file_0]", $this->fileSystem->realpath($file->getFileUri()));
274     $assert_session->assertWaitOnAjaxRequest();
275     $this->assertNotEmpty($assert_session->waitForLink($file->label()));
276   }
277
278   /**
279    * Create a new revision of the node.
280    *
281    * @param int $node_id
282    *   The node id.
283    */
284   protected function createNewNodeRevision($node_id) {
285     $node = Node::load($node_id);
286     $node->setTitle('Update node');
287     $node->setNewRevision();
288     $node->save();
289   }
290
291 }