A Symfony bundle to easily manage and browse files and directories associated with your application entities.
Includes secure file upload, download, browsing, and directory management features, ready to integrate in your Symfony project.
- Entity-based file & directory management
- Secure file upload & download (OneupUploader integration)
- Breadcumb navigation and Bootstrap/FontAwesome ready templates
- Fine-grained access control (voter system)
- Easy integration and configuration
- Extensible and customizable for your own use-case
- Symfony 7+
- PHP 8.1+
- oneup/uploader-bundle
You must include the following libraries in your project for the bundle's frontend to work correctly:
- Dropzone.js (file upload UI)
- Font Awesome Free (SVG icons)
- Bootstrap (UI framework)
- jQuery (required for some interactive features)
Example with Symfony Webpack Encore:
// app.js
import 'bootstrap';
import '@fortawesome/fontawesome-free';
import 'dropzone';
import 'dropzone/dist/dropzone-bootstrap.css';
import $ from 'jquery';Or include via CDN in your base template:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/dropzone@6/dist/dropzone.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/dropzone-bootstrap@1/dist/dropzone-bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dropzone@6/dist/dropzone.min.js"></script>composer require afornerot/bnine-filesbundleIf you use Symfony Flex, the bundle is auto-registered.
Otherwise, add to config/bundles.php:
return [
// ...
Bnine\FilesBundle\BnineFilesBundle::class => ['all' => true],
];Import the bundle routes in config/routes.yaml:
bninefilesbundle:
resource: '@BnineFilesBundle/config/routes.yaml'
prefix: '/bninefiles'If you want to use the provided Twig extensions or templates, add in config/packages/twig.yaml:
twig:
paths:
'%kernel.project_dir%/vendor/bnine/filesbundle/templates': bNineFilesYou need to install and configure OneupUploaderBundle:
composer require oneup/uploader-bundleThe upload directory must always be %kernel.project_dir%/uploads.
Changing the upload path is not supported.
Example in config/packages/oneup_uploader.yaml:
oneup_uploader:
mappings:
bninefile:
frontend: dropzone When creating a new entity, you should initialize the file container for the domain and entity ID after persisting the entity.
This makes the file area ready for uploads and management.
use Bnine\FilesBundle\Service\FileService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/admin/project/submit', name: 'app_admin_project_submit')]
public function submit(Request $request, EntityManagerInterface $em, FileService $fileService): Response
{
$project = new Project();
$form = $this->createForm(ProjectType::class, $project);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($project);
$em->flush();
// Initialize the file container for this project
$fileService->init('project', $project->getId());
return $this->redirectToRoute('app_admin_project');
}
return $this->render('project/edit.html.twig', [
'form' => $form,
]);
}}To display the file container for a specific domain and entity, insert the following line in your Twig template:
{{ render(path("bninefiles_files", {domain: 'project', id: project.id, editable: 0})) }}- editable: 0 renders the file browser in read-only mode.
- editable: 1 renders with upload, create folder, and delete options enabled.
Example:
{# Read-only file browser #}
{{ render(path("bninefiles_files", {domain: 'project', id: project.id, editable: 0})) }}
{# Editable file browser (allows upload, create folder, delete) #}
{{ render(path("bninefiles_files", {domain: 'project', id: project.id, editable: 1})) }}This is the only integration you need in your templates.
Just insert this line wherever you want the file manager to appear!
The bundle provides an abstract voter.
You must implement your own voter in your application for fine-grained access control.
Example:
<?php
namespace App\Security;
use App\Entity\User;
use App\Repository\ProjectRepository;
use Bnine\FilesBundle\Security\AbstractFileVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class FileVoter extends AbstractFileVoter
{
private ProjectRepository $projectRepository;
public function __construct(ProjectRepository $projectRepository)
{
$this->projectRepository = $projectRepository;
}
protected function canView(string $domain, $id, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
return true;
}
protected function canEdit(string $domain, $id, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
if ($user->hasRole('ROLE_ADMIN')) {
return true;
}
switch ($domain) {
case 'project':
$project = $this->projectRepository->find($id);
if ($project && $project->getUsers()->contains($user)) {
return true;
}
break;
}
return false;
}
protected function canDelete(string $domain, $id, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
if ($user->hasRole('ROLE_ADMIN')) {
return true;
}
switch ($domain) {
case 'project':
$project = $this->projectRepository->find($id);
if ($project && $project->getUsers()->contains($user)) {
return true;
}
break;
}
return false;
}
}MIT
Pull requests and issues are welcome!
Open an issue on GitHub for bugs or feature requests.