Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,5 @@ MAILER_DSN=smtp://localhost
###> php-translation/loco-adapter ###
LOCO_PROJECT_API_KEY=
###< php-translation/loco-adapter ###

FILES_STORAGE_SOURCE=files.storage.local
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "🧿 Bolt 5 Core",
"license": "MIT",
"require": {
"php": ">=7.2.9",
"php": "^8.0",
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to bump this in order to install flysystem v3

"ext-json": "*",
"ext-mbstring": "*",
"ext-pdo": "*",
Expand All @@ -29,6 +29,7 @@
"jasny/twig-extensions": "^1.3",
"knplabs/doctrine-behaviors": "^2.1",
"knplabs/knp-menu-bundle": "^3.1",
"league/flysystem-bundle": "^3.1",
"league/glide-symfony": "^1.0.4",
"miljar/php-exif": "^0.6.4",
"nelexa/zip": "^3.3 || ^4.0",
Expand Down
1 change: 1 addition & 0 deletions config/bundles.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@
Translation\Bundle\TranslationBundle::class => ['all' => true],
Translation\PlatformAdapter\Loco\Bridge\Symfony\TranslationAdapterLocoBundle::class => ['dev' => true, 'local' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
League\FlysystemBundle\FlysystemBundle::class => ['all' => true],
];
1 change: 0 additions & 1 deletion config/packages/extension_reference-extension.yaml

This file was deleted.

41 changes: 41 additions & 0 deletions config/packages/flysystem.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Read the documentation at https://github.com/thephpleague/flysystem-bundle/blob/master/docs/1-getting-started.md
flysystem:
storages:
files.storage.local:
adapter: 'local'
options:
directory: '%kernel.project_dir%/public/files'
permissions:
file:
public: 0o644
private: 0o600
dir:
public: 0o775
private: 0o700
visibility: public
files.storage:
adapter: 'lazy'
options:
source: '%env(FILES_STORAGE_SOURCE)%'
themes.storage:
adapter: 'local'
options:
directory: '%kernel.project_dir%/public/theme'
permissions:
file:
public: 0o644
private: 0o600
dir:
public: 0o775
private: 0o700
visibility: public
config.storage:
adapter: 'local'
options:
directory: '%kernel.project_dir%/config'
visibility: public
extension_config.storage:
adapter: 'local'
options:
directory: '%kernel.project_dir%/config/extensions'
visibility: public
1 change: 0 additions & 1 deletion config/routes/extension_reference-extension.yaml

This file was deleted.

4 changes: 3 additions & 1 deletion config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,6 @@ services:

Bolt\Api\ContentDataPersister:
decorates: 'api_platform.doctrine.orm.data_persister'


Bolt\Utils\FilesystemManager:
arguments: [ { files: '@files.storage', themes: '@themes.storage', config: '@config.storage', extension_config: '@extension_config.storage' }]
82 changes: 40 additions & 42 deletions src/Controller/Backend/FilemanagerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@
namespace Bolt\Controller\Backend;

use Bolt\Common\Str;
use Bolt\Configuration\FileLocation;
use Bolt\Configuration\FileLocations;
use Bolt\Controller\CsrfTrait;
use Bolt\Controller\TwigAwareController;
use Bolt\Repository\MediaRepository;
use Bolt\Utils\Excerpt;
use Bolt\Utils\PathCanonicalize;
use Bolt\Utils\FilesystemManager;
use League\Flysystem\DirectoryAttributes;
use League\Flysystem\DirectoryListing;
use League\Flysystem\FileAttributes;
use Pagerfanta\Adapter\ArrayAdapter;
use Pagerfanta\Pagerfanta;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;

Expand All @@ -39,14 +40,14 @@ class FilemanagerController extends TwigAwareController implements BackendZoneIn

private const PAGESIZE = 60;

/** @var Filesystem */
private $filesystem;
/** @var FilesystemManager */
private $filesystemManager;

public function __construct(FileLocations $fileLocations, MediaRepository $mediaRepository, RequestStack $requestStack, Filesystem $filesystem)
public function __construct(FileLocations $fileLocations, MediaRepository $mediaRepository, RequestStack $requestStack, FilesystemManager $filesystemManager)
{
$this->fileLocations = $fileLocations;
$this->mediaRepository = $mediaRepository;
$this->filesystem = $filesystem;
$this->filesystemManager = $filesystemManager;
$this->requestStack = $requestStack;
}

Expand Down Expand Up @@ -76,8 +77,8 @@ public function filemanager(string $location): Response

$location = $this->fileLocations->get($location);

$finder = $this->findFiles($location->getBasepath(), $path);
$folders = $this->findFolders($location->getBasepath(), $path);
$finder = $this->findFiles($location, $path);
$folders = $this->findFolders($location, $path);

$currentPage = (int) $this->getFromRequest('page', '1');
$pager = $this->createPaginator($finder, $currentPage);
Expand All @@ -91,7 +92,7 @@ public function filemanager(string $location): Response
'folders' => $folders,
'parent' => $parent,
'media' => $this->mediaRepository->findAll(),
'allfiles' => $location->isShowAll() ? $this->buildIndex($location->getBasepath()) : false,
'allfiles' => $location->isShowAll() ? $this->buildIndex('/', $location) : false,
'view' => $view,
]);
}
Expand All @@ -117,14 +118,14 @@ public function delete(): Response
$this->denyAccessUnlessGranted('managefiles:' . $location);

