src/Twig/AppRuntime.php line 166
<?php
namespace App\Twig;
use App\Entity\News;
use App\Repository\NewsRepository;
use App\Repository\UseCaseRepository;
use DateTime;
use Sulu\Bundle\MediaBundle\Api\Media;
use Sulu\Component\SmartContent\ArrayAccessItem;
use Sulu\Component\Webspace\Analyzer\Attributes\RequestAttributes;
use Sulu\Component\Webspace\Manager\WebspaceManagerInterface;
use Sulu\Component\Webspace\Webspace;
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\RequestStack;
use Twig\Extension\RuntimeExtensionInterface;
class AppRuntime implements RuntimeExtensionInterface
{
// Defines the max words count for each element in teaser-grid/news-grid
public const MAX_WORDS_COUNT = 20;
private string $flyOutNavigationPath;
public function __construct(
private readonly NewsRepository $newsRepository,
private readonly UseCaseRepository $useCaseRepository,
private readonly RequestStack $requestStack,
string $projectDir,
private readonly bool $forceWebpThumbnails,
private readonly array $forceWebpThumbnailsTypes
)
{
$this->flyOutNavigationPath = $projectDir . '/navigation/flyOutNavigation.json';
}
/**
* @return array
*/
public function getFlyOutNavigation(): array
{
if (!file_exists($this->flyOutNavigationPath)) {
throw new FileNotFoundException('Fly-Out-Navigation JSON file does not exist!');
}
$contentJSON = file_get_contents($this->flyOutNavigationPath);
$webSpaceKey = $this->getCurrentWebspace()->getKey();
$flyoutData = json_decode($contentJSON, true);
return $flyoutData[$webSpaceKey];
}
/**
* Return all news from database.
* News are filtered by tags from news-grid.
*
* @param string|null $newsGridTags
* @return array
*/
public function getNews(?string $newsGridTags): array
{
$newsArray = [];
$news = $this->newsRepository->findAll();
if (count($news) > 0) {
/** @var News $item */
foreach ($news as $item) {
$itemTags = $item->getTags();
// news-grid has Tags
if (!empty($newsGridTags) && !empty(trim($newsGridTags))) {
// But news item does not --> Continue to next news item!
if (empty($itemTags)) {
continue;
}
if (str_contains($itemTags, ',') !== false) {
$newsItemTagsArray = explode(',', $itemTags);
$newsItemTagsArray = array_map('trim', $newsItemTagsArray);
} else {
$newsItemTagsArray = [trim($itemTags)];
}
$matched = false;
foreach ($newsItemTagsArray as $newsItemTag) {
// news item has at least one tag that matches news-grid Tags
if (str_contains(strtolower($newsGridTags), strtolower($newsItemTag)) !== false) {
$matched = true;
break;
}
}
// news item does not have any tag that matches news-grid Tags --> Continue to next news item!
if (!$matched) {
continue;
}
}
$image = json_decode($item->getImage(), true);
$dateObject = new DateTime($item->getDate());
$newsArray[] = [
'title' => $item->getTitle(),
'description' => $item->getDescription(),
'source' => $item->getSource(),
'date' => $item->getDate(),
'dateGerman' => $dateObject->format('d.m.Y'),
'image' => $image['id'],
'thumbnails' => null,
'url' => $item->getUrl(),
];
}
}
return $newsArray;
}
/**
* Return all news from database.
* News are filtered by tags from news-grid.
*
* @return array
*/
public function getUseCases(string $locale, array $tags): array
{
$useCasesArray = [];
$useCases = $tags ? $this->useCaseRepository->findByTagsWithTopics($locale, $tags) : $this->useCaseRepository->findAllWithTopics($locale);
if (count($useCases) > 0) {
foreach ($useCases as $item) {
$image = json_decode($item->getImage(), true);
$useCasesArray[] = [
'topic' => [
'title' => $item->getUseCaseTopic()->getTitle(),
'description' => $item->getUseCaseTopic()->getDescription(),
'color' => $item->getUseCaseTopic()->getColor(),
],
'title' => $item->getTitle(),
'titleMobile' => $item->getTitleMobile(),
'description' => $item->getDescription(),
'image' => $image['id'],
'tags' => $item->getTags(),
'thumbnails' => null,
];
}
}
return $useCasesArray;
}
/**
* - Map stories to array like news
* - Merge stories and news
* - Sort by date
*
* @param array $stories
* @param array $publications
* @param array $pages
* @param array $news
* @param bool $contentMixed
* @return array
*/
public function sortByDate(
array $stories,
array $publications,
array $pages,
array $news,
bool $contentMixed = false
): array {
$pagesArray = [];
// Get other pages only if contentMixed is true
if ($contentMixed) {
$pagesArray = array_merge($stories, $publications, $pages);
}
$newsArray = $news;
if (count($pagesArray) > 0) {
$newsArray = array_merge($pagesArray, $news);
}
// Sort descending by date
usort($newsArray, function ($a, $b) {
return $a['date'] < $b['date'];
});
return $newsArray;
}
/**
* Prepare data from smart content for news-grid.
* If description has more than 20 words, the first 20 words will be cut and ... will be added to the end.
* If alternativeText is available, replace description. Text will not be cut. Length remains unchanged.
*
* @param array $items
* @param string $locale
* @return array
*/
public function getDataFromSmartContent(array $items, string $locale): array
{
$data = [];
/** @var ArrayAccessItem $item */
foreach ($items as $item) {
/** @var Media $image */
$excerptImage = $item['excerptAlternativeImage'] ?? $item['excerptImages'] ?? null;
$thumbnails = $excerptImage instanceof Media ? $excerptImage->getThumbnails() : null;
// Teaser grid title is preferred, then title!
$title = $item['excerptTitleTeaserGrid'] ?? '';
if (empty($title)) {
$title = $item['excerptTitle'] ?? '';
}
// Custom Text is preferred, then description!
$description = $item['excerptCustomText'] ?? '';
if (empty($description)) {
$description = $item['excerptDescription'] ?? '';
if (!empty($description)) {
$descriptionArray = explode(' ', $description);
// Get the first 20 words
if (count($descriptionArray) > self::MAX_WORDS_COUNT) {
$descriptionArray = array_slice($descriptionArray, 0, self::MAX_WORDS_COUNT);
$description = implode(' ', $descriptionArray);
$description .= ' ...';
}
}
}
// Creation date from settings is date of story!
$authored = $item['authored'];
$data[] = [
'title' => $title,
'description' => $description,
'source' => $item['excerptType'] ?? '',
'date' => $authored->format('Y-m-d'),
'dateGerman' => $authored->format('d.m.Y'),
'image' => $thumbnails['2880x'] ?? null,
'thumbnails' => $thumbnails,
'url' => '/' . $locale . $item['url'], // Add locale to URL
];
}
return $data;
}
public function getThumbnailUrl($media, $size)
{
if($media instanceof Media) {
$thumbnails = $media->getThumbnails();
if(!array_key_exists($size, $thumbnails)) {
throw new \InvalidArgumentException('Unknown thumbnail size "' . $size . '".');
}
if($this->forceWebpThumbnails && in_array($media->getFileVersion()->getMimeType(), $this->forceWebpThumbnailsTypes)) {
$size .= '.webp';
}
return $thumbnails[$size];
} else if(is_array($media) && array_key_exists('thumbnails', $media)) {
$thumbnails = $media['thumbnails'];
if(!array_key_exists($size, $thumbnails)) {
throw new \InvalidArgumentException('Unknown thumbnail size "' . $size . '".');
}
$extensionMimeMap = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'webp' => 'image/webp'
];
$ext = strtolower(pathinfo(parse_url($media['image'], PHP_URL_PATH), PATHINFO_EXTENSION));
if($this->forceWebpThumbnails && array_key_exists($ext, $extensionMimeMap) && in_array($extensionMimeMap[$ext], $this->forceWebpThumbnailsTypes)) {
$size .= '.webp';
}
return $thumbnails[$size];
}
}
private function getCurrentWebspace(): ?Webspace
{
$currentRequest = $this->requestStack->getCurrentRequest();
if (!$currentRequest) {
return null;
}
$suluAttributes = $currentRequest->attributes->get('_sulu');
if (!$suluAttributes instanceof RequestAttributes) {
return null;
}
return $suluAttributes->getAttribute('webspace');
}
}