<?php
namespace App\Controller\Api;
use App\Model\DataObject\News;
use Carbon\Carbon;
use Pimcore\Controller\FrontendController;
use Pimcore\Model\DataObject;
use Pimcore\Model\Element\Tag;
use Pimcore\Twig\Extension\Templating\PimcoreUrl;
use Pimcore\Tool;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Model\GeoLocation;
use Symfony\Component\String\Slugger\AsciiSlugger;
class NewsApiController extends FrontendController
{
/**
* @var PimcoreUrl
*/
protected PimcoreUrl $pimcoreUrl;
protected $condition;
protected $conditionParams;
protected $conditionText;
protected $ortNamen;
/**
* @param PimcoreUrl $pimcoreUrl
*/
public function __construct(PimcoreUrl $pimcoreUrl)
{
$this->pimcoreUrl = $pimcoreUrl;
$this->condition = [];
$this->conditionParams = [];
$this->conditionText = [];
$this->ortNamen = [];
}
private function conditionSQL(): string
{
return join(' AND ', array_map(fn($c) => '(' . $c . ')', $this->condition));
}
private function formatArticle(DataObject\News $article)
{
$link = $this->generateUrl('news_detail', ['path' => $article->getKey()]);
$getTags = \Pimcore\Model\Element\Tag::getTagsForElement("object", $article->getId());
$tags = [];
foreach ($getTags as $tag) {
$tags[] = $tag->getName();
}
$imageObject = null;
if ($article->getImage()) {
$imageObject = $article->getImage();
} elseif ($article->getImageGallery() && count($article->getImageGallery()->getItems())) {
$imageObject = $article->getImageGallery()->getItems()[0]->getImage();
}
$image = $imageObject?->getThumbnail('newsArticleImage')->getPath();
if (!$image) {
$image = "";
}
$imageCopyright = $imageObject?->getMetadata("copyright");
$tags = [];
$tagList = self::getTagsForElement('object', $article->getId());
foreach ($tagList as $tag) {
$tags[] = ['name' => $tag->getName(), 'path' => $tag->getNamePath()];
}
$ad = [];
if ($article->getAdvertisement()) {
$ad = [
'customer' => $article->getAdvertisementCustomer(),
'address' => $article->getAdvertisementAddress(),
'link' => $article->getAdvertisementLink()?->getPath(),
'linkTitle' => $article->getAdvertisementLink()?->getTitle(),
'logo' => $article->getAdvertisementLogo()?->getThumbnail('advertisementLogo')->getPath()
];
}
return [
"id" => $article->getId(),
"category" => $article->getCategory(),
"topic" => $article->getTopic(),
"locality" => $article->getLocations(),
"title" => $article->getTitle(),
"text" => $article->getText() ?? "",
"excerpt" => $article->getExerpt(),
"image" => $image,
"imageCopyright" => $imageCopyright,
"articleDate" => date("c", strtotime($article->getArticleDate())),
"published" => $article->getPublished(),
"highlight" => $article->getStaticPosition() != null,
"advertisement" => $article->getAdvertisement(),
"advertisementData" => $ad,
"createdAt" => date("c", $article->getCreationDate()),
"updatedAt" => date("c", $article->getModificationDate()),
"link" => $link,
"tags" => $tags
];
}
public function getAdvertisements(Request $request) {
$news = new DataObject\News\Listing();
$conditions = $this->setConditions($request, true);
$news->setCondition($this->conditionSQL(), $this->conditionParams);
}
/**
* @Route("/api/get-news", name="news_article_list")
* @param Request $request
* @return JsonResponse
*/
public function defaultAction(Request $request)
{
$news = new DataObject\News\Listing();
$landkreise = $request->get("lk", null);
$offset = $request->get("offset", 0);
$limit = $request->get("limit", 50);
$appSection = $request->get("app_section", null);
// boa did not always send app_section parameter.
// We can recognize old versions (only having home news) by their user agent.
if (!$appSection && str_contains(strtolower($request->headers->get('User-Agent')), 'dart/')) {
$appSection = "home";
}
if ($limit > 50) { $limit = 50; }
$this->setConditions($request, false, $appSection);
$news->setLimit($limit);
$news->setOffset($offset);
$news->setOrderKey(["staticPosition", "articleDate"]);
$news->setOrder("desc");
$news->setCondition($this->conditionSQL(), $this->conditionParams);
// Inject items with staticPosition != null ("highlights") at that position
// This only works reliably if $offset === 0
$newsItems = $news->load();
$staticPositionNews = array_filter($newsItems, fn($news) => $news->getStaticPosition() !== null);
$regularNews = array_filter($newsItems, fn($news) => $news->getStaticPosition() === null);
$sortedNews = [];
$idx = 0;
foreach ($regularNews as $item) {
foreach ($staticPositionNews as $s) {
if ($s->getStaticPosition() === $idx) {
$sortedNews[] = $s;
}
}
$sortedNews[] = $item;
$idx++;
}
$data = [];
foreach ($sortedNews as $article) {
$d = $this->formatArticle($article);
if (count($this->ortNamen)) {
$found = false;
// Landkreise ersetzen mit den alten Tags
$dataTopics = $article->getTopic();
if (!is_array($landkreise)) {
$landkreise = explode(',', $landkreise);
}
if ($dataTopics && count($dataTopics) && $landkreise && count($landkreise)) {
foreach ($landkreise as $landkreis) {
if (isset($landkreisTags[$landkreis])) {
foreach ($dataTopics as $topic) {
if (in_array($topic, $landkreisTags[$landkreis])) {
$found = true;
}
}
}
}
}
if (!$found) {
// Ortnamen direkt finden
if ($d['locality'] && count($d['locality'])) {
foreach ($d['locality'] as $locationName) {
if ($locationName && in_array(trim($locationName), $this->ortNamen)) {
$found = true;
}
}
}
}
if (!$found) {
$d = null;
}
}
if ($d) {
$data[] = $d;
}
}
$ad = null;
if ($appSection === "home" || $request->get('ad', false)) {
// get one advertisment
$ad = $this->getAdvertisment();
if ($data && count($data) > 3 && $ad) {
array_splice($data, 3, 0, [$ad]);
}
}
$response = $this->json([
"success" => true,
// "condition" => $this->condition,
// "conditionParams" => $this->conditionParams,
"orte" => $this->ortNamen,
// "landkreise" => $landkreise,
"data" => $data,
"ad" => $ad,
]);
$response->setExpires(new \DateTime('+15 seconds'));
return $response;
}
/**
* @Route("/api/get-news/{id}", name="news_article_by_id")
*/
public function articleAction(int $id, Request $request)
{
$article = DataObject\News::getById($id);
if (!$article) {
throw $this->createNotFoundException('The news does not exist');
}
$response = $this->json([
"success" => true,
"data" => $this->formatArticle($article)
]);
$response->setExpires(new \DateTime('+15 seconds'));
return $response;
}
/**
* @Route("/api/create-news-preflight", name="news_article_create_preflight")
* @param Request $request
* @return JsonResponse
*/
public function preflightAction(Request $request)
{
$r = [
'error' => false
];
$user = Tool\Admin::getCurrentUser();
if (!$user) {
$user = Tool\Session::getReadonly()->get("user");
}
if ($user) {
$r['user'] = $user->getName();
} else {
$r['error'] = true;
}
return $this->json($r);
}
/**
* @Route("/api/create-news", name="news_article_create")
* @param Request $request
* @return JsonResponse
*/
public function createAction(Request $request)
{
$r = [
'error' => false,
'newsId' => -1
];
$user = Tool\Admin::getCurrentUser();
if (!$user) {
$user = Tool\Session::getReadonly()->get("user");
}
if ($user) {
$data = $request->getContent();
if ($data) {
$news = new News();
$data = json_decode($data, true);
$data['title'] = trim($data['title']);
$text = nl2br(trim($data['text']));
if (strpos($text, '<br />') > 0) {
$text = explode('<br />', $text);
$newText = '';
foreach ($text as $line) {
$newText .= '<p>' . trim($line) . '</p>';
}
$text = $newText;
}
$data['text'] = $text;
$slugger = new AsciiSlugger();
$now = new Carbon();
$news->setTitle($data['title']);
$news->setText($data['text']);
$news->setArticleDate($now);
$news->setKey($now->format('Y-m-d') . '-' . $slugger->slug(strtolower($data['title'])));
$news->setLocations($data['locations']);
$news->setCategory('Nachrichten');
$news->setParentId(24);
$news->setPublished(true);
$news->save();
if ($news->getId()) {
$r['slug'] = $news->getKey();
$r['newsId'] = $news->getId();
}
}
}
return $this->json($r);
}
/**
* @param Request $request
* @param boolean $advertisement
* @return array $conditions
*/
private function setConditions(Request $request, $advertisement = false, ?string $appSection = null): array
{
$now = time();
$category = $request->get("category", null);
$search = $request->get("search", null);
$tags = $request->get("topics", null);
$landkreise = $request->get("lk", null);
$highlight = $request->get("highlight", false);
$ids = $request->get('id', null);
$not = $request->get("notId", '');
$limit = $request->get("limit", 50);
if ($limit > 50) { $limit = 50; }
$conditions = [
'condition' => [],
'conditionParams' => []
];
if ($advertisement) {
$conditions['condition'][] = "advertisement is true";
$conditions['condition'][] = "advertisementStart >= :now";
$conditions['conditionParams']['now'] = $now;
} else {
$this->condition[] = "advertisement is not true";
$this->condition[] = "articleDate <= :now";
$this->conditionParams["now"] = $now;
}
/*
name: 'Garmisch-Partenkirchen', search: 'DE21D',
name: 'Weilheim-Schongau', search: 'DE21N',
name: 'Starnberg', search: 'DE21L',
name: 'Bad Tölz-Wolfratshausen', search: 'DE216',
*/
$landkreisTags = [
'DE21N' => ['Landkreis WeilheimSchongau', 'WeilheimSchongau'],
'DE21D' => ['GarmischPartenkirchen', 'Landkreis GAP', 'Landkreis GarmischPartenkirchen'],
'DE21L' => ['Landkreis Starnberg'],
'DE216' => ['Bad TölzWolfratshausen', 'Landkreis Bad TölzWolfratshausen']
];
if ($landkreise) {
$landkreise = explode(',', $landkreise);
if (count($landkreise)) {
$landkreisCondition = [];
foreach ($landkreise as $landkreis) {
if (!$advertisement) {
if (isset($landkreisTags[$landkreis])) {
$this->conditionText = [];
foreach ($landkreisTags[$landkreis] as $i => $tag) {
$this->conditionText[] = 'topic LIKE :lkTag_' . $landkreis . '_' . $i;
$this->conditionParams['lkTag_' . $landkreis . '_' . $i] = '%' . $tag . '%';
}
$landkreisCondition[] = '(' . implode(' OR ', $this->conditionText) . ')';
}
}
}
$locationList = new GeoLocation\Listing();
$locationList->setCondition('kreisNutscode in (?)', [$landkreise]);
/** @var GeoLocation $locations */
foreach ($locationList as $i => $locations) {
if (!$advertisement) {
$this->ortNamen[] = $locations->gemeinde;
$landkreisCondition[] = 'locations LIKE :loc_' . $i;
$this->conditionParams['loc_' . $i] = '%' . $locations->gemeinde . ',%';
}
}
if (!$advertisement) {
$this->condition[] = '(' . implode(' OR ', $landkreisCondition) . ')';
}
}
}
if (!$advertisement) {
if ($search) {
$this->condition[] = "(topic LIKE :search OR title LIKE :search OR text LIKE :search OR locations LIKE :search)";
$this->conditionParams["search"] = "%$search%";
}
if ($category) {
$this->condition[] = "category LIKE :category";
$this->conditionParams["category"] = "%$category%";
}
if ($tags) {
$this->condition[] = "topic LIKE :topic";
$this->conditionParams["topic"] = "%$tags%";
}
if ($highlight) {
$this->condition[] = "highlight = :highlight";
$this->conditionParams["highlight"] = $highlight ? $highlight : 'null';
}
if ($not) {
$this->condition[] = "oo_id NOT IN (:notId)";
$this->conditionParams["notId"] = explode(',', $not);
}
if ($ids) {
$this->condition[] = "oo_id IN(:ids)";
$this->conditionParams["ids"] = explode(',', $ids);
}
if ($appSection) {
$sportTag = \Pimcore\Model\Element\Tag::getByPath("/Sport");
if ($sportTag) {
$sportCond = fn($include) => "oo_id " . ($include ? "" : "NOT") . " IN (
SELECT cid FROM tags_assignment INNER JOIN tags ON tags.id = tags_assignment.tagid
WHERE
ctype = 'object' AND
(id = :tagId OR idPath LIKE :tagIdPath)
)";
if ($appSection === "sport") {
// sport contains all articles with tags under /Sport, whether app-exclusive or not
$this->condition[] = $sportCond(true);
} else {
// home (fallback) is mixed:
// - all non-app-exclusive articles (including sports)
// - all non-sports articles
// - NO app-exclusive sports articles
$this->condition[] = "(" . $sportCond(false) . ") OR appExclusive IS NULL OR appExclusive = false";
}
$this->conditionParams["tagId"] = $sportTag->getId();
$this->conditionParams["tagIdPath"] = \Pimcore\Db\Helper::escapeLike($sportTag->getFullIdPath()) . "%";
}
} else {
// website has all articles except app-exclusive ones
$this->condition[] = "appExclusive IS NULL OR appExclusive = false";
}
}
return $conditions;
}
/**
* @Route("/api/feed", name="news_article_feed")
* @param Request $request
* @return Response
*/
public function rssAction(Request $request)
{
$news = new DataObject\News\Listing();
$landkreise = $request->get("lk", null);
$offset = $request->get("offset", 0);
$limit = $request->get("limit", 50);
if ($limit > 50) { $limit = 50; }
$this->setConditions($request, false);
$news->setLimit($limit);
$news->setOffset($offset);
$news->setOrderKey("articleDate");
$news->setOrder("desc");
$news->setCondition($this->conditionSQL(), $this->conditionParams);
$items = '';
foreach ($news as $key => $article) {
$link = $this->generateUrl('news_detail', ['path' => $article->getKey()]);
$getTags = \Pimcore\Model\Element\Tag::getTagsForElement("object", $article->getId());
$tags = [];
foreach ($getTags as $tag) {
$tags[] = $tag->getName();
}
$image = "";
if ($article->getImage()) {
$image = $article->getImage()->getThumbnail('newsArticleImage')->getPath();
} elseif ($article->getImageGallery() && count($article->getImageGallery()->getItems())) {
$image = $article->getImageGallery()->getItems()[0]->getImage()->getThumbnail('newsArticleImage')->getPath();
}
/*
<item>
<title>2023: Okowa may emerge Atiku’s running mate - Punch Newspapers</title>
<link>https://news.google.com/__i/rss/rd/articles/CBMiPmh0dHBzOi8vcHVuY2huZy5jb20vMjAyMy1va293YS1tYXktZW1lcmdlLWF0aWt1cy1ydW5uaW5nLW1hdGUv0gEA?oc=5</link>
<guid isPermaLink="false">1450966479</guid>
<pubDate>Tue, 14 Jun 2022 11:29:19 GMT</pubDate>
<description><ol><li><a href="https://news.google.com/__i/rss/rd/articles/CBMiPmh0dHBzOi8vcHVuY2huZy5jb20vMjAyMy1va293YS1tYXktZW1lcmdlLWF0aWt1cy1ydW5uaW5nLW1hdGUv0gEA?oc=5" target="_blank">2023: Okowa may emerge Atiku’s running mate</a> <font color="#6f6f6f">Punch Newspapers</font></li><li><a href="https://news.google.com/__i/rss/rd/articles/CBMidWh0dHBzOi8vZGFpbHlwb3N0Lm5nLzIwMjIvMDYvMTQvMjAyMy1oZS1oYXRlcy1tdXNsaW1zLWlzbGFtaWMtZ3JvdXAtd2FybnMtYXRpa3UtYWdhaW5zdC1waWNraW5nLXdpa2UtYXMtcnVubmluZy1tYXRlL9IBe2h0dHBzOi8vZGFpbHlwb3N0Lm5nLzIwMjIvMDYvMTQvMjAyMy1oZS1oYXRlcy1tdXNsaW1zLWlzbGFtaWMtZ3JvdXAtd2FybnMtYXRpa3UtYWdhaW5zdC1waWNraW5nLXdpa2UtYXMtcnVubmluZy1tYXRlLz9hbXA9MQ?oc=5" target="_blank">2023: He hates Muslims – Islamic group warns Atiku against picking Wike as running mate</a> <font color="#6f6f6f">Daily Post Nigeria</font></li><li><a href="https://news.google.com/__i/rss/rd/articles/CBMiWWh0dHBzOi8vd3d3LnZhbmd1YXJkbmdyLmNvbS8yMDIyLzA2L3ZwLWFwYy1leWVzLXNoZXR0aW1hLWxhbG9uZy13aWtlLW9rb3dhLWJhdHRsZS1pbi1wZHAv0gEA?oc=5" target="_blank">VP: APC eyes Shettima, Lalong; Wike, Okowa battle in PDP</a> <font color="#6f6f6f">Vanguard</font></li><li><a href="https://news.google.com/__i/rss/rd/articles/CBMiVmh0dHBzOi8vcHVuY2huZy5jb20vc291dGgtZWFzdC1wZHAtZGVtYW5kcy1hdGlrdXMtdnAtc2xvdC1wYXJ0eS1kaXNtaXNzZXMtb2Jpcy10aHJlYXQv0gEA?oc=5" target="_blank">South-East PDP demands Atiku’s VP slot, party dismisses Obi’s threat</a> <font color="#6f6f6f">Punch Newspapers</font></li><li><a href="https://news.google.com/__i/rss/rd/articles/CBMiT2h0dHBzOi8vdGhlbmF0aW9ub25saW5lbmcubmV0L3BkcC1yZXBzLXRpcC13aWtlLWFueWltLW5uYW1hbmktZm9yLXJ1bm5pbmctbWF0ZS_SAQA?oc=5" target="_blank">PDP Reps tip Wike, Anyim, Nnamani for running mate - The Nation Newspaper</a> <font color="#6f6f6f">The Nation Newspaper </font></li><li><strong><a href="https://news.google.com/stories/CAAqNggKIjBDQklTSGpvSmMzUnZjbmt0TXpZd1NoRUtEd2pQLS0tekJSSDQxLUc5WEo3N3VpZ0FQAQ?oc=5" target="_blank">View Full coverage on Google News</a></strong></li></ol></description>
<source url="https://punchng.com">Punch Newspapers</source>
</item>
*/
$previewText = strip_tags($article->getText());
if (strlen($previewText) > 50) {
$rest = substr($previewText, 50);
$nextSpace = strpos($rest, ' ');
$previewText = trim(substr($previewText, 0, 50 + $nextSpace)) . '...';
$radioText = '';
$radioTextRand = rand(1, 4);
switch ($radioTextRand) {
case 1:
$radioText = 'Radio Oberland';
break;
case 2:
$radioText = 'radio-oberland.de';
break;
case 3:
$radioText = 'radio oberland';
break;
case 4:
$radioText = 'radiooberland';
break;
}
$watermark = '';
$watermarkRand = rand(1, 4);
switch ($watermarkRand) {
case 1:
$watermark = '('.$radioText.')';
break;
case 2:
$watermark = '{'.$radioText.'}';
break;
case 3:
$watermark = '- (c) '.$radioText.' -';
break;
case 4:
$watermark = '--'.$radioText.'--';
}
}
$date = new \DateTime(date("D, d M Y G:i:s", strtotime($article->getArticleDate())));
$keywords = '';
if ($article->getTopic() && count($article->getTopic())) {
$keywords = '<media:keywords xmlns:media="http://search.yahoo.com/mrss/">' . str_replace('<', '', join(', ', $article->getTopic())) . '</media:keywords>';
}
$text = trim($article->getText());
$rand = rand(1, 2);
if ($rand === 2) {
$text = '<p>' . $watermark . '</p> ' . $text;
} else {
$text .= ' <p>' . $watermark . '</p>';
}
$item = '
<item>
<guid isPermaLink="false">' . $article->getKey() . '</guid>
<pubDate>' . date(DATE_RFC822, strtotime($article->getArticleDate())) . '</pubDate>
<title>' . $article->getTitle() . '</title>
<link>https://radio-oberland.de' . $link . '</link>
<description><![CDATA[' . $previewText . ']]></description>
' . $keywords . '
<content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[' . $text . ']]></content:encoded>
' . ($image ? '<media:content xmlns:media="http://search.yahoo.com/mrss/" url="https://radio-oberland.de' . $image . '" medium="image" />' : '') . '
</item>';
//
// $d = [
// "category" => $article->getCategory(),
// "topic" => $article->getTopic(),
// "locality" => $article->getLocations(),
// "title" => $article->getTitle(),
// "text" => $article->getText(),
// "excerpt" => $article->getExerpt(),
// "image" => $image,
// "articleDate" => date("c", strtotime($article->getArticleDate())),
// "published" => $article->getPublished(),
// "highlight" => $article->getHighlight(),
// "createdAt" => date("c", $article->getCreationDate()),
// "updatedAt" => date("c", $article->getModificationDate()),
// "link" => $link
// ];
if (count($this->ortNamen)) {
$found = false;
// Landkreise ersetzen mit den alten Tags
$dataTopics = $article->getTopic();
if ($dataTopics && count($dataTopics) && $landkreise && count($landkreise)) {
foreach ($landkreise as $landkreis) {
if (isset($landkreisTags[$landkreis])) {
foreach ($dataTopics as $topic) {
if (in_array($topic, $landkreisTags[$landkreis])) {
$found = true;
}
}
}
}
}
if (!$found) {
// Ortnamen direkt finden
$dataLocations = $article->getLocations();
if ($dataLocations && count($dataLocations)) {
foreach ($dataLocations as $locationName) {
if ($locationName && in_array($locationName, $this->ortNamen)) {
$found = true;
}
}
}
}
if (!$found) {
$item = null;
}
}
if ($item) {
$items .= $item;
}
}
$content = '<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Radio Oberland</title>
<link>https://radio-oberland.de</link>
<description>So klingt meine Heimat. Nachrichten aus Oberbayern, Garmisch-Partenkirchen, Weilheim, Bad Tölz, Wolfratshausen und Starnberg.</description>
<language>de-de</language>
<atom:link href="https://radio-oberland.de/api/feed" rel="self" type="application/rss+xml" />
' . $items . '
</channel>
</rss>';
$response = new Response(
'rss',
Response::HTTP_OK,
[
'Content-Type' => 'application/xml; charset=utf-8'
]
);
$response->setContent($content);
$response->setExpires(new \DateTime('+15 seconds'));
return $response;
}
/**
* returns all assigned tags for element
*
* @param string $cType
* @param int $cId
* @return Tag[]
*/
public static function getTagsForElement($cType, $cId)
{
$tag = new Tag();
return $tag->getDao()->getTagsForElement($cType, $cId);
}
private function getAdvertisment() {
$listing = new DataObject\News\Listing();
$listing->setOrderKey("RAND()", false);
$listing->setLimit(1);
$listing->setCondition('advertisement is true');
$advertisements = $listing->load();
if (count($advertisements)) {
return $this->formatArticle($advertisements[0]);
}
return null;
}
}