<?php
namespace App\Controller;
use App\Entity\Comp;
use App\Entity\CompFramework;
use App\Entity\CompScale;
use App\Entity\CompScaleValueDescription;
use App\Entity\Position;
use App\Entity\Skill;
use App\Entity\SkillScale;
use App\Enum\CsrfToken;
use App\Enum\FileArea;
use App\Exception\ValidationException;
use App\Model\Assessment\CompetencyFrameworkModel;
use App\Model\Assessment\CompetencyModel;
use App\Model\Assessment\CompetencyScaleModel;
use App\Model\Assessment\PositionModel;
use App\Model\Assessment\SkillModel;
use App\Model\Assessment\SkillScaleModel;
use App\Model\Common\ResponseModel;
use App\Response\ApiResponse;
use App\Service\Config\ConfigLoader;
use App\Service\File\Exceptions\FileNotFoundException;
use App\Service\File\FileService;
use App\Validator\AppValidator;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Throwable;
use App\Entity\File;
/**
* Class HomeController
* @package App\Controller
*/
class HomeController extends AbstractController
{
private $em;
private $logger;
private $configLoader;
private $fileService;
private $csrfTokenManager;
/**
* HomeController constructor.
*
* @param EntityManagerInterface $em
* @param LoggerInterface $logger
*/
public function __construct(EntityManagerInterface $em, LoggerInterface $logger, ConfigLoader $configLoader, FileService $fileService, CsrfTokenManagerInterface $csrfTokenManager)
{
$this->em = $em;
$this->logger = $logger;
$this->configLoader = $configLoader;
$this->fileService = $fileService;
$this->csrfTokenManager = $csrfTokenManager;
}
/**
* @Route("/", name="app_homepage")
*/
public function index(): Response
{
return $this->render('base.html.twig');
}
/**
* @Route("/api/init", name="init", methods={"GET"})
* @IsGranted("IS_AUTHENTICATED_FULLY")
* @return Response
*/
public function init(): Response
{
$response = new ResponseModel();
$response->csrfToken = $this->csrfTokenManager->getToken(CsrfToken::API_TOKEN)->getValue();
$response->config = $this->configLoader->getPublicConfigValues();
$positions = $this->em->getRepository(Position::class)->findAll();
$response->roles = [];
foreach ($positions as $position) {
$response->roles[$position->getId()] = PositionModel::createFromPosition($position);
}
$competencies = $this->em->getRepository(Comp::class)->findAll();
$response->competencies = [];
$compStructure = [];
foreach ($competencies as $competency) {
$frameworkId = $competency->getFramework()->getId();
$response->competencies[$competency->getId()] = CompetencyModel::createFromCompetency($competency);
if (empty($compStructure[$frameworkId])) {
$compStructure[$frameworkId] = [];
}
if ($parentComp = $competency->getParent()) {
$parentCompId = $parentComp->getId();
if (empty($compStructure[$frameworkId][$parentCompId])) {
$compStructure[$frameworkId][$parentCompId] = [];
}
$compStructure[$frameworkId][$parentCompId][] = $competency->getId();
} else {
$compStructure[$frameworkId][0][] = $competency->getId();
}
}
$response->competencyStructure = $compStructure;
$competencyFrameworks = $this->em->getRepository(CompFramework::class)->findAll();
$response->competencyFrameworks = [];
foreach ($competencyFrameworks as $competencyFramework) {
$response->competencyFrameworks[$competencyFramework->getId()] = CompetencyFrameworkModel::createFromFramework($competencyFramework);
}
$scales = $this->em->getRepository(CompScale::class)->findAll();
$response->scales = [];
foreach ($scales as $scale) {
$response->scales[$scale->getId()] = CompetencyScaleModel::createFromCompetencyScale($scale, true);
}
$csvDescriptions = $this->em->getRepository(CompScaleValueDescription::class)->findBy(['competency' => $competencies,]);
$response->customScaleValueDescriptions = [];
foreach ($csvDescriptions as $customDescription) {
$compId = $customDescription->getCompetency()->getId();
$scaleValueId = $customDescription->getScaleValue()->getId();
if (!array_key_exists($compId, $response->customScaleValueDescriptions)) {
$response->customScaleValueDescriptions[$compId] = [];
}
$response->customScaleValueDescriptions[$compId][$scaleValueId] = $customDescription->getDescription();
}
$skills = $this->em->getRepository(Skill::class)->findAll();
$response->skills = [];
foreach ($skills as $skill) {
$response->skills[$skill->getId()] = SkillModel::createFromSkill($skill);
}
$skillScaleRecords = $this->em->getRepository(SkillScale::class)->findBy(
['sortorder' => "0"],
['numericscore' => "DESC"]
);
$response->skillScales = [];
foreach ($skillScaleRecords as $skillScaleRecord) {
$response->skillScales[$skillScaleRecord->getId()] = SkillScaleModel::createFromSkillScale($skillScaleRecord);
}
$files = $this->fileService->getFilesByComponent(FileArea::ADDITIONAL_FILES);
$links = [];
foreach ($files as $file) {
$links[] = [
'name' => $file->getName(),
'id' => $file->getId()
];
}
$response->additionalFiles = $links;
return new JsonResponse($response);
}
/**
* @Route("/photo/{filename}", name="user_photo")
* @IsGranted("IS_AUTHENTICATED_FULLY")
*/
public function photo($filename): Response
{
$publicResourcesFolderPath = $this->getParameter('profile_photo_dir');
return new BinaryFileResponse("{$publicResourcesFolderPath}/{$filename}");
}
/**
* @Route("/file/{id}", name="get_file", methods={"GET"})
* @IsGranted("IS_AUTHENTICATED_FULLY")
* @ParamConverter("id", class="App:File")
* @param File $file
*
* @return Response
*/
public function downloadFile(File $file): Response
{
try {
if ($file->getComponent() !== FileArea::ADDITIONAL_FILES &&
$file->getComponent() !== FileArea::LMS_INTEGRATION) {
/** @todo: Check permission **/
// Only allow additional_files and course images
// to be returned until permissions handled
throw new NotFoundHttpException();
}
$filePath = $this->fileService->getFileFullPath($file);
return new BinaryFileResponse($filePath);
} catch (FileNotFoundException $exception) {
throw new NotFoundHttpException();
} catch (Throwable $e) {
$this->logger->error("Error downloading file", [
"e" => $e,
"file" => $file
]);
return ApiResponse::error();
}
}
/**
* @Route("/error/log", name="log_error", methods={"POST"})
* @IsGranted("IS_AUTHENTICATED_FULLY")
*/
public function logError(Request $request, AppValidator $validator): Response
{
try {
$data = $validator->validateLogError($request);
$this->logger->error("React Application Error: {$data->info}");
return ApiResponse::success(true);
} catch (ValidationException $e) {
return ApiResponse::error($e->getMessage());
} catch (Throwable $e) {
$this->logger->error("Error logging React Application error", [
"e" => $e,
]);
return ApiResponse::error();
}
}
/**
* @Route("/get_site_logo", name="get_site_logo")
* @IsGranted("IS_AUTHENTICATED_ANONYMOUSLY")
* @param Request $request
*
*/
public function getSiteLogo(Request $request, FileService $fileService)
{
$logoFilePath = $fileService->getSiteLogoFilePath();
if ($logoFilePath) {
return new BinaryFileResponse($logoFilePath);
}
return ApiResponse::error();
}
/**
* @return Response
* @Route("/admin/info", name="info", methods={"GET"})
* @IsGranted("IS_AUTHENTICATED_FULLY")
*/
public function info(): Response
{
echo phpinfo();
return new Response();
}
}