<?php
namespace App\Controller;
use App\Entity\Article;
use App\Entity\ArticleView;
use App\Entity\Comment;
use App\Form\AddCommentMobileType;
use App\Security\LoginFormAuthenticator;
use App\Service\ArticleBlockEntityService;
use App\Service\ArticleEntityService;
use App\Service\MediaEntityService;
use App\Service\PlatformService;
use App\Service\StripeService;
use App\Service\UserEntityService;
use Bluesquare\ValidatorBundle\Validator;
use Doctrine\ORM\EntityManagerInterface;
use Dompdf\Dompdf;
use Dompdf\Options;
use EWZ\Bundle\RecaptchaBundle\Form\Type\EWZRecaptchaV3Type;
use EWZ\Bundle\RecaptchaBundle\Validator\Constraints\IsTrueV3;
use Knp\Component\Pager\Pagination\PaginationInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Required;
class ArticleController extends AbstractController
{
/**
* @Route("/article/{slug}-{id}", name="article.view", requirements={"slug"="[a-zA-Z0-9-]+", "id"="\d+"})
*/
public function view(ArticleEntityService $articleService, Validator $validator, ArticleBlockEntityService $blockService, MediaEntityService $mediaEntityService, EntityManagerInterface $manager, $slug, $id, Request $request, PlatformService $platformService, UserEntityService $userEntityService, StripeService $stripeService)
{
$user = $this->getUser();
$article = $articleService->getFront($id);
if(!$article){
return $this->redirectToRoute('homepage');
}
if (!$article || !($article instanceof Article)) {
throw $this->createNotFoundException();
}
if ($slug !== $article->getSlug()) {
return $this->redirectToRoute('article.view', ['slug' => $article->getSlug(), 'id' => $article->getId()]);
}
$cover = $article->getCover() ? $mediaEntityService->format($article->getCover()) : null;
// TODO: uncomment si besoin de n'autoriser l'accès qu'aux articles en ligne via l'url directe
// if ($article->getPublicationStatus() !== Article::PUBLICATION_STATUS_PUBLISHED)
// throw $this->createNotFoundException();
$now = new \DateTime();
// if (!is_null($article->getPublicationEnd()) && $article->getPublicationEnd() < $now)
// throw $this->createNotFoundException();
// if (!is_null($article->getPublicationStart()) && $article->getPublicationStart() > $now)
// throw $this->createNotFoundException();
// Breadcrumb
$breadcrumb = [];
// $breadcrumb[getenv('APP_NAME')] = $this->generateUrl('home');
$categories = [];
$category = null;
if (\count($article->getArticleCategories()) > 0) {
$category = $article->getArticleCategories()[0]->getCategory();
}
while (null !== $category) {
$categories[] = $category;
$category = $category->getParent();
}
$categories = array_reverse($categories);
foreach ($categories as $category) {
$breadcrumb[$category->getName()] = $this->generateUrl('category.view', ['slug' => $category->getSlug()]);
}
$breadcrumb[$article->getTitle()] = $this->generateUrl('article.view', ['slug' => $article->getSlug(), 'id' => $article->getId()]);
// Tags
$tags = [];
foreach ($article->getArticleTags() as $articleTag) {
$tags[] = $articleTag->getTag();
}
// Blocks
$blocks = $blockService->index($article);
usort($blocks, function ($a, $b) {
return $a['position'] - $b['position'];
});
$blocks = array_map(function ($block) {
$block['data'] = @json_decode($block['data'], true);
if (!\is_array($block['data'])) {
$block['data'] = [];
}
return $block;
}, $blocks);
// Comments
$comments = $manager->getRepository(Comment::class)->findBy(['article' => $article, 'parent' => null, 'deleted_at' => null], ['created_at' => 'ASC']);
$articleRepository = $manager->getRepository(Article::class);
$live = $articleService->formatAll($articleRepository->getLiveArticles(null, 4)); // TODO: channel
$ip = $request->getClientIp();
$user_agents = $request->server->get('HTTP_USER_AGENT');
$identifier = md5($ip.$user_agents);
$articleViewRepository = $this->getDoctrine()->getRepository(ArticleView::class);
$articleViewVisited = $articleViewRepository->findIfVisitedToday($identifier, $article);
if (!$articleViewVisited) { // The user hasn't already visited the article today
$now = new \DateTime();
$articleView = new ArticleView();
$articleView->setVisitedAt($now);
$articleView->setUser($user);
$articleView->setArticle($article);
$articleView->setIdentifier($identifier);
// TODO: uncomment si le log des visites web sont souhaitées
// $em = $this->getDoctrine()->getManager();
// $em->persist($articleView);
// $em->flush();
}
// Check that the user has subscribed in zenabo or stripe product --------------
$user_is_premium = false;
$article_products = $article->getArticleProducts();
foreach ($article_products as $articleProduct) {
if (
$user &&
($stripeService->userSubscribedToProduct($user, $articleProduct->getProductStripeId()) ||
$stripeService->userBoughtProductOneShot($user, $articleProduct->getProductStripeId()))
) {
$user_is_premium = true;
break;
}
}
// LOANN => I commented this because the migration Zenabo -> A&C has been done
// if ($user && !$user_is_premium && ($userEntityService->isZenaboPremium($user))) {
// $user_is_premium = true;
// }
// -----------------------------------------------------------------------------
$comment = new Comment();
$user = $this->getUser();
$builder = $this->createFormBuilder($comment)
->add('parent', HiddenType::class, [
'mapped' => false,
])
->add('content', TextType::class, [
'constraints' => [
new NotBlank(),
],
])
->add('recaptcha', EWZRecaptchaV3Type::class, [
'action_name' => 'add_comment_'.$article->getId(),
'mapped' => false,
'constraints' => [
new IsTrueV3(),
],
]);
if (!$user) {
$builder
->add('author_name', TextType::class, [
'constraints' => [
new Required(),
new NotBlank(),
],
])
->add('author_email', EmailType::class, [
'constraints' => [
new Required(),
new Email(),
],
]);
}
$addCommentForm = $builder->getForm();
$addCommentForm->handleRequest($request);
if ($addCommentForm->isSubmitted() && $addCommentForm->isValid()) {
$comment->setArticle($article);
if ($user) {
$comment->setUser($user);
}
$parentId = $addCommentForm->get('parent')->getData();
if ($parentId) {
$parent = $manager->getRepository(Comment::class)->find($parentId);
if ($parent && $parent->getArticle() === $article && null === $parent->getParent()) {
$comment->setParent($parent);
}
}
$manager->persist($comment);
$manager->flush();
$url = $this->generateUrl('article.view', ['slug' => $article->getSlug(), 'id' => $article->getId()]);
return $this->redirect($url.'#comment-'.$comment->getId());
}
$commentM = new Comment();
$addCommentFormMobile = $this->createForm(AddCommentMobileType::class, $commentM);
$addCommentFormMobile->handleRequest($request);
if ($addCommentFormMobile->isSubmitted() && $addCommentFormMobile->isValid()) {
$commentM->setArticle($article);
if ($user) {
$commentM->setUser($user);
$commentM->setArticle($article);
$commentM->setContent($addCommentFormMobile->get('content')->getData());
$manager->persist($commentM);
$manager->flush();
}
$url = $this->generateUrl('article.view', ['slug' => $article->getSlug(), 'id' => $article->getId()]);
return $this->redirect($url.'#comment-'.$comment->getId());
}
$author = $articleService->getAuthorEntity($article);
$authorAvatar = $author && $author->getAvatar() ? $mediaEntityService->format($author->getAvatar()) : null;
$user_subscriptions = $user ? $stripeService->getUserSubscriptions($this->getUser()) : [];
$subscription_status = false;
$status=['active','trialing'];
if (!empty($user_subscriptions)) {
foreach ($user_subscriptions as $user_subscription) {
if (in_array(strtolower($user_subscription->getStatus()), $status)) {
$subscription_status = true;
}
}
}
return $this->render('article/article.html.twig', [
'author' => $author,
'article' => $article,
'article_formatted' => $articleService->format($article, true),
'breadcrumb' => $breadcrumb,
'tags' => $tags,
'blocks' => $blocks,
'comments' => $comments,
'cover' => $cover,
'live' => $live,
'add_comment_form' => $addCommentForm->createView(),
'add_comment_form_mobile' => $addCommentFormMobile->createView(),
'validator' => $validator,
'user_is_digital_premium' => $user_is_premium,
'e_shop_url_product' => $article->getEshopUrlProduct() ? $article->getEshopUrlProduct() : $stripeService->generateEshopUrl($article),
'e_shop_url_product_sub' => $article->getEshopUrlProductSub() ? $article->getEshopUrlProductSub() : $stripeService->generateEshopUrl($article, 'recurring'),
'userSubscriptionsStatus' => $subscription_status,
'authenticate_user' => $user,
'authorAvatar' => $authorAvatar,
]);
}
/**
* @Route("/pave-pub", name="pave.pub")
* @Cache(smaxage="3600", public=true)
*/
public function renderPubPave(StripeService $stripeService)
{
$user = $this->getUser();
$user_subscriptions = $user ? $stripeService->getUserSubscriptions($this->getUser()) : [];
$subscription_status = false;
if (!empty($user_subscriptions)) {
foreach ($user_subscriptions as $user_subscription) {
if ('active' === $user_subscription->getStatus()) {
$subscription_status = true;
}
}
}
$html = $this->render('article/pub.html.twig', [
'user' => $user,
'subscription_status' => $subscription_status,
])->getContent();
return $this->json(['html' => $html], 200);
}
public static function convert_from_latin1_to_utf8_recursively($dat)
{
if (\is_string($dat)) {
return utf8_encode($dat);
} elseif (\is_array($dat)) {
$ret = [];
foreach ($dat as $i => $d) {
$ret[$i] = self::convert_from_latin1_to_utf8_recursively($d);
}
return $ret;
} elseif (\is_object($dat)) {
foreach ($dat as $i => $d) {
$dat->$i = self::convert_from_latin1_to_utf8_recursively($d);
}
return $dat;
}
return $dat;
}
/**
* @Route("/article/{slug}-{id}/comment", name="article.comment", requirements={"slug"="[a-zA-Z0-9-]+", "id"="\d+"})
*/
public function comment(Request $request, ArticleEntityService $articleService, Validator $validator, EntityManagerInterface $manager, $slug, $id)
{
$article = $articleService->get($id);
if (!$article || !($article instanceof Article)) {
throw $this->createNotFoundException();
}
if ($validator->post()) {
if (null === $this->getUser()) {
$validator->required('author_name', 'author_email');
}
$validator->required('content', '_captcha', '_captcha_verification');
if ($validator->check()) {
$comment = new Comment();
$validator->entity($comment);
$validator->inject('content');
$comment->setArticle($article);
if (null === $this->getUser()) {
$validator->inject('author_name', 'author_email');
} else {
$comment->setUser($this->getUser());
}
if ($validator->has('parent')) {
$parent = $manager->getRepository(Comment::class)->find($validator->get('parent'));
if ($parent && $parent->getArticle() === $article && null === $parent->getParent()) {
$comment->setParent($parent);
}
}
$manager->persist($comment);
$manager->flush();
} else {
$validator->keep();
}
}
return $this->redirectToRoute('article.view', ['slug' => $article->getSlug(), 'id' => $article->getId()]);
}
/**
* @Route("/search", name="articles.search.view")
*/
public function searchView(Request $request)
{
return $this->render('search/search.html.twig');
}
/**
* @Route("/articles/search", name="articles.search")
*/
public function search(Request $request, ArticleEntityService $articleEntityService, EntityManagerInterface $manager)
{
$articleRepository = $manager->getRepository(Article::class);
$search = $request->get('search');
if (!$search) {
return $this->json([
'error' => 'value.empty',
'message' => 'Please write a value in search input',
], 404);
}
if ($request->get('limit')) {
$limit = $request->get('limit');
} else {
$limit = 10;
}
$articlesObjects = $articleRepository->findArticlesFromSearch($search, 'DESC', $limit);
$html = '';
$articles = $articleEntityService->formatAll($articlesObjects);
if (!empty($articles)) {
$html = $this->renderView('search/_partials/articles.html.twig', [
'articles' => $articles,
]);
}
$count = \count($articles);
return $this->json(['html' => $html, 'count' => $count], 200);
}
/**
* @Route("/{slug}-{id}", name="article.legacy", requirements={"slug"="[a-zA-Z0-9-]+", "id"="\d+"})
*/
public function legacy(ArticleEntityService $articleService, ArticleBlockEntityService $blockService, MediaEntityService $mediaEntityService, EntityManagerInterface $manager, $slug, $id, Request $request)
{
$article = $manager->getRepository(Article::class)->findOneBy(['legacy_id' => $id]);
if (!$article) {
throw $this->createNotFoundException();
}
return $this->redirectToRoute('article.view', ['slug' => $article->getSlug(), 'id' => $article->getId()]);
}
/**
* @Route("/article/download_pdf/{id}", name="article.download_pdf", methods={"GET"})
*/
public function downloadPdf($id, ArticleEntityService $entityService, ArticleBlockEntityService $blockEntityService)
{
$entity = $entityService->get($id);
if (!$entity || !($entity instanceof Article)) {
throw $this->createNotFoundException();
}
// Blocks
$blocks = $blockEntityService->index($entity);
usort($blocks, function ($a, $b) {
return $a['position'] - $b['position'];
});
$blocks = array_map(function ($block) {
$block['data'] = @json_decode($block['data'], true);
if (!\is_array($block['data'])) {
$block['data'] = [];
}
return $block;
}, $blocks);
$options = new Options();
$options->set('isRemoteEnabled', true);
$options->set('isHtml5ParserEnabled', true);
setlocale(\LC_NUMERIC, 'C');
$dompdf = new Dompdf($options);
$html = $this->renderView('article/article_pdf.html.twig', [
'article_formatted' => $entityService->format($entity, true),
'article' => $entity,
'blocks' => $blocks,
]);
iconv('latin5', 'utf-8', $html);
$dompdf->loadHtml($html);
$dompdf->render();
$dompdf->stream();
return new Response("");
}
private function getDeviceType(Request $request)
{
return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i",
$request->server->get('HTTP_USER_AGENT')) ? 'mobile' : 'desktop';
}
/**
* @Route("/articleLive/{page}", name="articleLive", methods={"GET"}, defaults={"page"=1})
*/
public function articleLive(Request $request, $page, ArticleEntityService $articleService, EntityManagerInterface $manager)
{
$deviceType = $this->getDeviceType($request);
$page = (int) $page;
$page = max(1, $page);
$paginationStep = $deviceType === 'desktop' ? 10 : 4;
$maxResults = $paginationStep * $page;
/** @var PaginationInterface $pagination */
$pagination = $manager->getRepository(Article::class)->getAllLiveArticles(1, $maxResults);
$formattedArticles = $articleService->formatAll($pagination->getItems());
$isLastPage = $pagination->getTotalItemCount() <= $maxResults;
if ($request->isXmlHttpRequest()) {
return $this->render('themes/theme-5/article/articleLiveDetails.html.twig', [
'live' => $formattedArticles,
'isLastPage' => $isLastPage,
'page' => $page,
'deviceType' => $deviceType,
]);
}
return $this->render('article/articleLive.html.twig', ['live' => $formattedArticles, 'isLastPage' => $isLastPage, 'page' => $page, 'deviceType' => $deviceType]);
}
/**
* @Route("/recent-comments", name="recent-comments", methods={"GET"})
*/
public function recentComments(Request $request, EntityManagerInterface $manager)
{
$comments = $manager->getRepository(Comment::class)->getLastComments();
$comment = new Comment();
$comment->setContent('ok');
return $this->render('_partials/comments/recent-comments.html.twig', [
'comments' => $comments,
]);
}
/**
* @Route("/all-comments", name="all-comments", methods={"GET"})
*/
public function AllComments(Request $request, EntityManagerInterface $manager)
{
$comments = $manager->getRepository(Comment::class)->getLastComments();
return $this->render('_partials/comments/commentaires.html.twig', [
'comments' => $comments,
]);
}
/**
* @Route("/add-comments/{commentObj}", name="add-comments", methods={"POST"})
*/
public function add(Request $request, EntityManagerInterface $manager, Comment $commentObj)
{
$user = $this->getUser();
$comment = new Comment();
$content = $request->get('comment');
$article = $commentObj->getArticle();
$comment->setArticle($article);
if ($user) {
$comment->setUser($user);
$article = $commentObj->getArticle();
$comment->setArticle($article);
$comment->setContent($content);
$parentId = $commentObj->getId();
if ($parentId) {
$parent = $manager->getRepository(Comment::class)->find($parentId);
if ($parent && $parent->getArticle() === $article && null === $parent->getParent()) {
$comment->setParent($parent);
}
}
$manager->persist($comment);
$manager->flush();
$url = $this->generateUrl('article.view', ['slug' => $article->getSlug(), 'id' => $article->getId()]);
return $this->redirect($url);
}
// $url = $this->generateUrl('homepage');
$url = $this->generateUrl('article.view', ['slug' => $article->getSlug(), 'id' => $article->getId()]);
return $this->redirect($url);
}
/**
* @Route("/recherche", name="articles.recherche.view")
*/
public function recherchView(Request $request)
{
return $this->render('search/search.html.twig');
}
/**
* @Route("/articles/recherche", name="articles.recherche")
*/
public function recherche(Request $request, ArticleEntityService $articleEntityService)
{
$articleRepository = $this->getDoctrine()->getRepository(Article::class);
$search = $request->get('search');
$fin = $request->get('fin');
$debut = $request->get('debut');
$order = $request->get('order');
$isModal = $request->get('isModal');
if ($request->get('limit')) {
$limit = $request->get('limit');
} else {
$limit = 10;
}
$click = 0;
if (isset($isModal)) {
if (!empty($fin) && !empty($debut)) {
$click = $click + 2;
} else {
$click = $click + 1;
}
}
if (empty($fin) && empty($debut)) {
$count = count($articleRepository->findArticlesFromSearch($search, $order, null) );
$articlesObjects = $articleRepository->findArticlesFromSearch($search, $order, $limit);
$articles = $articleEntityService->formatAll($articlesObjects);
} elseif (!empty($fin) && !empty($debut) && empty($search)) {
$count = count($articleRepository->findArticlesFromDate($debut, $fin, $order, null));
$articles = $articleRepository->findArticlesFromDate($debut, $fin, $order, $limit);
$articles = $articleEntityService->formatAll($articles);
} else {
$count = count($articleRepository->findArticlesFromAll($debut, $fin, $search, $order, null));
$articles = $articleRepository->findArticlesFromAll($debut, $fin, $search, $order, $limit);
$articles = $articleEntityService->formatAll($articles);
}
$html = $this->renderView('search/_partials/articles.html.twig', [
'articles' => $articles,
]);
return $this->json(['html' => $html, 'count' => $count, 'click' => $click], 200);
}
/**
* @Route("/articles/recherche/koisque", name="articles.recherche.koisque")
*/
public function recherchekoisque(Request $request, StripeService $stripeService)
{
$search = $request->get('search');
$fin = $request->get('fin');
$debut = $request->get('debut');
$order = $request->get('order');
if ($request->get('limit')) {
$limit = $request->get('limit');
} else {
$limit = 10;
}
if (empty($fin) && empty($debut)) {
$products = $stripeService->search($search, $order, $limit);
} elseif (!empty($fin) && !empty($debut) && empty($search)) {
$products = $stripeService->filterByDate($debut, $fin, 'ASC', $limit);
} else {
$products = $stripeService->filterByAll($debut, $fin, $search, $order, $limit);
}
$html = $this->renderView('search/_partials/articles.html.twig', [
'products' => $products,
'koisque' => 'koisque',
]);
$count = \count($products);
return $this->json(['html' => $html, $count], 200);
}
}