287 changed files with 18 additions and 19739 deletions
@ -1,6 +1,6 @@
|
||||
/* Custom variables */ |
||||
|
||||
@import "../../core/Resources/assets/css/admin.scss"; |
||||
@import "../../vendor/murph/murph-core/src/core/Resources/assets/css/admin.scss"; |
||||
|
||||
/* Custom CSS */ |
||||
|
||||
|
@ -1 +1 @@
|
||||
import '../../core/Resources/assets/js/admin.js' |
||||
import '../../vendor/murph/murph-core/src/core/Resources/assets/js/admin.js' |
||||
|
@ -1,204 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Analytic; |
||||
|
||||
use App\Core\Entity\Site\Node; |
||||
use App\Core\Repository\Analytic\RefererRepositoryQuery; |
||||
use App\Core\Repository\Analytic\ViewRepositoryQuery; |
||||
|
||||
/** |
||||
* class DateRangeAnalytic. |
||||
* |
||||
* @author Simon Vieille <simon@deblan.fr> |
||||
*/ |
||||
class DateRangeAnalytic |
||||
{ |
||||
protected ViewRepositoryQuery $viewQuery; |
||||
protected RefererRepositoryQuery $refererQuery; |
||||
protected ?Node $node; |
||||
protected ?\DateTime $from; |
||||
protected ?\DateTime $to; |
||||
protected bool $reload = true; |
||||
protected array $cache = []; |
||||
|
||||
public function __construct(ViewRepositoryQuery $viewQuery, RefererRepositoryQuery $refererQuery) |
||||
{ |
||||
$this->viewQuery = $viewQuery; |
||||
$this->refererQuery = $refererQuery; |
||||
} |
||||
|
||||
public function getViews(): array |
||||
{ |
||||
$entities = $this->getEntities('view'); |
||||
$this->reload = false; |
||||
|
||||
if ($entities) { |
||||
$first = $entities[0]; |
||||
$last = $entities[count($entities) - 1]; |
||||
|
||||
$diff = $first->getDate()->diff($last->getDate()); |
||||
|
||||
if ($diff->days >= 90) { |
||||
$format = 'Y-m'; |
||||
} else { |
||||
$format = 'Y-m-d'; |
||||
} |
||||
} |
||||
|
||||
$datas = []; |
||||
|
||||
foreach ($entities as $entity) { |
||||
$index = $entity->getDate()->format($format); |
||||
|
||||
if (!isset($datas[$index])) { |
||||
$datas[$index] = 0; |
||||
} |
||||
|
||||
$datas[$index] += $entity->getViews(); |
||||
} |
||||
|
||||
return $datas; |
||||
} |
||||
|
||||
public function getPathViews(): array |
||||
{ |
||||
$entities = $this->getEntities('view'); |
||||
$this->reload = false; |
||||
|
||||
$datas = []; |
||||
|
||||
foreach ($entities as $entity) { |
||||
$index = $entity->getPath(); |
||||
|
||||
if (!isset($datas[$index])) { |
||||
$datas[$index] = [ |
||||
'views' => 0, |
||||
'desktopViews' => 0, |
||||
'mobileViews' => 0, |
||||
]; |
||||
} |
||||
|
||||
$datas[$index]['views'] += $entity->getViews(); |
||||
$datas[$index]['desktopViews'] += $entity->getDesktopViews(); |
||||
$datas[$index]['mobileViews'] += $entity->getMobileViews(); |
||||
} |
||||
|
||||
uasort($datas, function($a, $b) { |
||||
if ($a['views'] > $b['views']) { |
||||
return -1; |
||||
} |
||||
|
||||
if ($a['views'] < $b['views']) { |
||||
return 1; |
||||
} |
||||
|
||||
return 0; |
||||
}); |
||||
|
||||
return $datas; |
||||
} |
||||
|
||||
public function getReferers(): array |
||||
{ |
||||
$entities = $this->getEntities('referer'); |
||||
$this->reload = false; |
||||
|
||||
$datas = []; |
||||
|
||||
foreach ($entities as $entity) { |
||||
$index = parse_url($entity->getUri(), PHP_URL_HOST); |
||||
|
||||
if (!isset($datas[$index])) { |
||||
$datas[$index] = [ |
||||
'views' => 0, |
||||
'uris' => [], |
||||
]; |
||||
} |
||||
|
||||
$datas[$index]['views'] += $entity->getViews(); |
||||
|
||||
$path = parse_url($entity->getUri(), PHP_URL_PATH); |
||||
|
||||
if (empty($path)) { |
||||
$path = '/'; |
||||
} |
||||
|
||||
if (!isset($datas[$index]['uris'][$path])) { |
||||
$datas[$index]['uris'][$path] = 0; |
||||
} |
||||
|
||||
$datas[$index]['uris'][$path] += $entity->getViews(); |
||||
} |
||||
|
||||
uasort($datas, function($a, $b) { |
||||
if ($a['views'] > $b['views']) { |
||||
return -1; |
||||
} |
||||
|
||||
if ($a['views'] < $b['views']) { |
||||
return 1; |
||||
} |
||||
|
||||
return 0; |
||||
}); |
||||
|
||||
return $datas; |
||||
} |
||||
|
||||
public function setDateRange(?\DateTime $from, ?\DateTime $to): self |
||||
{ |
||||
$this->from = $from; |
||||
$this->to = $to; |
||||
$this->reload = true; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
public function setNode(?Node $node): self |
||||
{ |
||||
$this->node = $node; |
||||
$this->reload = true; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
protected function getEntities(string $type): array |
||||
{ |
||||
if ('view' === $type) { |
||||
$query = $this->viewQuery->create(); |
||||
} elseif ('referer' === $type) { |
||||
$query = $this->refererQuery->create(); |
||||
} else { |
||||
throw new \InvalidArgumentException('Invalid type'); |
||||
} |
||||
|
||||
if (!$this->reload && isset($this->cache[$type])) { |
||||
return $this->cache[$type]; |
||||
} |
||||
|
||||
if (null !== $this->from) { |
||||
$query |
||||
->andWhere('.date >= :from') |
||||
->setParameter(':from', $this->from) |
||||
; |
||||
} |
||||
|
||||
if (null !== $this->to) { |
||||
$query |
||||
->andWhere('.date <= :to') |
||||
->setParameter(':to', $this->to) |
||||
; |
||||
} |
||||
|
||||
if (null !== $this->node) { |
||||
$query |
||||
->andWhere('.node = :node') |
||||
->setParameter(':node', $this->node->getId()) |
||||
; |
||||
} |
||||
|
||||
$this->cache[$type] = $query->orderBy('.date')->find(); |
||||
|
||||
return $this->cache[$type]; |
||||
} |
||||
} |
@ -1,20 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Annotation; |
||||
|
||||
use Doctrine\Common\Annotations\Annotation; |
||||
|
||||
/** |
||||
* class UrlGenerator. |
||||
* |
||||
* @author Simon Vieille <simon@deblan.fr> |
||||
* @Annotation |
||||
*/ |
||||
class UrlGenerator |
||||
{ |
||||
public string $service; |
||||
|
||||
public string $method; |
||||
|
||||
public array $options = []; |
||||
} |
@ -1,96 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Authenticator; |
||||
|
||||
use App\Entity\User; |
||||
use Doctrine\ORM\EntityManagerInterface; |
||||
use Symfony\Component\HttpFoundation\RedirectResponse; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; |
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; |
||||
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; |
||||
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; |
||||
use Symfony\Component\Security\Core\Security; |
||||
use Symfony\Component\Security\Core\User\UserInterface; |
||||
use Symfony\Component\Security\Core\User\UserProviderInterface; |
||||
use Symfony\Component\Security\Csrf\CsrfToken; |
||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; |
||||
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator; |
||||
use Symfony\Component\Security\Http\Util\TargetPathTrait; |
||||
|
||||
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator |
||||
{ |
||||
use TargetPathTrait; |
||||
|
||||
private EntityManagerInterface $entityManager; |
||||
|
||||
private UrlGeneratorInterface $urlGenerator; |
||||
|
||||
private CsrfTokenManagerInterface $csrfTokenManager; |
||||
|
||||
private UserPasswordEncoderInterface $passwordEncoder; |
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder) |
||||
{ |
||||
$this->entityManager = $entityManager; |
||||
$this->urlGenerator = $urlGenerator; |
||||
$this->csrfTokenManager = $csrfTokenManager; |
||||
$this->passwordEncoder = $passwordEncoder; |
||||
} |
||||
|
||||
public function supports(Request $request) |
||||
{ |
||||
return 'auth_login' === $request->attributes->get('_route') && $request->isMethod('POST'); |
||||
} |
||||
|
||||
public function getCredentials(Request $request) |
||||
{ |
||||
$credentials = [ |
||||
'email' => $request->request->get('_username'), |
||||
'password' => $request->request->get('_password'), |
||||
'csrf_token' => $request->request->get('_csrf_token'), |
||||
]; |
||||
|
||||
$request->getSession()->set(Security::LAST_USERNAME, $credentials['email']); |
||||
|
||||
return $credentials; |
||||
} |
||||
|
||||
public function getUser($credentials, UserProviderInterface $userProvider) |
||||
{ |
||||
$token = new CsrfToken('authenticate', $credentials['csrf_token']); |
||||
|
||||
if (!$this->csrfTokenManager->isTokenValid($token)) { |
||||
throw new InvalidCsrfTokenException(); |
||||
} |
||||
|
||||
$user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]); |
||||
|
||||
if (!$user) { |
||||
// fail authentication with a custom error |
||||
throw new CustomUserMessageAuthenticationException('Email could not be found.'); |
||||
} |
||||
|
||||
return $user; |
||||
} |
||||
|
||||
public function checkCredentials($credentials, UserInterface $user) |
||||
{ |
||||
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']); |
||||
} |
||||
|
||||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) |
||||
{ |
||||
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) { |
||||
return new RedirectResponse($targetPath); |
||||
} |
||||
|
||||
return new RedirectResponse($this->urlGenerator->generate('admin_dashboard_index')); |
||||
} |
||||
|
||||
protected function getLoginUrl() |
||||
{ |
||||
return $this->urlGenerator->generate('auth_login'); |
||||
} |
||||
} |
@ -1,24 +0,0 @@
|
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of Twig. |
||||
* |
||||
* (c) Fabien Potencier |
||||
* |
||||
* For the full copyright and license information, please view the LICENSE |
||||
* file that was distributed with this source code. |
||||
*/ |
||||
|
||||
namespace App\Core\Bundle; |
||||
|
||||
use App\Core\DependencyInjection\CoreExtension; |
||||
use Symfony\Component\HttpKernel\Bundle\Bundle; |
||||
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; |
||||
|
||||
class CoreBundle extends Bundle |
||||
{ |
||||
public function getContainerExtension(): ?ExtensionInterface |
||||
{ |
||||
return new CoreExtension(); |
||||
} |
||||
} |
@ -1,73 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Cache; |
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application; |
||||
use Symfony\Component\Console\Input\ArrayInput; |
||||
use Symfony\Component\Console\Output\BufferedOutput; |
||||
use Symfony\Component\Finder\Finder; |
||||
use Symfony\Component\HttpKernel\KernelInterface; |
||||
use Symfony\Contracts\HttpClient\HttpClientInterface; |
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
||||
use Symfony\Component\HttpClient\Exception\ClientException; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
use Symfony\Component\HttpClient\Exception\TransportException; |
||||
|
||||
/** |
||||
* class SymfonyCacheManager. |
||||
* |
||||
* @author Simon Vieille <simon@deblan.fr> |
||||
*/ |
||||
class SymfonyCacheManager |
||||
{ |
||||
protected KernelInterface $kernel; |
||||
protected HttpClientInterface $httpClient; |
||||
protected UrlGeneratorInterface $urlGenerator; |
||||
|
||||
public function __construct(KernelInterface $kernel, HttpClientInterface $httpClient, UrlGeneratorInterface $urlGenerator) |
||||
{ |
||||
$this->kernel = $kernel; |
||||
$this->httpClient = $httpClient; |
||||
$this->urlGenerator = $urlGenerator; |
||||
} |
||||
|
||||
public function cleanRouting() |
||||
{ |
||||
$finder = new Finder(); |
||||
$finder |
||||
->in($this->kernel->getCacheDir()) |
||||
->depth('== 0') |
||||
->name('url_*.php*') |
||||
; |
||||
|
||||
$pingUrl = $this->urlGenerator->generate('_ping', [], UrlGeneratorInterface::ABSOLUTE_URL); |
||||
|
||||
foreach ($finder as $file) { |
||||
unlink((string) $file->getPathname()); |
||||
} |
||||
|
||||
try { |
||||
// Hack: used to regenerate cache of url generator |
||||
$this->httpClient->request('POST', $pingUrl); |
||||
} catch (ClientException $e) { |
||||
} catch (TransportException $e) { |
||||
} |
||||
} |
||||
|
||||
public function cleanAll(OutputInterface $output = null) |
||||
{ |
||||
$application = new Application($this->kernel); |
||||
$application->setAutoExit(false); |
||||
|
||||
if (null === $output) { |
||||
$output = new BufferedOutput(); |
||||
} |
||||
|
||||
$input = new ArrayInput([ |
||||
'command' => 'cache:warmup', |
||||
'-e' => $this->kernel->getEnvironment(), |
||||
]); |
||||
|
||||
$application->run($input, $output); |
||||
} |
||||
} |
@ -1,108 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Command; |
||||
|
||||
use App\Core\Factory\UserFactory; |
||||
use App\Core\Manager\EntityManager; |
||||
use Symfony\Component\Console\Command\Command; |
||||
use Symfony\Component\Console\Input\InputArgument; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Input\InputOption; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
use Symfony\Component\Console\Question\ConfirmationQuestion; |
||||
use Symfony\Component\Console\Question\Question; |
||||
use Symfony\Component\Console\Style\SymfonyStyle; |
||||
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface; |
||||
|
||||
class UserCreateCommand extends Command |
||||
{ |
||||
protected static $defaultName = 'murph:user:create'; |
||||
protected static $defaultDescription = 'Creates a user'; |
||||
protected UserFactory $userFactory; |
||||
protected EntityManager $entityManager; |
||||
protected TokenGeneratorInterface $tokenGenerator; |
||||
|
||||
public function __construct( |
||||
UserFactory $userFactory, |
||||
EntityManager $entityManager, |
||||
TokenGeneratorInterface $tokenGenerator |
||||
) { |
||||
$this->userFactory = $userFactory; |
||||
$this->entityManager = $entityManager; |
||||
$this->tokenGenerator = $tokenGenerator; |
||||
|
||||
parent::__construct(); |
||||
} |
||||
|
||||
protected function configure() |
||||
{ |
||||
$this |
||||
->setDescription(self::$defaultDescription) |
||||
->addArgument('email', InputArgument::OPTIONAL, 'E-mail') |
||||
->addOption('is-admin', null, InputOption::VALUE_NONE, 'Add the admin role') |
||||
->addOption('is-writer', null, InputOption::VALUE_NONE, 'Add the write role') |
||||
; |
||||
} |
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int |
||||
{ |
||||
$io = new SymfonyStyle($input, $output); |
||||
$helper = $this->getHelper('question'); |
||||
|
||||
$emailQuestion = new Question('E-mail: '); |
||||
$emailQuestion->setValidator(function ($value) { |
||||
if (empty($value)) { |
||||
throw new \RuntimeException('The email must not be empty.'); |
||||
} |
||||
|
||||
return $value; |
||||
}); |
||||
|
||||
$passwordQuestion = new Question('Password (leave empty to generate a random password): '); |
||||
$passwordQuestion->setHidden(true); |
||||
|
||||
$isAdminDefault = $input->getOption('is-admin'); |
||||
$isWriterDefault = $input->getOption('is-writer'); |
||||
|
||||
$isAdminQuestionLabel = sprintf('Administrator [%s] ', $isAdminDefault ? 'Y/n' : 'y/N'); |
||||
$isWriterQuestionLabel = sprintf('Writer [%s] ', $isWriterDefault ? 'Y/n' : 'y/N'); |
||||
|
||||
$isAdminQuestion = new ConfirmationQuestion($isAdminQuestionLabel, $isAdminDefault); |
||||
$isWriterQuestion = new ConfirmationQuestion($isWriterQuestionLabel, $isWriterDefault); |
||||
|
||||
$io->section('Authentication'); |
||||
|
||||
$email = $input->getArgument('email'); |
||||
|
||||
if (empty($email)) { |
||||
$email = $helper->ask($input, $output, $emailQuestion); |
||||
} |
||||
|
||||
$password = $helper->ask($input, $output, $passwordQuestion); |
||||
|
||||
$showPassword = empty($password); |
||||
|
||||
if ($showPassword) { |
||||
$password = mb_substr($this->tokenGenerator->generateToken(), 0, 18); |
||||
$io->info(sprintf('Password: %s', $password)); |
||||
} else { |
||||
$io->newLine(); |
||||
} |
||||
|
||||
$io->section('Roles'); |
||||
|
||||
$isAdmin = $helper->ask($input, $output, $isAdminQuestion); |
||||
$isWriter = $helper->ask($input, $output, $isWriterQuestion); |
||||
|
||||
$user = $this->userFactory->create($email, $password); |
||||
$user->setIsAdmin($isAdmin); |
||||
$user->setIsWriter($isWriter); |
||||
|
||||
$this->entityManager->create($user); |
||||
|
||||
$io->newLine(); |
||||
$io->success('User created!'); |
||||
|
||||
return Command::SUCCESS; |
||||
} |
||||
} |
@ -1,150 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Controller\Account; |
||||
|
||||
use App\Core\Controller\Admin\AdminController; |
||||
use App\Core\Manager\EntityManager; |
||||
use App\Repository\UserRepository; |
||||
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface; |
||||
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface as TotpAuthenticatorInterface; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\Routing\Annotation\Route; |
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; |
||||
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface; |
||||
use ZxcvbnPhp\Zxcvbn; |
||||
|
||||
/** |
||||
* @Route("/admin/account") |
||||
*/ |
||||
class AccountAdminController extends AdminController |
||||
{ |
||||
/** |
||||
* @Route("/", name="admin_account") |
||||
*/ |
||||
public function account(Request $request, TotpAuthenticatorInterface $totpAuthenticatorService): Response |
||||
{ |
||||
$account = $this->getUser(); |
||||
|
||||
return $this->render('@Core/account/admin/edit.html.twig', [ |
||||
'account' => $account, |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* @Route("/2fa", name="admin_account_2fa") |
||||
*/ |
||||
public function twoFactorAuthentication( |
||||
Request $request, |
||||
GoogleAuthenticatorInterface $totpAuthenticatorService, |
||||
EntityManager $entityManager |
||||
): Response { |
||||
if ($request->isMethod('GET')) { |
||||
return $this->redirectToRoute('admin_account'); |
||||
} |
||||
|
||||
$account = $this->getUser(); |
||||
$csrfToken = $request->request->get('_csrf_token'); |
||||
$enable = (bool) $request->request->get('enable'); |
||||
$code = $request->request->get('code', ''); |
||||
$secret = $request->request->get('secret', ''); |
||||
$qrCodeContent = null; |
||||
|
||||
if ($this->isCsrfTokenValid('2fa', $csrfToken)) { |
||||
if ($enable && !$account->isTotpAuthenticationEnabled()) { |
||||
if (empty($secret)) { |
||||
$secret = $totpAuthenticatorService->generateSecret(); |
||||
|
||||
$account->setTotpSecret($secret); |
||||
|
||||
$qrCodeContent = $totpAuthenticatorService->getQRContent($account); |
||||
} else { |
||||
$account->setTotpSecret($secret); |
||||
|
||||
$qrCodeContent = $totpAuthenticatorService->getQRContent($account); |
||||
|
||||
if (!$totpAuthenticatorService->checkCode($account, $code)) { |
||||
$this->addFlash('error', 'The code is not valid.'); |
||||
} else { |
||||
$this->addFlash('success', 'Double authentication enabled.'); |
||||
|
||||
$entityManager->update($account); |
||||
|
||||
return $this->redirectToRoute('admin_account'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (!$enable && $account->isTotpAuthenticationEnabled()) { |
||||
$account->setTotpSecret(null); |
||||
|
||||
$entityManager->update($account); |
||||
|
||||
$this->addFlash('success', 'Double authentication disabled.'); |
||||
|
||||
return $this->redirectToRoute('admin_account'); |
||||
} |
||||
} |
||||
|
||||
return $this->render('@Core/account/admin/edit.html.twig', [ |
||||
'account' => $account, |
||||
'twoFaKey' => $secret, |
||||
'twoFaQrCodeContent' => $qrCodeContent, |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* @Route("/password", name="admin_account_password", methods={"POST"}) |
||||
*/ |
||||
public function password( |
||||
Request $request, |
||||
UserRepository $repository, |
||||
TokenGeneratorInterface $tokenGenerator, |
||||
UserPasswordEncoderInterface $encoder, |
||||
EntityManager $entityManager |
||||
): Response { |
||||
$account = $this->getUser(); |
||||
$csrfToken = $request->request->get('_csrf_token'); |
||||
|
||||
if ($this->isCsrfTokenValid('password', $csrfToken)) { |
||||
$password = $request->request->get('password'); |
||||
|
||||
if (!$encoder->isPasswordValid($account, $password)) { |
||||
$this->addFlash('error', 'The form is not valid.'); |
||||
|
||||
return $this->redirectToRoute('admin_account'); |
||||
} |
||||
|
||||
$password1 = $request->request->get('password1'); |
||||
$password2 = $request->request->get('password2'); |
||||
|
||||
$zxcvbn = new Zxcvbn(); |
||||
$strength = $zxcvbn->passwordStrength($password1, []); |
||||
|
||||
if (4 === $strength['score'] && $password1 === $password2) { |
||||
$account |
||||
->setPassword($encoder->encodePassword($account, $password1)) |
||||
->setConfirmationToken($tokenGenerator->generateToken()) |
||||
; |
||||
|
||||
$entityManager->update($account); |
||||
|
||||
$this->addFlash('success', 'Password updated.'); |
||||
|
||||
return $this->redirectToRoute('admin_account'); |
||||
} |
||||
} |
||||
|
||||
$this->addFlash('error', 'The form is not valid.'); |
||||
|
||||
return $this->redirectToRoute('admin_account'); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function getSection(): string |
||||
{ |
||||
return 'account'; |
||||
} |
||||
} |
@ -1,41 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Controller\Admin; |
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\Routing\Annotation\Route; |
||||
|
||||
abstract class AdminController extends AbstractController |
||||
{ |
||||
protected array $coreParameters; |
||||
|
||||
public function __construct(ParameterBagInterface $parameters) |
||||
{ |
||||
$this->coreParameters = $parameters->get('core'); |
||||
} |
||||
|
||||
/** |
||||
* @Route("/_ping", name="_ping") |
||||
*/ |
||||
public function ping() |
||||
{ |
||||
return $this->json(true); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function render(string $view, array $parameters = [], Response $response = null): Response |
||||
{ |
||||
$parameters['section'] = $this->getSection(); |
||||
$parameters['site_name'] = $this->coreParameters['site']['name']; |
||||
$parameters['site_logo'] = $this->coreParameters['site']['logo']; |
||||
$parameters['murph_version'] = defined('MURPH_VERSION') ? MURPH_VERSION : null; |
||||
|
||||
return parent::render($view, $parameters, $response); |
||||
} |
||||
|
||||
abstract protected function getSection(): string; |
||||
} |
@ -1,342 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Controller\Admin\Crud; |
||||
|
||||
use App\Core\Controller\Admin\AdminController; |
||||
use App\Core\Crud\CrudConfiguration; |
||||
use App\Core\Entity\EntityInterface; |
||||
use App\Core\Manager\EntityManager; |
||||
use App\Core\Repository\RepositoryQuery; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\HttpFoundation\Session\Session; |
||||
|
||||
/** |
||||
* class CrudController. |
||||
* |
||||
* @author Simon Vieille <simon@deblan.fr> |
||||
*/ |
||||
abstract class CrudController extends AdminController |
||||
{ |
||||
protected array $filters = []; |
||||
protected array $sort = [ |
||||
'label' => null, |
||||
'direction' => null, |
||||
]; |
||||
|
||||
abstract protected function getConfiguration(): CrudConfiguration; |
||||
|
||||
protected function doIndex(int $page = 1, RepositoryQuery $query, Request $request, Session $session): Response |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
|
||||
$this->applySort('index', $query, $request); |
||||
$this->updateFilters($request, $session); |
||||
|
||||
$pager = $query |
||||
->usefilters($this->filters) |
||||
->paginate($page, $configuration->getmaxperpage('index')) |
||||
; |
||||
|
||||
return $this->render($this->getConfiguration()->getView('index'), [ |
||||
'configuration' => $configuration, |
||||
'pager' => $pager, |
||||
'sort' => $this->sort, |
||||
'filters' => [ |
||||
'show' => null !== $configuration->getForm('filter'), |
||||
'isEmpty' => empty($this->filters), |
||||
], |
||||
]); |
||||
} |
||||
|
||||
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeCreate = null): Response |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
|
||||
$this->prepareEntity($entity); |
||||
|
||||
$form = $this->createForm($configuration->getForm('new'), $entity, $configuration->getFormOptions('new')); |
||||
|
||||
if ($request->isMethod('POST')) { |
||||
$form->handleRequest($request); |
||||
|
||||
if ($form->isValid()) { |
||||
if (null !== $beforeCreate) { |
||||
call_user_func_array($beforeCreate, [$entity, $form, $request]); |
||||
} |
||||
|
||||
$entityManager->create($entity); |
||||
$this->addFlash('success', 'The data has been saved.'); |
||||
|
||||
return $this->redirectToRoute($configuration->getPageRoute('edit'), array_merge( |
||||
['entity' => $entity->getId()], |
||||
$configuration->getPageRouteParams('edit') |
||||
)); |
||||
} |
||||
$this->addFlash('warning', 'The form is not valid.'); |
||||
} |
||||
|
||||
return $this->render($configuration->getView('new'), [ |
||||
'form' => $form->createView(), |
||||
'configuration' => $configuration, |
||||
'entity' => $entity, |
||||
]); |
||||
} |
||||
|
||||
protected function doShow(EntityInterface $entity): Response |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
|
||||
return $this->render($configuration->getView('show'), [ |
||||
'entity' => $entity, |
||||
'configuration' => $configuration, |
||||
]); |
||||
} |
||||
|
||||
protected function doEdit(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null): Response |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
|
||||
$this->prepareEntity($entity); |
||||
|
||||
$form = $this->createForm($configuration->getForm('edit'), $entity, $configuration->getFormOptions('edit')); |
||||
|
||||
if ($request->isMethod('POST')) { |
||||
$form->handleRequest($request); |
||||
|
||||
if ($form->isValid()) { |
||||
if (null !== $beforeUpdate) { |
||||
call_user_func_array($beforeUpdate, [$entity, $form, $request]); |
||||
} |
||||
|
||||
$entityManager->update($entity); |
||||
$this->addFlash('success', 'The data has been saved.'); |
||||
|
||||
return $this->redirectToRoute($configuration->getPageRoute('edit'), array_merge( |
||||
['entity' => $entity->getId()], |
||||
$configuration->getPageRouteParams('edit') |
||||
)); |
||||
} |
||||
$this->addFlash('warning', 'The form is not valid.'); |
||||
} |
||||
|
||||
return $this->render($configuration->getView('edit'), [ |
||||
'form' => $form->createView(), |
||||
'configuration' => $configuration, |
||||
'entity' => $entity, |
||||
]); |
||||
} |
||||
|
||||
protected function doSort(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
$context = $request->query->get('context', 'index'); |
||||
|
||||
if (!$configuration->getIsSortableCollection($context)) { |
||||
throw $this->createNotFoundException(); |
||||
} |
||||
|
||||
$this->applySort($context, $query, $request); |
||||
$this->updateFilters($request, $session); |
||||
|
||||
$pager = $query |
||||
->useFilters($this->filters) |
||||
->paginate($page, $configuration->getMaxPerPage($context)) |
||||
; |
||||
|
||||
if ($this->isCsrfTokenValid('sort', $request->query->get('_token'))) { |
||||
$items = $request->request->get('items', []); |
||||
$setter = 'set'.$configuration->getSortableCollectionProperty(); |
||||
$orderStart = ($page - 1) * $configuration->getMaxPerPage($context); |
||||
|
||||
foreach ($pager as $key => $entity) { |
||||
if (isset($items[$key + 1])) { |
||||
$entity->{$setter}($items[$key + 1] + $orderStart); |
||||
|
||||
$entityManager->update($entity); |
||||
} |
||||
} |
||||
|
||||
$this->addFlash('success', 'The data has been saved.'); |
||||
} else { |
||||
$this->addFlash('warning', 'The form is not valid.'); |
||||
} |
||||
|
||||
return $this->json([]); |
||||
} |
||||
|
||||
protected function doBatch(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
$datas = $request->request->get('batch', []); |
||||
|
||||
$context = $datas['context'] ?? 'index'; |
||||
$target = $datas['target'] ?? null; |
||||
$action = $datas['action'] ?? null; |
||||
$token = $datas['_token'] ?? null; |
||||
$items = $datas['items'] ?? []; |
||||
$batchAction = $configuration->getBatchAction($context, $action); |
||||
|
||||
if (empty($context) || empty($action) || empty($target)) { |
||||
return $this->json([]); |
||||
} |
||||
|
||||
if (!$this->isCsrfTokenValid('batch', $token) || empty($batchAction)) { |
||||
$this->addFlash('warning', 'The form is not valid.'); |
||||
|
||||
return $this->json([]); |
||||
} |
||||
|
||||
$callback = $batchAction['callback']; |
||||
|
||||
$this->applySort($context, $query, $request); |
||||
$this->updateFilters($request, $session); |
||||
|
||||
$query->useFilters($this->filters); |
||||
|
||||
if ('selection' === $target) { |
||||
$isSelection = true; |
||||
$pager = $query->paginate($page, $configuration->getMaxPerPage($context)); |
||||
} else { |
||||
$isSelection = false; |
||||
$pager = $query->find(); |
||||
} |
||||
|
||||
foreach ($pager as $key => $entity) { |
||||
if (($isSelection && isset($items[$key + 1])) || !$isSelection) { |
||||
$callback($entity, $entityManager); |
||||
} |
||||
} |
||||
|
||||
$this->addFlash('success', 'Batch action done.'); |
||||
|
||||
return $this->json([]); |
||||
} |
||||
|
||||
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null): Response |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
|
||||
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) { |
||||
if (null !== $beforeDelete) { |
||||
call_user_func($beforeDelete, $entity); |
||||
} |
||||
|
||||
$entityManager->delete($entity); |
||||
|
||||
$this->addFlash('success', 'The data has been removed.'); |
||||
} |
||||
|
||||
return $this->redirectToRoute($configuration->getPageRoute('index')); |
||||
} |
||||
|
||||
protected function doFilter(Session $session): Response |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
$type = $configuration->getForm('filter'); |
||||
|
||||
if (null === $type) { |
||||
throw $this->createNotFoundException(); |
||||
} |
||||
|
||||
$form = $this->createForm($type); |
||||
$form->submit($session->get($form->getName(), [])); |
||||
|
||||
return $this->render($configuration->getView('filter'), [ |
||||
'form' => $form->createView(), |
||||
'configuration' => $configuration, |
||||
]); |
||||
} |
||||
|
||||
protected function updateFilters(Request $request, Session $session) |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
$type = $configuration->getForm('filter'); |
||||
|
||||
if (null === $type) { |
||||
return; |
||||
} |
||||
|
||||
$form = $this->createForm($type); |
||||
|
||||
if ($request->query->has($form->getName())) { |
||||
$filters = $request->query->get($form->getName()); |
||||
|
||||
if ('0' === $filters) { |
||||
$filters = []; |
||||
} |
||||
} elseif ($session->has($form->getName())) { |
||||
$filters = $session->get($form->getName()); |
||||
} else { |
||||
$filters = []; |
||||
} |
||||
|
||||
$form->submit($filters); |
||||
|
||||
if (empty($filters)) { |
||||
$this->filters = $filters; |
||||
$session->set($form->getName(), $filters); |
||||
} elseif ($form->isValid()) { |
||||
$this->filters = $form->getData(); |
||||
$session->set($form->getName(), $filters); |
||||
} |
||||
} |
||||
|
||||
protected function prepareEntity(EntityInterface $entity) |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
|
||||
if ($configuration->isI18n()) { |
||||
foreach ($configuration->getLocales() as $locale) { |
||||
$entity->addTranslation($entity->translate($locale, false)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected function applySort(string $context, RepositoryQuery $query, Request $request) |
||||
{ |
||||
$configuration = $this->getConfiguration(); |
||||
|
||||
if ($configuration->getIsSortableCollection($context)) { |
||||
$query->orderBy(sprintf('.%s', $configuration->getSortableCollectionProperty())); |
||||
|
||||
return; |
||||
} |
||||
|
||||
$defaultSort = $configuration->getDefaultSort($context); |
||||
|
||||
$name = $request->query->get('_sort', $defaultSort['label'] ?? null); |
||||
$direction = strtolower($request->query->get('_sort_direction', $defaultSort['direction'] ?? 'asc')); |
||||
|
||||
if (!in_array($direction, ['asc', 'desc'])) { |
||||
$direction = 'asc'; |
||||
} |
||||
|
||||
foreach ($configuration->getFields($context) as $label => $field) { |
||||
$sortOption = $field['options']['sort'] ?? null; |
||||
|
||||
if (null === $sortOption) { |
||||
continue; |
||||
} |
||||
|
||||
if ($sortOption[0] !== $name) { |
||||
continue; |
||||
} |
||||
|
||||
$sorter = $sortOption[1]; |
||||
|
||||
if (is_string($sorter)) { |
||||
$query->orderBy($sorter, $direction); |
||||
} else { |
||||
call_user_func_array($sorter, [$query, $direction]); |
||||
} |
||||
|
||||
$this->sort = [ |
||||
'label' => $label, |
||||
'direction' => $direction, |
||||
]; |
||||
|
||||
return; |
||||
} |
||||
} |
||||
} |
@ -1,38 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Controller\Analytic; |
||||
|
||||
use App\Core\Analytic\DateRangeAnalytic; |
||||
use App\Core\Entity\Site\Node; |
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\Routing\Annotation\Route; |
||||
|
||||
/** |
||||
* @Route("/admin/analytic") |
||||
*/ |
||||
class AnalyticController extends AbstractController |
||||
{ |
||||
/** |
||||
* @Route("/stats/{node}/{range}", name="admin_analytic_stats") |
||||
*/ |
||||
public function stats(Node $node, DateRangeAnalytic $analytic, string $range = '7days'): Response |
||||
{ |
||||
if (!in_array($range, ['7days', '30days', '90days', '1year'])) { |
||||
throw $this->createNotFoundException(); |
||||
} |
||||
|
||||
$analytic |
||||
->setDateRange(new \DateTime('now - '.$range), new \DateTime()) |
||||
->setNode($node) |
||||
; |
||||
|
||||
return $this->render('@Core/analytic/stats.html.twig', [ |
||||
'range' => $range, |
||||
'views' => $analytic->getViews(), |
||||
'pathViews' => $analytic->getPathViews(), |
||||
'referers' => $analytic->getReferers(), |
||||
'node' => $node, |
||||
]); |
||||
} |
||||
} |
@ -1,155 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Controller\Auth; |
||||
|
||||
use App\Core\Event\Account\PasswordRequestEvent; |
||||
use App\Core\Manager\EntityManager; |
||||
use App\Repository\UserRepository; |
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; |
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\Routing\Annotation\Route; |
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; |
||||
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface; |
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; |
||||
use ZxcvbnPhp\Zxcvbn; |
||||
|
||||
class AuthController extends AbstractController |
||||
{ |
||||
protected array $coreParameters; |
||||
|
||||
public function __construct(ParameterBagInterface $parameters) |
||||
{ |
||||
$this->coreParameters = $parameters->get('core'); |
||||
} |
||||
|
||||
/** |
||||
* @Route("/login", name="auth_login") |
||||
*/ |
||||
public function login(AuthenticationUtils $authenticationUtils): Response |
||||
{ |
||||
if ($this->getUser()) { |
||||
return $this->redirectToRoute('admin_dashboard_index'); |
||||
} |
||||
|
||||
$error = $authenticationUtils->getLastAuthenticationError(); |
||||
$lastUsername = $authenticationUtils->getLastUsername(); |
||||
|
||||
return $this->render('@Core/auth/login.html.twig', [ |
||||
'last_username' => $lastUsername, |
||||
'error' => $error, |
||||
'site_name' => $this->coreParameters['site']['name'], |
||||
'site_logo' => $this->coreParameters['site']['logo'], |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* @Route("/resetting/request", name="auth_resetting_request") |
||||
*/ |
||||
public function requestResetting(Request $request, UserRepository $repository, EventDispatcherInterface $eventDispatcher): Response |
||||
{ |
||||
if ($this->getUser()) { |
||||
return $this->redirectToRoute('admin_dashboard_index'); |
||||
} |
||||
|
||||
if ($request->isMethod('POST')) { |
||||
$csrfToken = $request->request->get('_csrf_token'); |
||||
|
||||
if (!$this->isCsrfTokenValid('resetting_request', $csrfToken)) { |
||||
throw $this->createAccessDeniedException(); |
||||
} |
||||
|
||||
$username = trim((string) $request->request->get('username')); |
||||
|
||||
if (!$username) { |
||||
throw $this->createAccessDeniedException(); |
||||
} |
||||
|
||||
$account = $repository->findOneByEmail($username); |
||||
|
||||
if ($account) { |
||||
$requestedAt = $account->getPasswordRequestedAt(); |
||||
|
||||
if (null === $requestedAt || $requestedAt->getTimestamp() < (time() - 3600 / 2)) { |
||||
$eventDispatcher->dispatch(new PasswordRequestEvent($account), PasswordRequestEvent::EVENT); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return $this->render('@Core/auth/resetting_request.html.twig', [ |
||||
'email_sent' => $request->isMethod('POST'), |
||||
'site_name' => $this->coreParameters['site']['name'], |
||||
'site_logo' => $this->coreParameters['site']['logo'], |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* @Route("/resetting/update/{token}", name="auth_resetting_update") |
||||
*/ |
||||
public function requestUpdate( |
||||
string $token, |
||||
Request $request, |
||||
UserRepository $repository, |
||||
TokenGeneratorInterface $tokenGenerator, |
||||
UserPasswordEncoderInterface $encoder, |
||||
EntityManager $entityManager |
||||
): Response { |
||||
if ($this->getUser()) { |
||||
return $this->redirectToRoute('admin_dashboard_index'); |
||||
} |
||||
|
||||
$account = $repository->findOneByConfirmationToken($token); |
||||
$passwordUpdated = false; |
||||
$expired = true; |
||||
|
||||
if ($account) { |
||||
$requestedAt = $account->getPasswordRequestedAt(); |
||||
$expired = (null === $requestedAt || ($requestedAt->getTimestamp() < (time() - 3600 * 2))); |
||||
} |
||||
|
||||
if ($request->isMethod('POST') && !$expired) { |
||||
$csrfToken = $request->request->get('_csrf_token'); |
||||
|
||||
if ($this->isCsrfTokenValid('resetting_update', $csrfToken)) { |
||||
$password = $request->request->get('password'); |
||||
$password2 = $request->request->get('password2'); |
||||
|
||||
$zxcvbn = new Zxcvbn(); |
||||
$strength = $zxcvbn->passwordStrength($password, []); |
||||
|
||||
if (4 === $strength['score'] && $password === $password2) { |
||||
$account |
||||
->setPassword($encoder->encodePassword( |
||||
$account, |
||||
$password |
||||
)) |
||||
->setConfirmationToken($tokenGenerator->generateToken()) |
||||
->setPasswordRequestedAt(new \DateTime('now')) |
||||
; |
||||
|
||||
$entityManager->update($account); |
||||
|
||||
$passwordUpdated = true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return $this->render('@Core/auth/resetting_update.html.twig', [ |
||||
'password_updated' => $passwordUpdated, |
||||
'token' => $token, |
||||
'expired' => $expired, |
||||
'site_name' => $this->coreParameters['site']['name'], |
||||
'site_logo' => $this->coreParameters['site']['logo'], |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* @Route("/logout", name="auth_logout") |
||||
*/ |
||||
public function logout() |
||||
{ |
||||
throw new \Exception('This method can be blank - it will be intercepted by the logout key on your firewall'); |
||||
} |
||||
} |
@ -1,27 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Controller\Dashboard; |
||||
|
||||
use App\Core\Controller\Admin\AdminController; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\Routing\Annotation\Route; |
||||
|
||||
/** |
||||
* @Route("/admin") |
||||
*/ |
||||
class DashboardAdminController extends AdminController |
||||
{ |
||||
/** |
||||
* @Route("/", name="admin_dashboard_index") |
||||
*/ |
||||
public function index(): Response |
||||
{ |
||||
return $this->render('@Core/dashboard/index.html.twig', [ |
||||
]); |
||||
} |
||||
|
||||
protected function getSection(): string |
||||
{ |
||||
return 'dashboard'; |
||||
} |
||||
} |
@ -1,438 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace App\Core\Controller\FileManager; |
||||
|
||||
use App\Core\Controller\Admin\AdminController; |
||||
use App\Core\FileManager\FsFileManager; |
||||
use App\Core\Form\FileManager\DirectoryCreateType; |
||||
use App\Core\Form\FileManager\DirectoryRenameType; |
||||
use App\Core\Form\FileManager\FileInformationType; |
||||
use App\Core\Form\FileManager\FileRenameType; |
||||
use App\Core\Form\FileManager\FileUploadType; |
||||
use App\Core\Manager\EntityManager; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\Routing\Annotation\Route; |
||||
use Symfony\Contracts\Translation\TranslatorInterface; |
||||
|
||||
/** |
||||
* @Route("/admin/file_manager") |
||||
*/ |
||||
class FileManagerAdminController extends AdminController |
||||
{ |
||||
/** |
||||
* @Route("/", name="admin_file_manager_index") |
||||
*/ |
||||
public function index(): Response |
||||
{ |
||||
return $this->render('@Core/file_manager/index.html.twig'); |
||||
} |
||||
|
||||
/** |
||||
* @Route("/api/directory", name="admin_file_manager_api_directory", options={"expose"=true}) |
||||
*/ |
||||
public function directory(FsFileManager $manager, Request $request): Response |
||||
{ |
||||
$options = [ |
||||
'sort' => $request->query->get('_sort', 'name'), |
||||
'sort_direction' => $request->query->get('_sort_direction', 'asc'), |
||||
]; |
||||
|
||||
$files = $manager->list($request->query->get('directory', '/'), $options); |
||||
|
||||
return $this->json($files); |
||||
} |
||||
|
||||
/** |
||||
* @Route("/info/{tab}/{context}/{ajax}", name="admin_file_manager_info", options={"expose"=true}) |
||||
*/ |
||||
public function info( |
||||
FsFileManager $manager, |
||||
Request $request, |
||||
EntityManager $entityManager, |
||||
TranslatorInterface $translator, |
||||
string $context = 'crud', |
||||
string $tab = 'information', |
||||
bool $ajax = false |
||||
): Response { |
||||
$splInfo = $manager->getSplInfo($request->query->get('file')); |
||||
|
||||
if (!$splInfo) { |
||||
throw $this->createNotFoundException(); |
||||
} |
||||
|
||||
$fileInfo = $manager->getFileInformation($request->query->get('file')); |
||||
$path = $manager->getPathUri().'/'.$splInfo->getRelativePathname(); |
||||
|
||||
$form = $this->createForm(FileInformationType::class, $fileInfo); |
||||
|
||||
if ($request->isMethod(' |