<?php
namespace App\Controller\API;
use App\Common\API\V4Common;
use App\Common\VideoStream;
use App\Entity\ArtObjectMedia;
use App\Entity\SurveyQuestion;
use App\Entity\SurveyQuestionResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class V4Controller
*
* @package App\Controller
*/
#[Route(path: '/api/v4', name: 'v4.')]
class V4Controller extends CommonController
{
/**
* Get list of update dates
*
* @return string
* @throws Exception
*/
#[Route(path: '/', name: 'index')]
public function index(): JsonResponse
{
//Return JSON
return new JsonResponse(['status'=> 'OK']);
}
/**
* Get list of update dates
*
* @return string
* @throws Exception
*/
#[Route(path: '/check', name: 'check')]
public function checkForUpdate(): JsonResponse
{
//Return JSON
return new JsonResponse(V4Common::getLastEntityTouchDateTime($this->em));
}
/**
* Get application data
*
* @param $timestamp
* oldest data set to retrieve; null fetches all
*
* @return Response
*/
#[Route(path: '/data/{timestamp}', name: 'data', defaults: ['$timestamp' => 'null'])]
public function fetchData($timestamp = null): Response
{
$projectDir = $this->container->get('parameter_bag')->get('kernel.project_dir');
$cache = new V4Common($projectDir);
$filename = $cache->getFileName();
return new Response(file_get_contents($filename));
}
/**
* Get application data
*
* @return string
*/
#[Route(path: '/webtour', name: 'webtour')]
public function fetchWebTourAction()
{
$data = [];
/** @var QueryBuilder $qb */
$qb =
$this->em->createQueryBuilder()
->select('WebTour')
->from('App:WebTour', 'WebTour');
$results =
$qb->getQuery()
->getResult();
/** @var WebTour $item */
foreach ($results as $item) {
$webTour = array(
"id" => $item->getId(),
"tourID" => $item->getTourID(),
"title" => $item->getTitle(),
"description" => $item->getDescription(),
"displayDescription" => $item->getDisplayDescription(),
"priority" => $item->getPriority(),
"objects" => [],
"media" => [],
);
/** @var WebTourLink $link */
foreach ($item->getArtObjects() as $link) {
/** @var ArtObject $artObject */
$artObject = $link->getArtObject();
$object = array(
"id" => $artObject->getObjectID(),
"priority" => $link->getPriority(),
"defaultDescription" => $artObject->getDescription(),
"collectionDescription" => null,
"collectionDescriptionFormatted" => null,
);
/** @var ArtObjectDescription $desc */
foreach ($artObject->getDescriptions() as $desc) {
if ($desc->getCollectionId() == $item->getTourID()) {
$object['collectionDescription'] = $desc->getDescription();
$object['collectionDescriptionFormatted'] = $desc->getDisplayDescription();
break;
}
}
$webTour['objects'][] = $object;
}
/** @var WebTourMedia $media */
foreach ($item->getMedia() as $media) {
$media = [
"id" => $media->getId(),
"objectID" => $media->getObjectID(),
"linkID" => $media->getLinkID(),
"name" => $media->getName(),
"ext" => $media->getExt(),
"typeDesc" => $media->getTypeDesc(),
"description" => $media->getDescription(),
];
$webTour['media'][] = $media;
}
$data[] = $webTour;
}
$this->em->clear();
unset($results);
return new JsonResponse($data);
}
/**
* Get multimedia asset.
*
* @param $objectID
* @param $linkID
* @param $name
* @param $ext
* @return Response
*/
#[Route(path: '/asset/{objectID}/{linkID}/{name}.{ext}', name: 'media', requirements: [
'objectID' => '\d+',
'linkID' => '.+',
'name' => '.+',
'ext' => '.+',
])]
public function objectAsset($objectID, $linkID, $name, $ext)
{
/** @var ArtObjectMedia $media */
$media = $this->getMediaAsset($objectID, $linkID, $name, $ext);
if (!$media) {
// Asset not found. Kill process.
throw new NotFoundHttpException('Object not found.');
}
$remoteAsset = $this->getRemoteAssetPath($media);
if (!file_exists($remoteAsset) || filesize($remoteAsset) <= 0) {
// Asset not found. Kill process.
throw new NotFoundHttpException('Asset not found.');
}
// Get last modified time.
$lastModified = filemtime($remoteAsset);
// Getting headers sent by the client.
$headers = apache_request_headers();
// Checking if the client is validating its cache and if it is current.
if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == $lastModified)) {
// Client's cache IS current, so we just respond '304 Not Modified'.
header("Last-Modified: ".gmdate('D, d M Y H:i:s', $lastModified)." GMT", true, 304);
exit;
}
$response = new BinaryFileResponse($remoteAsset);
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT);
$response->headers->add(array("Last-Modified" => gmdate('D, d M Y H:i:s', $lastModified)." GMT"));
return $response;
}
/**
* Stream MP4 asset.
*
* @param $objectID
* @param $linkID
* @param $name
* @param $ext
*
* @param Request $request
*
* @return Response
*/
#[Route(path: '/asset/stream/{objectID}/{linkID}/{name}.{ext}', name: 'stream', requirements: [
'objectID' => '\d+',
'linkID' => '.+',
'name' => '.+',
'ext' => '.+',
])]
public function streamVideo($objectID, $linkID, $name, $ext, Request $request): Response|StreamedResponse
{
/** @var \App\Entity\ArtObjectMedia $media */
$media = $this->getMediaAsset($objectID, $linkID, $name, $ext);
if (!$media) {
// Asset not found. Kill process.
throw new NotFoundHttpException('Object not found.');
}
$remoteAsset = $this->getRemoteAssetPath($media);
if (!file_exists($remoteAsset) || filesize($remoteAsset) <= 0 || $media->getExt() != "mp4") {
// Asset not found. Kill process.
throw new NotFoundHttpException('Asset not found.');
}
// Run stream protocol.
return (new VideoStream($remoteAsset))->streamAction($request);
}
/**
* Save POST survey data. Example POST JSON: {"response":[{"qid":1,"data":"true"}]}
*
* @param Request $request
*
* @return string
*/
#[Route(path: '/survey', name: 'survey_data')]
public function submitSurvey(Request $request): Response
{
// Configure batch saving
$batchSize = 20;
$i = 0;
// Retrieve submission from Request
$submission = json_decode($request->getContent(), true);
if (isset($submission['response'])) {
$submission = $submission['response'];
} else {
// bad data, return failure
return new JsonResponse(array("failure" => true));
}
// Loop through data items in submission
foreach ($submission as $data) {
if (!isset($data['qid']) || !isset($data['data'])) {
// invalid data
continue;
}
$id = $data['qid'];
/** @var SurveyQuestion $question */
$question = $this->em->getRepository('App:SurveyQuestion')->findOneBy(
array("id" => $id, "enabled" => true)
);
if (!$question) {
// Survey question not found
continue;
}
$response = new SurveyQuestionResponse();
if ($question->getType() == "boolean") {
// boolean requires additional check
if ($data['data'] === "true" || $data['data'] === "false") {
$response->setResponse($data['data']);
} else {
// weed out trash responses
continue;
}
} else {
$response->setResponse($data['data']);
}
$response->setQuestion($question);
// Save response to database
$this->em->persist($response);
// Increment batch index
$i++;
// Save batch, clear in memory objects
if (($i % $batchSize) === 0) {
$this->em->flush();
$this->em->clear();
}
}
// Save unsaved objects
$this->em->flush();
$this->em->clear();
return new JsonResponse(array("success" => true));
}
}