<?php
namespace App\Controller;
use App\Entity\Channel;
use App\Form\ChannelType;
use App\Image\Processor;
use App\Repository\ChannelRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
/**
* @Route("/channel")
*/
class ChannelController extends AbstractController
{
/**
* @IsGranted("ROLE_USER")
* @Route("/new", name="channel_new", methods={"GET","POST"})
*/
public function new(Request $request): Response
{
$channel = new Channel();
$channel->setKey(substr(base64_encode(random_bytes(30)), 0, 16));
$channel->setUser($this->getUser());
$form = $this->createForm(ChannelType::class, $channel);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($channel);
$entityManager->flush();
return $this->redirectToRoute('channel_edit', ['id' => $channel->getId()], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('channel/new.html.twig', [
'channel' => $channel,
'form' => $form,
]);
}
private function getFileName(Channel $channel): string
{
return "raw.jpg";
}
private function processPostFile(Request $request, Channel $channel)
{
if($request->files->count() === 0) {
throw new BadRequestHttpException("No file uploaded");
}
$first = $request->files->keys()[0];
/** @var UploadedFile $uploadedImage */
$uploadedImage = $request->files->get($first);
if ($uploadedImage->getError()) {
throw new BadRequestHttpException($uploadedImage->getErrorMessage());
}
if (!$uploadedImage->getSize()) {
throw new BadRequestHttpException("Zero size");
}
$mime = $uploadedImage->getMimeType();
$allowedMime = ['image/jpeg'];
if (!in_array($mime, $allowedMime)) {
throw new BadRequestHttpException("MIME \"$mime\" is not allowed");
}
$processor = new Processor();
$path = $channel->getDataDirectory(true) . $this->getFileName($channel);
$processor->process($uploadedImage->getContent(), $path, $channel);
$channel->ensureDirectory();
$uploadedImage->move($channel->getDataDirectory(), $this->getFileName($channel));
}
private function processPutFile(Request $request, Channel $channel)
{
$content = $request->getContent();
$channel->ensureDirectory();
$path = $channel->getDataDirectory(true) . $this->getFileName($channel);
$res = file_put_contents($path, $content);
if ($res === false) {
throw new BadRequestHttpException("Write file error");
}
if ($res === 0) {
@unlink($path);
throw new BadRequestHttpException("Zero file written?!");
}
$processor = new Processor();
$path = $channel->getDataDirectory(true) . $this->getFileName($channel);
$processor->process($content, $path, $channel);
}
/**
* @Route("/{slug}", name="channel_show", methods={"GET", "POST", "PUT"})
*/
public function show(Request $request, Channel $channel): Response
{
if ($request->getMethod() !== 'GET') {
$key = $request->get('key') ?? $request->server->get('QUERY_STRING');
if (empty($key)) {
throw new BadRequestHttpException("No key in request");
}
if ($key !== $channel->getKey()) {
throw new BadRequestHttpException("Invalid channel key '".$key."'");
}
if ($request->getMethod() === 'POST') {
$this->processPostFile($request, $channel);
} else if ($request->getMethod() === 'PUT') {
$this->processPutFile($request, $channel);
} else {
throw new BadRequestHttpException("Method ".$request->getMethod()." not yet allowed");
}
$channel->setLastUploadAt(new \DateTime());
$this->getDoctrine()->getManager()->flush();
return new Response('OK');
}
return $this->render('channel/show.html.twig', [
'channel' => $channel,
]);
}
/**
* @Route("/{slug}/last", name="channel_last", methods={"GET"})
*/
public function last(Request $request, Channel $channel): Response
{
return new Response($channel->getLastUploadAt()->getTimestamp());
}
/**
* @IsGranted("ROLE_USER")
* @Route("/{id}/edit", name="channel_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Channel $channel): Response
{
$this->checkOwnership($channel);
$form = $this->createForm(ChannelType::class, $channel);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('user', [], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('channel/edit.html.twig', [
'channel' => $channel,
'form' => $form,
]);
}
/**
* @IsGranted("ROLE_USER")
* @Route("/{id}/rekey", name="channel_rekey", methods={"GET"})
*/
public function rekey(Request $request, Channel $channel): Response
{
$this->checkOwnership($channel);
$channel->setKey(substr(base64_encode(random_bytes(30)), 0, 16));
$entityManager = $this->getDoctrine()->getManager();
$entityManager->flush();
return $this->redirectToRoute('channel_edit', ['id' => $channel->getId()], Response::HTTP_SEE_OTHER);
}
/**
* @IsGranted("ROLE_USER")
* @Route("/{id}/delete", name="channel_delete", methods={"POST"})
*/
public function delete(Request $request, Channel $channel): Response
{
$this->checkOwnership($channel);
if ($this->isCsrfTokenValid('delete'.$channel->getId(), $request->request->get('_token'))) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($channel);
$entityManager->flush();
}
return $this->redirectToRoute('user', [], Response::HTTP_SEE_OTHER);
}
private function checkOwnership(Channel $channel)
{
if ($channel->getUser()->getId() != $this->getUser()->getId()) {
throw new AccessDeniedHttpException("Nice try, you don't have permissions to do that");
}
}
}