Skip to content
Snippets Groups Projects
Commit 1584761f authored by Mingsong's avatar Mingsong
Browse files

Merge branch '3403904-private-file' into '1.x'

Issue #3403904: Private file can not be published

See merge request !1
parents 51ec674f 8e7734e9
No related branches found
No related tags found
No related merge requests found
Pipeline #71313 skipped
......@@ -3,7 +3,6 @@
namespace Drupal\file_to_media\Controller;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityFormBuilderInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\file\FileInterface;
use Drupal\file_to_media\FileToMediaAccessTrait;
......@@ -20,6 +19,8 @@ class FileToMedia implements ContainerInjectionInterface {
use FileToMediaAccessTrait;
/**
* Entity form builder.
*
* @var \Drupal\Core\Entity\EntityFormBuilderInterface
*/
protected $entityFormBuilder;
......@@ -49,6 +50,11 @@ class FileToMedia implements ContainerInjectionInterface {
if (!$this->hasCreateAccessToMediaType($media_type)->isAllowed()) {
throw new AccessDeniedHttpException('You do not have permission to create media of this type');
}
// A media entity for a private file should not be created.
if (!$this->isPublicDownloadable($file)) {
throw new AccessDeniedHttpException('Media entity should not be created for a private file.');
}
$field_definition = $media_type->getSource()->getSourceFieldDefinition($media_type);
$filename = $file->getFilename();
$extension = pathinfo($filename, PATHINFO_EXTENSION);
......
......@@ -3,10 +3,11 @@
namespace Drupal\file_to_media;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\file\FileInterface;
use Drupal\file\Plugin\Field\FieldType\FileItem;
use Drupal\media\MediaTypeInterface;
use Drupal\user\Entity\User;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -15,6 +16,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
trait FileToMediaAccessTrait {
/**
* Entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
......@@ -59,4 +62,17 @@ trait FileToMediaAccessTrait {
return is_a($field_definition->getItemDefinition()->getClass(), FileItem::class, TRUE) && in_array($extension, $extensions, TRUE);
}
/**
* Checks if a file is downloadable to public.
*
* @param \Drupal\file\FileInterface $file
* The file to check.
*
* @return bool
* TRUE if the file is a public accessible file, otherwise return FALSE.
*/
protected function isPublicDownloadable(FileInterface $file) {
return $file->access('download', User::load(0));
}
}
......@@ -5,7 +5,6 @@ namespace Drupal\file_to_media\Plugin\views\field;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Url;
use Drupal\file\FileInterface;
use Drupal\file\FileUsage\FileUsageInterface;
use Drupal\file_to_media\FileToMediaAccessTrait;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;
......@@ -23,6 +22,8 @@ class ToMedia extends FieldPluginBase {
use FileToMediaAccessTrait;
/**
* File usage calculation.
*
* @var \Drupal\file\FileUsage\FileUsageInterface
*/
protected $fileUsage;
......@@ -72,7 +73,9 @@ class ToMedia extends FieldPluginBase {
$cache = CacheableMetadata::createFromObject($file);
$type_storage = $this->entityTypeManager->getStorage('media_type');
$cache->addCacheTags(['media_type_list', 'media_list']);
if ($this->getFileUsages($file)) {
// Hide the creating link for files that
// are not used or are private.
if ($this->getFileUsages($file) || !$this->isPublicDownloadable($file)) {
$build = ['#markup' => ''];
$cache->applyTo($build);
return $build;
......
<?php
namespace Drupal\Tests\file_to_media\Functional;
use Drupal\Tests\file\Functional\FileFieldTestBase;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
use Drupal\file\FileInterface;
use Drupal\file\Entity\File;
use Drupal\media\MediaTypeInterface;
use Drupal\node\Entity\NodeType;
use Drupal\views\Entity\View;
/**
* Private test file for File to Media module.
*
* @group file
*/
class PrivateFileTest extends FileFieldTestBase {
use MediaTypeCreationTrait;
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'node_access_test',
'field_test',
'file_to_media',
'media',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* A private file entity for testing.
*
* @var \Drupal\file\FileInterface
*/
protected FileInterface $privateFile;
/**
* A media type for testing.
*
* @var \Drupal\media\MediaTypeInterface
*/
protected MediaTypeInterface $mediaType;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
node_access_test_add_field(NodeType::load('article'));
node_access_rebuild();
\Drupal::state()->set('node_access_test.private', TRUE);
// This test expects unused managed files to be marked as a temporary file.
$this->config('file.settings')
->set('make_unused_managed_files_temporary', TRUE)
->save();
// Create an admin user for this test.
$this->adminUser = $this->drupalCreateUser([
'access files overview',
'bypass node access',
'administer media',
'access media overview',
]);
// Add the file to media links to the file overview.
$this->setUpFileToMediaLinks();
// Create a private file.
$this->createPrivateFile();
// Create the document media type.
$this->mediaType = $this->createMediaType('file');
}
/**
* Tests creating a media entity for a private file.
*/
public function testPrivateFile() {
$assert = $this->assertSession();
// Logout the admin user.
$this->drupalLogOut();
// Ensure the file cannot be downloaded after logging out.
$this->drupalGet($this->privateFile->createFileUrl(FALSE));
$assert->statusCodeEquals(403);
// Login as an admin user.
$this->drupalLogin($this->adminUser);
// Check view.
$this->drupalGet('admin/content/files');
// The path to create a new media for the private file.
$path_create_file_media = '/file/to-media/' . $this->privateFile->id() . '/' . $this->mediaType->id();
// The file to media link should not be available for a private file.
$assert->linkByHrefNotExists($path_create_file_media);
$link_title = sprintf('Create %s', $this->mediaType->label());
$assert->linkNotExists($link_title);
// Test the route to create a new media for the private file directly.
$this->drupalGet($path_create_file_media);
$assert->statusCodeEquals(403);
}
/**
* Set up the file to media links in the file overview.
*/
private function setUpFileToMediaLinks() {
$view = View::load('files');
$displays = $view->get('display');
// Add in the file_to_media field.
$displays['default']['display_options']['fields']['file_to_media'] = [
'id' => 'file_to_media',
'table' => 'file_managed',
'field' => 'file_to_media',
'relationship' => 'none',
'group_type' => 'group',
'admin_label' => '',
'label' => 'Create media',
'exclude' => FALSE,
'alter' => [
'alter_text' => FALSE,
'text' => '',
'make_link' => FALSE,
'path' => '',
'absolute' => FALSE,
'external' => FALSE,
'replace_spaces' => FALSE,
'path_case' => 'none',
'trim_whitespace' => FALSE,
'alt' => '',
'rel' => '',
'link_class' => '',
'prefix' => '',
'suffix' => '',
'target' => '',
'nl2br' => FALSE,
'max_length' => '0',
'word_boundary' => TRUE,
'ellipsis' => TRUE,
'more_link' => FALSE,
'more_link_text' => '',
'more_link_path' => '',
'strip_tags' => FALSE,
'trim' => FALSE,
'preserve_tags' => '',
'html' => FALSE,
],
'element_type' => '',
'element_class' => '',
'element_label_type' => '',
'element_label_class' => '',
'element_label_colon' => TRUE,
'element_wrapper_type' => '',
'element_wrapper_class' => '',
'element_default_classes' => TRUE,
'empty' => '',
'hide_empty' => FALSE,
'empty_zero' => FALSE,
'hide_alter_empty' => TRUE,
'entity_type' => 'file',
'plugin_id' => 'file_to_media',
];
$view->set('display', $displays);
$view->save();
}
/**
* Create a private file for testing.
*/
private function createPrivateFile() {
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
$type_name = 'article';
$field_name = strtolower($this->randomMachineName());
$this->createFileField($field_name, 'node', $type_name, ['uri_scheme' => 'private']);
$test_file = $this->getTestFile('text');
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name, TRUE, ['private' => TRUE]);
\Drupal::entityTypeManager()->getStorage('node')->resetCache([$nid]);
/** @var \Drupal\node\NodeInterface $node */
$node = $node_storage->load($nid);
$this->privateFile = File::load($node->{$field_name}->target_id);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment