<?php
namespace Diplix\KMGBundle\Controller;
use Cocur\Slugify\Slugify;
use Diplix\KMGBundle\Entity\Customer;
use Diplix\KMGBundle\Entity\File;
use Diplix\KMGBundle\Entity\Role;
use Diplix\KMGBundle\Entity\SessionParameters;
use Diplix\KMGBundle\Entity\User;
use Diplix\KMGBundle\Exception\PasswordAgeExceededException;
use Diplix\KMGBundle\Repository\FileRepository;
use Doctrine\ORM\AbstractQuery;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class BaseController extends \Symfony\Bundle\FrameworkBundle\Controller\AbstractController
{
/** @var FileRepository */
protected $fileRepo = null;
/**
* @var SessionParameters
*/
protected $sessionParameters = null;
/**
* @return SessionParameters
*/
protected function getSessionParameters()
{
if ($this->sessionParameters === null)
{
$session = $this->get("session");
$this->sessionParameters = $session->get(SessionParameters::SESSION_KEY, new SessionParameters());
}
return $this->sessionParameters;
}
protected function saveSessionParameters()
{
$session = $this->get("session");
$session->set(SessionParameters::SESSION_KEY, $this->sessionParameters);
}
/**
* Guess the needed content-type to respond to a json request
* @return string
*/
protected function guessJsonContentType()
{
return 'application/json'; // 9 years later, this seems to work right...
/* // Use plain browser-wide and let the js assume that the format will be json
// this seems to work with every browser
return 'text/plain';
*/
}
/**
* format an array as json response
* @param \Symfony\Component\HttpFoundation\Request $request
* @param array|\stdClass $responseData
* @param int $httpResponseCode
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function getJsonResponse($request, $responseData, $httpResponseCode = Response::HTTP_OK )
{
$resp = new Response(json_encode($responseData),
$httpResponseCode,
array( 'content-type'=> $this->guessJsonContentType(),
'cache-control'=>'no-store, no-cache, must-revalidate',
'pragma'=>'no-cache'));
$resp->prepare($request);
$resp->setCache(array('last_modified' => new \DateTime()));
$resp->expire();
return $resp;
}
/**
* format a simple json success / error response
* which looks like array( 'success'=>(true|false), 'error'=>(error message string) )
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param bool|string $responseData use true to indicate a success or a string message to indicate an error
* @deprecated
*/
protected function getJsonResultResponse($request,$responseData)
{
if ($responseData===true)
return $this->getJsonDataResponse($request,true,null);
else
return $this->getJsonDataResponse($request,false,['error'=>$responseData] ,$responseData);
}
protected function getJsonDataResponse($request,$success = true, $responseData=null, $message=null)
{
$ret = array( 'success'=>$success,
"message"=> $message != null ? $message : ($success? "OK." : "Failed."),
"data" => $responseData );
// compat for exceptions with old format
if ( (is_array($responseData))&&(array_key_exists('error',$responseData)) )
{
$ret['error'] = $responseData['error'];
}
else
// compat for ret = false with old format
if (!$success)
{
$ret['error'] = var_export($responseData,true);
}
return $this->getJsonResponse($request, $ret, Response::HTTP_OK /* a failed request will kill our js code flow :: $success ? Response::HTTP_OK : Response::HTTP_BAD_REQUEST */);
}
/**
* get the the currently logged in user
* @return User
*/
protected function getCurrentUser($refresh = false)
{
$token = $this->container->get('security.token_storage')->getToken();
$u = $token->getUser();
if ($refresh)
{
//$u = $this->getDoctrine()->getRepository(User::class)->findOneBy(["id"=>$u->getId()]);
$r = $this->getDoctrine()->getRepository(User::class);
$u = $r->createQueryBuilder("u")
->select("u,c,s,sc")
->leftJoin("u.customer","c")
->leftJoin("u.currentSubstituteUser","s")
->leftJoin("s.customer","sc")
->where("u.id = :id")
->setParameter("id",$u->getId())
->getQuery()
->getSingleResult(AbstractQuery::HYDRATE_OBJECT);
//$this->addFlash("notice",$u->getCustomer()->getId());
//$this->addFlash("notice",get_class($u->getCustomer()));
}
return $u;
}
/**
* @param $roleIdentifier
* @param bool|false $exclusive set to true to disable a positive result for SUPER_ADMIN
* @return bool
*/
protected function hasUserRole($roleIdentifier,$exclusive=false)
{
$sac = $this->get('security.authorization_checker');
$super = (!$exclusive)&&$sac->isGranted(Role::SUPER_ADMIN);
if (false === $super) // super admin role allows everything
{
return $sac->isGranted($roleIdentifier);
}
else
{
return true;
}
}
/**
* Ensure that a User hat the given role
* @param string $roleIdentifier
* @throws AccessDeniedException
*/
protected function ensureUserHasRole($roleIdentifier)
{
if (!$this->hasUserRole($roleIdentifier))
{
throw new AccessDeniedException();
}
}
/**
* Issue an access denied response
*
*/
protected function accessDenied(Request $request, $optionalMessage = '')
{
$out = $this->render('@Twig/Exception/error403.html.twig',array('message'=>$optionalMessage));
return $out;
}
protected function getDownloadResponse($fullPathToFile,$fileName)
{
// @see http://stackoverflow.com/questions/25680932/symfony2-file-download-and-the-filename-fallback-must-only-contain-ascii-char
// @see https://github.com/symfony/symfony/issues/9093
$sl = new Slugify();
$fnFallback = $sl->slugify($fileName); // need to provide a ascii fallback for filename to be http conform
$br = new BinaryFileResponse($fullPathToFile, 200, array(), true);
$br->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$fileName, // or '' to guess the file name from $file
$fnFallback
);
return $br;
}
// /**
// * @param string $type
// * @param string|array $message
// */
// protected function addFlash($type, $message): void
// {
// parent::addFlash($type, $message);
// }
protected function checkPasswordAge()
{
$up = $this->getCurrentUser()->getLastPasswordChange();
$age = $this->getCurrentUser()->getCustomer()->getMaxPasswordAge();
if ($age > 0)
{
$di = new \DateInterval(sprintf("P%dD", $age));
if (
(!is_object($up)) ||
($up->add($di) < new \DateTime())
)
{
throw new PasswordAgeExceededException();
}
}
}
protected function getCustomerContextFromSession($required=true)
{
$ctx = $this->getSessionParameters()->selectedCustomer;
if ($required)
{
if (! is_object($ctx)) throw new \Exception("No customer selected.");
}
if (is_object($ctx))
{
// refresh, then detach
$ctx = $this->getDoctrine()->getManager()->find(Customer::class,$ctx->getId());
$this->getDoctrine()->getManager()->detach($ctx);
// for unknown reasons, we need to access the object once in code
// after detaching. only then we can use it correctly in in twig in every case
// todo: check this weird behaviour
$dummy = $ctx->getName();
}
return $ctx;
}
protected function handleFileUpload(FormInterface $form, $entity, $fieldName, $fileType)
{
// TODO: NOT WORKING WITH PHP8 !!!!! (form->getData() is always null)
if ($this->fileRepo === null)
{
throw new \RuntimeException("fileRepo not set. Update controller !");
}
$accessor = PropertyAccess::createPropertyAccessor();
$exist = $accessor->getValue($entity,$fieldName);
/* @var $file UploadedFile */
$file = $form->get($fieldName)->getData();
if ($file !== null)
{
if ($fileType===null)
{
$fileType = $file->getMimeType();
}
$newObj = null;
try
{
$newObj = $this->fileRepo->receiveUpload($fileType, "img", $file);
}
catch (\Exception $ex)
{
$this->addFlash('warning', "File Upload Error: " . $ex->getMessage());
return false;
}
// new file successfully received at this point, delete an existing one
if (($exist !== null) && ($exist instanceof File)) $this->fileRepo->delete($exist);
// persist the new one
$this->fileRepo->persistFlush($newObj);
// attach it
$accessor->setValue($entity, $fieldName, $newObj);
$this->addFlash("info", sprintf("Upload %s received.", $newObj->getFileName()));
}
else
{
// we only need to handle an explicit deletion if there has been no new file
$del = ($form->has($fieldName.'_delete') ? $form->get($fieldName.'_delete')->getData() : 0 );
if ($del == 1)
{
if ( ($exist !== null) && ($exist instanceof File) )
{
$this->fileRepo->delete($exist);
$this->addFlash("info",sprintf("File %s has been deleted.",$exist->getFileName()));
}
$accessor->setValue($entity,$fieldName,null);
}
}
return true;
}
}