$location = $this->fileLocations->get($location);
$folder = '/' . $path;
$filesystem = $this->filesystemManager->get($location->getKey());

$folder = Path::canonicalize($location->getBasepath() . '/' . $path);

if (! $this->filesystem->exists($folder)) {
if (! $filesystem->directoryExists($folder)) {
$this->addFlash('warning', 'filemanager.delete_folder_missing');
} else {
try {
$this->filesystem->remove($folder);
$filesystem->deleteDirectory($folder);
$this->addFlash('success', 'filemanager.delete_folder_successful');
} catch (IOException $e) {
$this->addFlash('danger', 'filemanager.delete_folder_error');
Expand Down Expand Up @@ -158,15 +159,15 @@ public function create(): Response
$this->denyAccessUnlessGranted('managefiles:' . $location);

$location = $this->fileLocations->get($location);
$folder = '/' . $path;
$filesystem = $this->filesystemManager->get($location->getKey());

$folder = Path::canonicalize($location->getBasepath() . '/' . $path);

if ($this->filesystem->exists($folder)) {
if ($filesystem->directoryExists($folder)) {
$this->addFlash('warning', 'filemanager.create_folder_already_exists');
$this->addFlash('danger', 'filemanager.create_folder_error');
} else {
try {
$this->filesystem->mkdir($folder);
$filesystem->createDirectory($folder, ['visibility' => 'public']);
$this->addFlash('success', 'filemanager.create_folder_success');
} catch (IOException $exception) {
$this->addFlash('danger', 'filemanager.create_folder_error');
Expand All @@ -179,48 +180,45 @@ public function create(): Response
]);
}

private function findFiles(string $base, string $path): Finder
private function findFiles(FileLocation $location, string $path): DirectoryListing
{
$fullpath = PathCanonicalize::canonicalize($base, $path);

$finder = new Finder();
$finder->in($fullpath)->depth('== 0')->files()->sortByName();

return $finder;
return $this->filesystemManager->get($location->getKey())
->listContents($path, false)
->filter(fn($item) => $item instanceof FileAttributes);
}

private function findFolders(string $base, string $path): Finder
private function findFolders(FileLocation $location, string $path): DirectoryListing
{
$fullpath = PathCanonicalize::canonicalize($base, $path);

$finder = new Finder();
$finder->in($fullpath)->depth('== 0')->directories()->sortByName();

return $finder;
return $this->filesystemManager->get($location->getKey())
->listContents($path, false)
->filter(fn($item) => $item instanceof DirectoryAttributes);
}

private function createPaginator(Finder $finder, int $page): Pagerfanta
private function createPaginator(DirectoryListing $listing, int $page): Pagerfanta
{
$paginator = new Pagerfanta(new ArrayAdapter(iterator_to_array($finder, true)));
$paginator = new Pagerfanta(new ArrayAdapter(iterator_to_array($listing->getIterator(), true)));
$paginator->setMaxPerPage(self::PAGESIZE);
$paginator->setCurrentPage($page);

return $paginator;
}

private function buildIndex(string $base)
private function buildIndex(string $base, FileLocation $location)
{
$fullpath = Path::canonicalize($base);
$filesystem = $this->filesystemManager->get($location->getKey());

$finder = new Finder();
$finder->in($fullpath)->depth('< 5')->sortByName()->files();
$files = $filesystem
->listContents($base, true)
->filter(fn($item) => $item instanceof FileAttributes);

$index = [];

foreach ($finder as $file) {
$contents = $this->getFileSummary($file->getContents());
foreach ($files as $file) {
$contents = $this->getFileSummary(
$filesystem->read($file->path())
);
$index[] = [
'filename' => $file->getRelativePathname(),
'filename' => $file->path(),
'description' => $contents,
];
}
Expand Down
20 changes: 11 additions & 9 deletions src/Controller/ImageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
namespace Bolt\Controller;

use Bolt\Configuration\Config;
use Bolt\Utils\FilesystemManager;
use League\Glide\Responses\SymfonyResponseFactory;
use League\Glide\Server;
use League\Glide\ServerFactory;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;

class ImageController
Expand All @@ -23,16 +23,20 @@ class ImageController
/** @var Server */
private $server;

/** @var FilesystemManager */
private $filesystemManager;

/** @var array */
private $parameters = [];

/** @var Request */
private $request;

public function __construct(Config $config, RequestStack $requestStack)
public function __construct(Config $config, RequestStack $requestStack, FilesystemManager $filesystemManager)
{
$this->config = $config;
$this->request = $requestStack->getCurrentRequest();
$this->filesystemManager = $filesystemManager;
}

/**
Expand All @@ -55,7 +59,7 @@ private function createServer(): void
{
$this->server = ServerFactory::create([
'response' => new SymfonyResponseFactory(),
'source' => $this->getPath(),
'source' => $this->filesystemManager->get('files'),
'cache' => $this->getPath('cache', true, 'thumbnails'),
]);
}
Expand All @@ -80,7 +84,7 @@ private function saveAsFile(string $paramString, string $filename): void
return;
}

$filesystem = new Filesystem();
$filesystem = new SymfonyFilesystem();
$filePath = sprintf('%s%s%s%s%s', $this->getPath('thumbs'), DIRECTORY_SEPARATOR, $paramString, DIRECTORY_SEPARATOR, $filename);
$folderMode = $this->config->get('general/filepermissions/folders', 0775);
$fileMode = $this->config->get('general/filepermissions/files', 0664);
Expand Down Expand Up @@ -115,17 +119,15 @@ private function buildImage(string $filename): string

private function buildResponse(string $filename): Response
{
$filepath = $this->getPath(null, false, $filename);

if (! (new Filesystem())->exists($filepath)) {
if (! $this->filesStorage->fileExists($filename)) {
// $notice = sprintf("The file '%s' does not exist.", $filepath);

return $this->sendErrorImage();
}

// In case we're trying to "thumbnail" an svg, just return the whole thing.
if ($this->isSvg($filename)) {
$response = new Response(file_get_contents($filepath));
$response = new Response(file_get_contents($this->filesStorage->read($filename)));
$response->headers->set('Content-Type', 'image/svg+xml');

return $response;
Expand Down
Loading