3 namespace Drupal\Tests\layout_builder\FunctionalJavascript;
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;
13 * Test access to private files in block fields on the Layout Builder.
15 * @group layout_builder
17 class InlineBlockPrivateFilesTest extends InlineBlockTestBase {
19 use FileFieldCreationTrait;
20 use TestFileCreationTrait;
25 public static $modules = [
30 * The file system service.
32 * @var \Drupal\Core\File\FileSystemInterface
34 protected $fileSystem;
39 protected function setUp() {
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);
48 'file_extensions' => 'txt',
49 'uri_scheme' => 'private',
51 $this->createFileField('field_file', 'block_content', 'basic', $field_settings);
52 $this->fileSystem = $this->container->get('file_system');
56 * Test access to private files added via inline blocks in the layout builder.
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',
67 // Enable layout builder and overrides.
68 $this->drupalPostForm(
69 static::FIELD_UI_PREFIX . '/display/default',
70 ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
73 $this->drupalLogout();
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',
81 $this->drupalGet('node/1/layout');
82 $file = $this->createPrivateFile('drupal.txt');
84 $file_real_path = $this->fileSystem->realpath($file->getFileUri());
85 $this->assertFileExists($file_real_path);
86 $this->addInlineFileBlockToLayout('The file', $file);
87 $this->assertSaveLayout();
89 $this->drupalGet('node/1');
90 $private_href1 = $this->assertFileAccessibleOnNode($file);
92 // Remove the inline block with the private file.
93 $this->drupalGet('node/1/layout');
94 $this->removeInlineBlockFromLayout();
95 $this->assertSaveLayout();
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);
107 $file2 = $this->createPrivateFile('2ndFile.txt');
109 $this->drupalGet('node/1/layout');
110 $this->addInlineFileBlockToLayout('Number2', $file2);
111 $this->assertSaveLayout();
113 $this->drupalGet('node/1');
114 $private_href2 = $this->assertFileAccessibleOnNode($file2);
116 $this->createNewNodeRevision(1);
118 $file3 = $this->createPrivateFile('3rdFile.txt');
119 $this->drupalGet('node/1/layout');
120 $this->replaceFileInBlock($file3);
121 $this->assertSaveLayout();
123 $this->drupalGet('node/1');
124 $private_href3 = $this->assertFileAccessibleOnNode($file3);
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');
132 $node = Node::load(1);
133 $node->setUnpublished();
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');
141 $this->drupalGet('node/2/layout');
142 $file4 = $this->createPrivateFile('drupal.txt');
143 $this->addInlineFileBlockToLayout('The file', $file4);
144 $this->assertSaveLayout();
146 $this->drupalGet('node/2');
147 $private_href4 = $this->assertFileAccessibleOnNode($file4);
149 $this->createNewNodeRevision(2);
151 // Remove the inline block with the private file.
152 // The inline block will still be attached to the previous revision of the
154 $this->drupalGet('node/2/layout');
155 $this->removeInlineBlockFromLayout();
156 $this->assertSaveLayout();
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');
165 * Replaces the file in the block with another one.
167 * @param \Drupal\file\FileInterface $file
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);
183 * Adds an entity block with a file.
185 * @param string $title
186 * The title field value.
187 * @param \Drupal\file\Entity\File $file
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);
206 * Creates a private file.
208 * @param string $file_name
211 * @return \Drupal\Core\Entity\EntityInterface|\Drupal\file\Entity\File
214 protected function createPrivateFile($file_name) {
215 // Create a new file entity.
216 $file = File::create([
218 'filename' => $file_name,
219 'uri' => "private://$file_name",
220 'filemime' => 'text/plain',
221 'status' => FILE_STATUS_PERMANENT,
223 file_put_contents($file->getFileUri(), $this->getFileSecret($file));
229 * Asserts a file is accessible on the page.
231 * @param \Drupal\file\FileInterface $file
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));
245 // Access file directly.
246 $this->drupalGet($private_href);
247 $assert_session->pageTextContains($this->getFileSecret($file));
248 return $private_href;
252 * Gets the text secret for a file.
254 * @param \Drupal\file\FileInterface $file
260 protected function getFileSecret(FileInterface $file) {
261 return "The secret in {$file->label()}";
265 * Attaches a file to the block edit form.
267 * @param \Drupal\file\FileInterface $file
268 * The file to be attached.
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()));
279 * Create a new revision of the node.
281 * @param int $node_id
284 protected function createNewNodeRevision($node_id) {
285 $node = Node::load($node_id);
286 $node->setTitle('Update node');
287 $node->setNewRevision();