<?php
namespace App\Controller;
use App\Constant\BandTypeConstant;
use App\Constant\UtilsConstant;
use App\Entity\Project;
use App\Services\CurlHttpClient;
use DateInterval;
use DateTime;
use App\Services\SeoManager;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use FOS\RestBundle\Controller\Annotations as Rest;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use App\Constant\BandMemberRoleConstant;
use App\Constant\FileActionConstant;
use App\Constant\FilePlatformConstant;
use App\Constant\FileStatusConstant;
use App\Constant\FileTypeConstant;
use App\Entity\BandMember;
use App\Entity\FileBandMember;
use App\Entity\FileReport;
use App\Entity\Picture;
use App\Entity\Playable;
use App\Entity\User;
use App\Entity\File;
use App\Entity\Band;
use App\Entity\Folder;
use App\Form\AddDistantFileType;
use App\Form\CreateFolderType;
use App\Form\FileEditType;
use App\Form\FileReportType;
use App\Form\ShiftFileType;
use App\Form\RenameFolderType;
use App\Services\StorageManager;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Contracts\Translation\TranslatorInterface;
class FileController extends WhiplayController
{
const BAND_SHOW_ROUTE = 'whiplay_band_show';
const FILES_FOLDER_ROUTE = 'whiplay_files_folder';
const FILE_EDIT_ROUTE = 'whiplay_file_edit';
const FILE_NAME = 'file_name';
const BAND_NAME = 'band_name';
/**
* Show file detail view
*
* @param Request $request
* @param File $file
* @param SeoManager $seoManager
* @return Response
*/
public function show(Request $request, SeoManager $seoManager, File $file): Response
{
preg_match('/(youtube#!)(.+)/i', $file->getUrl() ?? '', $YtUrlMatches);
if (count($YtUrlMatches) === 3 && $YtUrlMatches[1] === 'youtube#!') {
$ogImageUrl = 'https://i.ytimg.com/vi/' . $YtUrlMatches[2] . '/hqdefault.jpg';
} else {
$package = new Package(new EmptyVersionStrategy());
$ogImageUrl = $this->getParameter(UtilsConstant::GLOBAL_URL) .
($file->getPicture() instanceof Picture ?
$file->getPicture()->getWebPath() :
$package->getUrl('images/player/player-default.jpg'));
}
$seoManager
->setTitleAndDescription(
'file.show.seo.title',
'file.show.seo.description',
[self::FILE_NAME => htmlspecialchars($file->getName()), self::BAND_NAME => htmlspecialchars($file->getBand()->getName())]
)
->setOgImage($ogImageUrl, 170, 170)
->setOgType('music.song')
->setProperty('og:audio:type', 'audio/vnd.facebook.bridge')
->setProperty('music:musician', $file->getBand()->getName());
if ($file->getStatus() == FileStatusConstant::STATUS_PRIVATE) {
/** @var User $user */
$user = $this->getUser();
if (!($user instanceof User)) {
return $this->ajaxForward($request, 'app_login');
}
$userInBand = $user->isInBand($file->getBand());
if (!$userInBand) {
return $this->ajaxForward(
$request,
self::BAND_SHOW_ROUTE,
array('id' => $file->getBand()->getId())
);
}
}
return $this->render('File/show.html.twig', array(
'file' => $file,
));
}
/**
* Show files and folders trash of specified band
*
* @param Request $request
* @param Band $band
* @param SeoManager $seoManager
* @return Response
*/
public function trash(Request $request, SeoManager $seoManager, Band $band): Response
{
/** @var User $user */
$user = $this->getUser();
$userInBand = $user instanceof User && $user->isInBand($band);
if (!$userInBand) {
return $this->ajaxForward($request, self::BAND_SHOW_ROUTE, array('id' => $band->getId()));
}
$seoManager
->setTitleAndDescription(
'file.trash.seo.title',
'file.trash.seo.title',
[self::BAND_NAME => htmlspecialchars($band->getName())]
)
->setNoIndex();
$deletedFolders = [];
$deletedFiles = [];
foreach ($band->getFolders() as $folder) {
if ($folder->getDeleted()) {
$deletedFolders[] = $folder;
}
/** @var File $file */
foreach ($folder->getFiles() as $file) {
if ($file->getDeleted()) {
$deletedFiles[] = $file;
}
}
}
return $this->render('File/trash.html.twig', array(
'band' => $band,
'deletedFolders' => $deletedFolders,
'deletedFiles' => $deletedFiles,
));
}
/**
* Show folder detail and files stored in it
*
* @param Request $request
* @param Folder $folder
* @param SeoManager $seoManager
* @param StorageManager $storageManager
* @param TranslatorInterface $translator
* @return Response
*/
public function folder(
Request $request,
Folder $folder,
SeoManager $seoManager,
StorageManager $storageManager,
TranslatorInterface $translator
): Response
{
$filesType = $folder->getFilesType();
$band = $folder->getOwner();
/** @var User $user */
$user = $this->getUser();
$userInBand = $user instanceof User && $user->isInBand($band);
if (!$filesType || !$userInBand) {
return $this->ajaxForward($request, self::BAND_SHOW_ROUTE, array('id' => $band->getId()));
}
if ($band->getType() === BandTypeConstant::USER_COLLABORATION) {
return $this->ajaxForward($request, 'whiplay_home');
}
$seoManager
->setTitleAndDescription(
'file.folder.seo.title',
'file.folder.seo.title',
[
'folder_name' => $translator->trans($folder->getName()),
self::BAND_NAME => htmlspecialchars($folder->getOwner()->getName())
]
)
->setNoIndex();
$returnResponse = null;
$newFolder = new Folder();
$formCreateFolder = $this->createForm(CreateFolderType::class, $newFolder);
$formCreateFolder->handleRequest($request);
if ($formCreateFolder->isSubmitted() && $formCreateFolder->isValid() && $user->isMemberButNotGuest($band)) {
$em = $this->getDoctrine()->getManager();
$newFolder->setCreator($user);
$newFolder->setCreatedAt(new DateTime());
$newFolder->setParent($folder);
$newFolder->setOwner($band);
$em->persist($newFolder);
$band->addFolder($newFolder);
$em->persist($band);
$em->flush();
$request->setMethod('GET');
$returnResponse = $this->ajaxForward($request, self::FILES_FOLDER_ROUTE, array('id' => $folder->getId()));
}
$formShiftFile = $this->createForm(ShiftFileType::class);
if ($this->handleShiftForm($formShiftFile, $request, $folder)) {
$returnResponse = $this->ajaxForward($request, self::FILES_FOLDER_ROUTE, array('id' => $folder->getId()));
}
$formRenameFolder = $this->createForm(RenameFolderType::class);
if ($user->isMemberButNotGuest($band) && $this->handleRenameForm($formRenameFolder, $request)) {
$returnResponse = $this->ajaxForward($request, self::FILES_FOLDER_ROUTE, array('id' => $folder->getId()));
}
//FORM VIDEO
$newDistantFile = new File();
$formAddDistantFile = $this->createForm(AddDistantFileType::class, $newDistantFile);
if ($this->handleVideoForm($formAddDistantFile, $request, $newDistantFile, $folder, $translator)) {
$returnResponse = $this->ajaxForward($request, self::FILE_EDIT_ROUTE, array('id' => $newDistantFile->getId()));
}
if ($returnResponse !== null) {
return $returnResponse;
}
$fullUsedSize = $storageManager->getStorageUsage($band);
$fullAvailableSize = $storageManager->getAvailableStorage($band);
return $this->render('File/folder.html.twig', array(
'form_create_folder' => $formCreateFolder->createView(),
'form_shift_file' => $formShiftFile->createView(),
'form_rename_folder' => $formRenameFolder->createView(),
'form_add_distant_file' => $formAddDistantFile->createView(),
'band' => $band,
'currentFolder' => $folder,
'baseFolders' => $band->getBaseFolders(),
'selectedFilesType' => $filesType,
'fullAvailableSize' => $fullAvailableSize,
'fullUsedSize' => $fullUsedSize,
));
}
/**
* Private: Handle video form from folder method
*
* @param $formAddDistantFile
* @param $request
* @param $newDistantFile
* @param $folder
* @param $translator
* @return bool
*/
private function handleVideoForm($formAddDistantFile, $request, $newDistantFile, $folder, $translator): bool
{
$formAddDistantFile->handleRequest($request);
if (!$formAddDistantFile->isSubmitted() || !$formAddDistantFile->isValid()) {
return false;
}
$em = $this->getDoctrine()->getManager();
if (preg_match("#^(?:https?://)?(?:www\.)?(?:youtu\.be/|youtube\.com/(?:embed/|v/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$#", $newDistantFile->getUrl(), $matches)) {
$id = count($matches) > 1 ? $matches[1] : '';
$newDistantFile->setUrl(FilePlatformConstant::YOUTUBE . '#!' . $id);
$newDistantFile->setType(FileTypeConstant::VIDEO);
$playable = new Playable();
$newDistantFile->setPlayable($playable);
$newDistantFile->setCreatedAt(new DateTime());
$newDistantFile->setFolder($folder);
$em->persist($newDistantFile);
$em->flush();
$this->toggleFileAction($newDistantFile, FileActionConstant::ADD, false);
$request->setMethod('GET');
return true;
} else {
$this->addFlash(
'video_upload_notice',
$translator->trans('file.folder.platform_not_found')
);
}
return false;
}
/**
* Private: Handle file shift form from folder method
*
* @param FormInterface $form
* @param Request $request
* @param Folder $folder
* @return bool
*/
private function handleShiftForm(FormInterface $form, Request $request, Folder $folder): bool
{
$form->handleRequest($request);
if (!$form->isSubmitted() || !$form->isValid()) {
return false;
}
$data = $form->getData();
$file = $this->getDoctrine()
->getRepository(File::class)
->find($data['file-id']);
if (!$file || $file->getBand() !== $folder->getOwner()) {
return false;
}
$file->setFolder($folder);
$em = $this->getDoctrine()->getManager();
$em->persist($file);
$em->flush();
$request->setMethod('GET');
return true;
}
/**
* Private: Handle folder rename form from folder method
*
* @param FormInterface $form
* @param Request $request
* @return bool
*/
private function handleRenameForm(FormInterface $form, Request $request): bool
{
$form->handleRequest($request);
if (!$form->isSubmitted() || !$form->isValid()) {
return false;
}
$formData = $form->getData();
$renamedFolder = $this->getDoctrine()
->getRepository(Folder::class)
->find($formData['folderId']);
if (!$renamedFolder || empty($formData['newName'])) {
return false;
}
$renamedFolder->setName($formData['newName']);
$em = $this->getDoctrine()->getManager();
$em->persist($renamedFolder);
$em->flush();
$request->setMethod('GET');
return true;
}
/**
* Show edition page and handle form if it's submitted
*
* @param Request $request
* @param SeoManager $seoManager
* @param TranslatorInterface $translator
* @param File $file
* @return Response
*/
public function edit(
Request $request,
SeoManager $seoManager,
TranslatorInterface $translator,
File $file
): Response
{
$band = $file->getBand();
if ($file->getCreatedBy() !== $this->getUser() && !$this->currentUserIsAdminOfBand($band, $translator)) {
return $this->ajaxForwardToReferer($request, self::BAND_SHOW_ROUTE, array('id' => $band->getId()));
}
if ($band->getType() === BandTypeConstant::USER_COLLABORATION) {
return $this->ajaxForward($request, 'whiplay_home');
}
$seoManager
->setTitleAndDescription(
'file.edit.seo.title',
'file.edit.seo.title',
[self::FILE_NAME => htmlspecialchars($file->getName()), self::BAND_NAME => htmlspecialchars($file->getBand()->getName())]
)
->setNoIndex();
$this->addMissingBandMembersOnFile($band, $file);
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(FileEditType::class, $file, ['band' => $band]);
if (!$file->isPlayable()) {
$form->remove('playable');
}
$oldStatus = $file->getStatus();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if ($request->get('filePictureDelete')) {
$em->remove($file->getPicture());
$em->flush();
$file->setPicture();
}
$newProjectName = $form->get('newProjectName')->getData();
if ($newProjectName) {
$newProject = new Project();
$newProject->setName($newProjectName);
$newProject->setBand($band);
$em->persist($newProject);
$band->addProject($newProject);
$file->setProject($newProject);
}
/** @var User $user */
$user = $this->getUser();
if ($file->getStatus() !== $oldStatus && $user->isInBand($file->getBand(), BandMemberRoleConstant::LEADER)) {
$this->toggleFileAction($file, FileActionConstant::PUBLISH, false);
}
$em->persist($file);
$em->flush();
$this->addFlash(
UtilsConstant::NOTICE,
$translator->trans('file.edit.saved')
);
$request->setMethod('GET');
return $this->ajaxForward($request, self::FILE_EDIT_ROUTE, array('id' => $file->getId()));
}
return $this->render('File/edit.html.twig', array(
'file' => $file,
'band' => $band,
'form' => $form->createView(),
));
}
/**
* Private: Check all band's members and add FileBandMember without instrument if it's missing
* NOT SURE: probably to avoid an issue with form
*
* @param Band $band
* @param File $file
*/
private function addMissingBandMembersOnFile(Band $band, File $file)
{
$em = $this->getDoctrine()->getManager();
$bandMembers = $band->getMembers(false)->toArray();
/** @var FileBandMember[] $fileBandMembers */
$fileBandMembers = $file->getBandMembers()->count() ? $file->getBandMembers()->toArray() : [];
/** @var BandMember $bandMember */
foreach ($bandMembers as $bandMember) {
$playOnMusic = false;
foreach ($fileBandMembers as $fileBandMember) {
if ($bandMember === $fileBandMember->getBandMember()) {
$playOnMusic = true;
break;
}
}
if ($playOnMusic === false) {
$newFileBandMember = new FileBandMember();
$newFileBandMember->setBandMember($bandMember);
$newFileBandMember->setFile($file);
$file->addBandMember($newFileBandMember);
$em->persist($newFileBandMember);
$em->persist($file);
}
}
$em->flush();
}
/**
* Show file report page and handle form if it's submitted
*
* @param Request $request
* @param SeoManager $seoManager
* @param TranslatorInterface $translator
* @param CurlHttpClient $httpClient
* @param MailerInterface $mailer
* @param File $file
* @return Response
* @throws TransportExceptionInterface
*/
public function report(
Request $request,
SeoManager $seoManager,
TranslatorInterface $translator,
CurlHttpClient $httpClient,
MailerInterface $mailer,
File $file
): Response
{
$em = $this->getDoctrine()->getManager();
$seoManager
->setTitleAndDescription(
'file.report.seo.title',
'file.report.seo.title',
[self::FILE_NAME => $file->getName(), self::BAND_NAME => $file->getBand()->getName()]
)
->setNoIndex();
$report = new FileReport();
$form = $this->createForm(FileReportType::class, $report);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$yesterdayDateTime = new DateTime();
$yesterdayDateTime->sub(new DateInterval('P1D'));
$user = $this->getUser();
$userId = $user instanceof User ? $user->getId() : 0;
$dayReports = $em->getRepository(FileReport::class)
->createQueryBuilder('file_report')
->andWhere('file_report.createdAt > :yesterday')
->andWhere('file_report.file = :fileId')
->andWhere('file_report.ip = :ip OR file_report.email = :email OR (file_report.user IS NOT NULL AND file_report.user = :userId)')
->setParameter('fileId', $file->getId())
->setParameter('ip', $request->getClientIp())
->setParameter('userId', $userId)
->setParameter('email', $report->getEmail())
->setParameter('yesterday', $yesterdayDateTime)
->getQuery()
->getResult();
if (count($dayReports)) {
$this->addFlash(
UtilsConstant::NOTICE,
$translator->trans('file.report.wait_delay')
);
} else {
$data = array(
'secret' => "0xA9e155ba26Ea705ec9A893B7553fd1Ac1Cd8b96A",
'response' => $request->request->get('h-captcha-response')
);
$response = $httpClient->post("https://hcaptcha.com/siteverify", $data, false);
$captchaData = json_decode($response);
if ($captchaData->success) {
$recipients = array($report->getEmail(), $this->getParameter('admin_email'));
if ($file->getCreatedBy()) {
$recipients[] = $file->getCreatedBy()->getEmail();
}
$report->setUser($this->getUser());
$report->setCreatedAt(new DateTime());
$report->setFile($file);
$report->setIp($request->getClientIp());
$em->persist($report);
$em->flush();
$reportMail = (new TemplatedEmail())
->from(new Address('reports@whiplay.fr', $translator->trans('file.report.from')))
->to(...$recipients)
->subject($translator->trans('file.report.subject'))
->htmlTemplate('Mail/savedReport.html.twig')
->context([
'report' => $report
]);
$mailer->send($reportMail);
$this->addFlash(
UtilsConstant::NOTICE,
$translator->trans('file.report.sent')
);
$request->setMethod('GET');
if ($this->getUser()) {
return $this->ajaxForward($request, 'whiplay_management_reports');
} else {
return $this->ajaxForward($request, 'whiplay_file_report', array('id' => $file->getId()));
}
}
}
}
return $this->render('File/report.html.twig', array(
'file' => $file,
'form' => $form->createView(),
));
}
/**
* Restore file by setting deleted column to false
*
* @param Request $request
* @param TranslatorInterface $translator
* @param File $file
* @return Response
*/
public function restore(Request $request, TranslatorInterface $translator, File $file): Response
{
$band = $file->getBand();
if (!$this->currentUserIsAdminOfBand($band, $translator)) {
return $this->ajaxForwardToReferer($request, self::BAND_SHOW_ROUTE, array('id' => $band->getId()));
}
$em = $this->getDoctrine()->getManager();
$file->setDeleted(false);
$em->persist($file);
$em->flush();
$this->addFlash(
UtilsConstant::NOTICE,
$translator->trans('file.restore.restored')
);
return $this->ajaxForward($request, self::FILES_FOLDER_ROUTE, array('id' => $file->getFolder()->getId()));
}
/**
* Restore folder by setting deleted column to false
*
* @param Request $request
* @param TranslatorInterface $translator
* @param Folder $folder
* @return Response
*/
public function folderRestore(Request $request, TranslatorInterface $translator, Folder $folder): Response
{
$band = $folder->getOwner();
if (!$this->currentUserIsAdminOfBand($band, $translator)) {
return $this->ajaxForwardToReferer($request, self::BAND_SHOW_ROUTE, array('id' => $band->getId()));
}
$em = $this->getDoctrine()->getManager();
$folder->setDeleted(false);
$em->persist($folder);
$em->flush();
$this->addFlash(
UtilsConstant::NOTICE,
$translator->trans('file.folder_restore.restored')
);
return $this->ajaxForward($request, self::FILES_FOLDER_ROUTE, array('id' => $folder->getParent()->getId()));
}
/**
* Delete file by setting deleted column to true or really delete it if reallyDelete GET param is set
*
* @param Request $request
* @param TranslatorInterface $translator
* @param File $file
* @return Response
*/
public function delete(Request $request, TranslatorInterface $translator, File $file): Response
{
$band = $file->getBand();
if (!$this->currentUserIsAdminOfBand($band, $translator)) {
return $this->ajaxForwardToReferer($request, self::BAND_SHOW_ROUTE, array('id' => $band->getId()));
}
$em = $this->getDoctrine()->getManager();
if ($request->query->get('reallyDelete')) {
$em->remove($file);
$em->flush();
$request->setMethod('GET');
return $this->ajaxForward($request, 'whiplay_files_trash', array('id' => $file->getBand()->getId()));
} else {
if ($file->getPublishAction()) {
$em->remove($file->getPublishAction());
}
$file->setStatus(FileStatusConstant::STATUS_PRIVATE);
$file->setDeleted(true);
$em->persist($file);
$em->flush();
$this->addFlash(
UtilsConstant::NOTICE,
$translator->trans('file.delete.deleted')
);
}
return $this->ajaxForwardToReferer($request, self::FILES_FOLDER_ROUTE, array('id' => $file->getFolder()->getId()));
}
/**
* Delete folder by setting deleted column to true or really delete it if reallyDelete GET param is set
*
* @param Request $request
* @param TranslatorInterface $translator
* @param Folder $folder
* @return Response
*/
public function folderDelete(
Request $request,
TranslatorInterface $translator,
Folder $folder
): Response
{
$band = $folder->getOwner();
if (!$this->currentUserIsAdminOfBand($band, $translator)) {
return $this->ajaxForwardToReferer($request, self::BAND_SHOW_ROUTE, array('id' => $band->getId()));
}
if ($request->query->get('reallyDelete')) {
$physically = true;
} else {
$physically = false;
}
$parentId = $folder->getParent()->getId();
$em = $this->getDoctrine()->getManager();
$this->deleteFolderAndChildren($folder, $em, $physically);
$em->flush();
if ($physically) {
$request->setMethod('GET');
return $this->ajaxForward($request, 'whiplay_files_trash', array('id' => $folder->getOwner()->getId()));
} else {
return $this->ajaxForward($request, self::FILES_FOLDER_ROUTE, array('id' => $parentId));
}
}
/**
* Private: Recursively delete folder and children
*
* @param Folder $folder
* @param EntityManagerInterface $em
* @param false $physically
*/
private function deleteFolderAndChildren(Folder $folder, EntityManagerInterface &$em, bool $physically = false)
{
if ($physically) {
foreach ($folder->getFiles() as $file) {
$em->remove($file);
}
}
foreach ($folder->getChildren() as $child) {
$this->deleteFolderAndChildren($child, $em, $physically);
}
if ($physically) {
$em->remove($folder);
} else {
$folder->setDeleted(true);
}
}
/**
* Toggle FAVORITE file action status for current user
*
* @param Request $request
* @param TranslatorInterface $translator
* @param File $file
* @return Response
*/
public function favourite(Request $request, TranslatorInterface $translator, File $file): Response
{
$band = $file->getBand();
if (!$this->currentUserIsAdminOfBand($band, $translator)) {
return $this->ajaxForwardToReferer($request, self::BAND_SHOW_ROUTE, array('id' => $band->getId()));
}
$this->toggleFileAction($file, FileActionConstant::FAVORITE, false);
return $this->ajaxForwardToReferer($request, 'whiplay_band_files', array('id' => $file->getBand()->getId()));
}
/**
* Toggle LIKE file action for current user
*
* @param Request $request
* @param File $file
* @return Response
*/
public function like(Request $request, File $file): Response
{
$this->toggleFileAction($file, FileActionConstant::LIKE);
return $this->ajaxForwardToReferer($request, 'whiplay_band_files', array('id' => $file->getBand()->getId()));
}
/**
* Toggle file status (to public or private depends on current status)
*
* @param Request $request
* @param TranslatorInterface $translator
* @param File $file
* @return Response
*/
public function publish(
Request $request,
TranslatorInterface $translator,
File $file
): Response
{
$band = $file->getBand();
if (!$this->currentUserIsAdminOfBand($band, $translator)) {
return $this->ajaxForwardToReferer($request, self::BAND_SHOW_ROUTE, array('id' => $band->getId()));
}
if ($file->getStatus() === FileStatusConstant::STATUS_PRIVATE) {
$reports = $this->getDoctrine()
->getRepository(FileReport::class)
->findBy(array('file' => $file));
if (count($reports)) {
$this->addFlash(
UtilsConstant::NOTICE,
$translator->trans('file.publish.reported')
);
return $this->ajaxForwardToReferer($request);
}
$file->setStatus(FileStatusConstant::STATUS_PUBLIC);
$this->addFlash(
UtilsConstant::NOTICE,
$translator->trans('file.publish.published')
);
} else {
$file->setStatus(FileStatusConstant::STATUS_PRIVATE);
$this->addFlash(
UtilsConstant::NOTICE,
$translator->trans('file.publish.private')
);
}
$this->toggleFileAction($file, FileActionConstant::PUBLISH, false);
$em = $this->getDoctrine()->getManager();
$em->persist($file);
$em->flush();
return $this->ajaxForwardToReferer($request);
}
}