Initial commit - Typo3 11.5.41

This commit is contained in:
Matteo Gallo
2026-07-03 17:53:31 +02:00
commit 5ca4743197
6811 changed files with 568848 additions and 0 deletions

View File

@@ -0,0 +1,383 @@
<?php
namespace GeorgRinger\News\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Category Model
*/
class Category extends AbstractEntity
{
/**
* @var int
*/
protected $sorting = 0;
/**
* @var \DateTime
*/
protected $crdate;
/**
* @var \DateTime
*/
protected $tstamp;
/**
* @var \DateTime
*/
protected $starttime;
/**
* @var \DateTime
*/
protected $endtime;
/**
* @var bool
*/
protected $hidden = false;
/**
* @var int
*/
protected $sysLanguageUid = 0;
/**
* @var int
*/
protected $l10nParent = 0;
/**
* @var string
*/
protected $title = '';
/**
* @var string
*/
protected $description = '';
/**
* @var \GeorgRinger\News\Domain\Model\Category
* @TYPO3\CMS\Extbase\Annotation\ORM\Lazy
*/
protected $parentcategory;
/**
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\GeorgRinger\News\Domain\Model\FileReference>
* @TYPO3\CMS\Extbase\Annotation\ORM\Lazy
*/
protected $images;
/**
* @var int
*/
protected $shortcut = 0;
/**
* @var int
*/
protected $singlePid = 0;
/**
* @var string
*/
protected $importId = '';
/**
* @var string
*/
protected $importSource = '';
/**
* keep it as string as it should be only used during imports
* @var string
*/
protected $feGroup = '';
/**
* @var string
*/
protected $seoTitle = '';
/**
* @var string
*/
protected $seoDescription = '';
/**
* @var string
*/
protected $seoHeadline = '';
/**
* @var string
*/
protected $seoText = '';
/**
* @var string
*/
protected $slug = '';
public function __construct()
{
$this->images = new ObjectStorage();
}
public function getSorting(): int
{
return $this->sorting;
}
public function setSorting(int $sorting): void
{
$this->sorting = $sorting;
}
public function getCrdate(): ?\DateTime
{
return $this->crdate;
}
public function setCrdate(\DateTime $crdate): void
{
$this->crdate = $crdate;
}
public function getTstamp(): ?\DateTime
{
return $this->tstamp;
}
public function setTstamp(\DateTime $tstamp): void
{
$this->tstamp = $tstamp;
}
public function getStarttime(): ?\DateTime
{
return $this->starttime;
}
public function setStarttime(\DateTime $starttime): void
{
$this->starttime = $starttime;
}
public function getEndtime(): ?\DateTime
{
return $this->endtime;
}
public function setEndtime(\DateTime $endtime): void
{
$this->endtime = $endtime;
}
public function getHidden(): bool
{
return $this->hidden;
}
public function setHidden(bool $hidden): void
{
$this->hidden = $hidden;
}
public function getSysLanguageUid(): int
{
// int cast is needed as $this->_languageUid is null by default
return (int)$this->_languageUid;
}
public function setSysLanguageUid(int $sysLanguageUid): void
{
$this->_languageUid = $sysLanguageUid;
}
public function getL10nParent(): int
{
return $this->l10nParent;
}
public function setL10nParent(int $l10nParent): void
{
$this->l10nParent = $l10nParent;
}
public function getTitle(): string
{
return $this->title;
}
public function setTitle(string $title): void
{
$this->title = $title;
}
public function getDescription(): string
{
return $this->description;
}
public function setDescription(string $description): void
{
$this->description = $description;
}
public function getParentcategory(): ?\GeorgRinger\News\Domain\Model\Category
{
return $this->parentcategory instanceof LazyLoadingProxy
? $this->parentcategory->_loadRealInstance()
: $this->parentcategory;
}
public function setParentcategory(\GeorgRinger\News\Domain\Model\Category $category): void
{
$this->parentcategory = $category;
}
/**
* @psalm-return ObjectStorage<FileReference>
*/
public function getImages(): ObjectStorage
{
return $this->images;
}
public function setImages(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $images): void
{
$this->images = $images;
}
public function addImage(FileReference $image): void
{
$this->images->attach($image);
}
public function removeImage(FileReference $image): void
{
$this->images->detach($image);
}
public function getFirstImage(): ?FileReference
{
$images = $this->getImages();
$images->rewind();
return $images->valid() ? $images->current() : null;
}
public function getShortcut(): int
{
return $this->shortcut;
}
public function setShortcut(int $shortcut): void
{
$this->shortcut = $shortcut;
}
public function getSinglePid(): int
{
return $this->singlePid;
}
public function setSinglePid(int $singlePid): void
{
$this->singlePid = $singlePid;
}
public function getImportId(): string
{
return $this->importId;
}
public function setImportId(string $importId): void
{
$this->importId = $importId;
}
public function getImportSource(): string
{
return $this->importSource;
}
public function setImportSource(string $importSource): void
{
$this->importSource = $importSource;
}
public function getFeGroup(): string
{
return $this->feGroup;
}
public function setFeGroup(string $feGroup): void
{
$this->feGroup = $feGroup;
}
public function getSeoTitle(): string
{
return $this->seoTitle;
}
public function setSeoTitle(string $seoTitle): void
{
$this->seoTitle = $seoTitle;
}
public function getSeoDescription(): string
{
return $this->seoDescription;
}
public function setSeoDescription(string $seoDescription): void
{
$this->seoDescription = $seoDescription;
}
public function getSeoHeadline(): string
{
return $this->seoHeadline;
}
public function setSeoHeadline(string $seoHeadline): void
{
$this->seoHeadline = $seoHeadline;
}
public function getSeoText(): string
{
return $this->seoText;
}
public function setSeoText(string $seoText): void
{
$this->seoText = $seoText;
}
public function getSlug(): string
{
return $this->slug;
}
public function setSlug(string $slug): void
{
$this->slug = $slug;
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace GeorgRinger\News\Domain\Model;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Demanded repository interface
*/
interface DemandInterface
{
}

View File

@@ -0,0 +1,180 @@
<?php
namespace GeorgRinger\News\Domain\Model\Dto;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Administration Demand model
*/
class AdministrationDemand extends NewsDemand
{
/**
* @var string
*/
protected $recursive = '';
/**
* @var array
*/
protected $selectedCategories = [];
/**
* @var string
*/
protected $sortingField = 'datetime';
/**
* @var string
*/
protected $sortingDirection = 'desc';
/**
* @var string
*/
protected $searchWord = '';
/**
* @var int
*/
protected $hidden = 0;
/**
* @var int
*/
protected $archived = 0;
/**
* @return string
*/
public function getRecursive(): string
{
return $this->recursive;
}
/**
* @param string $recursive
*
* @return void
*/
public function setRecursive(string $recursive): void
{
$this->recursive = $recursive;
}
/**
* @return array
*/
public function getSelectedCategories(): array
{
return $this->selectedCategories;
}
/**
* @param string|array $selectedCategories
*
* @return void
*/
public function setSelectedCategories($selectedCategories)
{
if ($selectedCategories === '0' || $selectedCategories === ['0']) {
return;
}
if (is_string($selectedCategories)) {
$selectedCategories = explode(',', $selectedCategories);
}
$this->selectedCategories = $selectedCategories;
}
/**
* @return string
*/
public function getSortingField(): string
{
return $this->sortingField;
}
/**
* @param string $sortingField
* @return void
*/
public function setSortingField(string $sortingField): void
{
$this->sortingField = $sortingField;
}
/**
* @return string
*/
public function getSortingDirection(): string
{
return $this->sortingDirection;
}
/**
* @param string $sortingDirection
* @return void
*/
public function setSortingDirection(string $sortingDirection): void
{
$this->sortingDirection = $sortingDirection;
}
/**
* @return string
*/
public function getSearchWord(): string
{
return $this->searchWord;
}
/**
* @param string $searchWord
* @return void
*/
public function setSearchWord(string $searchWord): void
{
$this->searchWord = $searchWord;
}
/**
* @return int
*/
public function getHidden(): int
{
return $this->hidden;
}
/**
* @param int $hidden
* @return void
*/
public function setHidden(int $hidden): void
{
$this->hidden = $hidden;
}
/**
* @return int
*/
public function getArchived(): int
{
return $this->archived;
}
/**
* @param int $archived
*
* @return void
*/
public function setArchived(int $archived): void
{
$this->archived = $archived;
}
}

View File

@@ -0,0 +1,188 @@
<?php
namespace GeorgRinger\News\Domain\Model\Dto;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Extension Manager configuration
*/
class EmConfiguration
{
/**
* Fill the properties properly
*
* @param array $configuration em configuration
*/
public function __construct(array $configuration = [])
{
if (empty($configuration)) {
try {
$extensionConfiguration = GeneralUtility::makeInstance(ExtensionConfiguration::class);
$configuration = $extensionConfiguration->get('news');
} catch (\Exception $exception) {
// do nothing
}
}
foreach ($configuration as $key => $value) {
if (property_exists(__CLASS__, $key)) {
$this->$key = $value;
}
}
}
/** @var int */
protected $tagPid = 0;
/** @var bool */
protected $prependAtCopy = true;
/** @var string */
protected $categoryRestriction = '';
/** @var bool */
protected $categoryBeGroupTceFormsRestriction = false;
/** @var bool */
protected $contentElementRelation = true;
/** @var bool */
protected $contentElementPreview = true;
/** @var bool */
protected $manualSorting = false;
/** @var string */
protected $archiveDate = 'date';
/** @var bool */
protected $dateTimeNotRequired = false;
/** @var bool */
protected $showImporter = false;
/** @var bool */
protected $rteForTeaser = false;
/** @var bool */
protected $showAdministrationModule = true;
/** @var bool */
protected $hidePageTreeForAdministrationModule = false;
/** @var int */
protected $storageUidImporter = 1;
/** @var string */
protected $resourceFolderImporter = '/news_import';
/** @var bool */
protected $advancedMediaPreview = true;
/** @var string */
protected $slugBehaviour = 'unique';
public function getTagPid(): int
{
return (int)$this->tagPid;
}
public function getPrependAtCopy(): bool
{
return (bool)$this->prependAtCopy;
}
public function getCategoryRestriction(): string
{
return $this->categoryRestriction;
}
public function getCategoryBeGroupTceFormsRestriction(): bool
{
return (bool)$this->categoryBeGroupTceFormsRestriction;
}
public function getContentElementRelation(): bool
{
return (bool)$this->contentElementRelation;
}
public function getContentElementPreview(): bool
{
return (bool)$this->contentElementPreview;
}
public function getManualSorting(): bool
{
return (bool)$this->manualSorting;
}
public function getArchiveDate(): string
{
return $this->archiveDate;
}
public function getShowImporter(): bool
{
return (bool)$this->showImporter;
}
public function setShowAdministrationModule($showAdministrationModule): void
{
$this->showAdministrationModule = $showAdministrationModule;
}
public function getShowAdministrationModule(): bool
{
return (bool)$this->showAdministrationModule;
}
public function getRteForTeaser(): bool
{
return (bool)$this->rteForTeaser;
}
public function getResourceFolderImporter(): string
{
return $this->resourceFolderImporter;
}
public function getStorageUidImporter(): int
{
return (int)$this->storageUidImporter;
}
public function getDateTimeNotRequired(): bool
{
return (bool)$this->dateTimeNotRequired;
}
public function getDateTimeRequired(): bool
{
return !(bool)$this->dateTimeNotRequired;
}
public function getHidePageTreeForAdministrationModule(): bool
{
return (bool)$this->hidePageTreeForAdministrationModule;
}
public function isAdvancedMediaPreview(): bool
{
return (bool)$this->advancedMediaPreview;
}
public function getSlugBehaviour(): string
{
return $this->slugBehaviour;
}
}

View File

@@ -0,0 +1,734 @@
<?php
namespace GeorgRinger\News\Domain\Model\Dto;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use GeorgRinger\News\Domain\Model\DemandInterface;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/**
* News Demand object which holds all information to get the correct news records.
*/
class NewsDemand extends AbstractEntity implements DemandInterface
{
/**
* @var array
*/
protected $categories = [];
/**
* @var string
*/
protected $categoryConjunction = '';
/**
* @var bool
*/
protected $includeSubCategories = false;
/**
* @var string
*/
protected $author = '';
/** @var string */
protected $tags = '';
/**
* @var string
*/
protected $archiveRestriction = '';
/**
* @var string
*/
protected $timeRestriction = '';
/** @var string */
protected $timeRestrictionHigh = '';
/** @var int */
protected $topNewsRestriction = 0;
/** @var string */
protected $dateField = '';
/** @var int */
protected $month = 0;
/** @var int */
protected $year = 0;
/** @var int */
protected $day = 0;
/** @var string */
protected $searchFields = '';
/** @var Search */
protected $search;
/** @var string */
protected $order = '';
/** @var string */
protected $orderByAllowed = '';
/** @var bool */
protected $topNewsFirst = false;
/** @var string */
protected $storagePage = '';
/** @var int */
protected $limit = 0;
/** @var int */
protected $offset = 0;
/** @var bool */
protected $excludeAlreadyDisplayedNews = false;
/** @var string */
protected $hideIdList = '';
/** @var string */
protected $idList = '';
/** @var string */
protected $action = '';
/** @var string */
protected $class = '';
/**
* List of allowed types
*
* @var array
*/
protected $types = [];
/**
* Holding custom data, use e.g. your ext key as array key
*
* @var array
*/
protected $_customSettings = [];
/**
* Set archive settings
*
* @param string $archiveRestriction archive setting
* @return NewsDemand
*/
public function setArchiveRestriction(string $archiveRestriction): NewsDemand
{
$this->archiveRestriction = $archiveRestriction;
return $this;
}
/**
* Get archive setting
*
* @return string
*/
public function getArchiveRestriction(): string
{
return $this->archiveRestriction;
}
/**
* List of allowed categories
*
* @param array $categories categories
* @return NewsDemand
*/
public function setCategories(array $categories): NewsDemand
{
$this->categories = $categories;
return $this;
}
/**
* Get allowed categories
*
* @return array
*/
public function getCategories(): array
{
return $this->categories;
}
/**
* Set category mode
*
* @param string $categoryConjunction
* @return NewsDemand
*/
public function setCategoryConjunction(string $categoryConjunction): NewsDemand
{
$this->categoryConjunction = $categoryConjunction;
return $this;
}
/**
* Get category mode
*
* @return string
*/
public function getCategoryConjunction(): string
{
return $this->categoryConjunction;
}
/**
* Get include sub categories
* @return bool
*/
public function getIncludeSubCategories(): bool
{
return (boolean)$this->includeSubCategories;
}
/**
* @param bool $includeSubCategories
* @return NewsDemand
*/
public function setIncludeSubCategories(bool $includeSubCategories): NewsDemand
{
$this->includeSubCategories = $includeSubCategories;
return $this;
}
/**
* Set author
*
* @param string $author
* @return NewsDemand
*/
public function setAuthor(string $author): NewsDemand
{
$this->author = $author;
return $this;
}
/**
* Get author
*
* @return string
*/
public function getAuthor(): string
{
return $this->author;
}
/**
* Get Tags
*
* @return string
*/
public function getTags(): string
{
return $this->tags;
}
/**
* Set Tags
*
* @param string $tags tags
* @return NewsDemand
*/
public function setTags(string $tags): NewsDemand
{
$this->tags = $tags;
return $this;
}
/**
* Set time limit low, either integer or string
*
* @param string|int $timeRestriction
* @return NewsDemand
*/
public function setTimeRestriction($timeRestriction): NewsDemand
{
$this->timeRestriction = $timeRestriction;
return $this;
}
/**
* Get time limit low
*
* @return string|int
*/
public function getTimeRestriction()
{
return $this->timeRestriction;
}
/**
* Get time limit high
*
* @return string|int
*/
public function getTimeRestrictionHigh()
{
return $this->timeRestrictionHigh;
}
/**
* Set time limit high
*
* @param string|int $timeRestrictionHigh
* @return NewsDemand
*/
public function setTimeRestrictionHigh($timeRestrictionHigh): NewsDemand
{
$this->timeRestrictionHigh = $timeRestrictionHigh;
return $this;
}
/**
* Set order
*
* @param string $order order
* @return NewsDemand
*/
public function setOrder(string $order): NewsDemand
{
$this->order = $order;
return $this;
}
/**
* Get order
*
* @return string
*/
public function getOrder(): string
{
return $this->order;
}
/**
* Set order allowed
*
* @param string $orderByAllowed allowed fields for ordering
* @return NewsDemand
*/
public function setOrderByAllowed(string $orderByAllowed): NewsDemand
{
$this->orderByAllowed = $orderByAllowed;
return $this;
}
/**
* Get allowed order fields
*
* @return string
*/
public function getOrderByAllowed(): string
{
return $this->orderByAllowed;
}
/**
* Set order respect top news flag
*
* @param bool $topNewsFirst respect top news flag
* @return NewsDemand
*/
public function setTopNewsFirst(bool $topNewsFirst): NewsDemand
{
$this->topNewsFirst = $topNewsFirst;
return $this;
}
/**
* Get order respect top news flag
*
* @return bool
*/
public function getTopNewsFirst(): bool
{
return $this->topNewsFirst;
}
/**
* Set search fields
*
* @param string $searchFields search fields
*
* @return NewsDemand
*/
public function setSearchFields(string $searchFields): NewsDemand
{
$this->searchFields = $searchFields;
return $this;
}
/**
* Get search fields
*
* @return string
*/
public function getSearchFields(): string
{
return $this->searchFields;
}
/**
* Set top news setting
*
* @param int $topNewsRestriction top news settings
* @return NewsDemand
*/
public function setTopNewsRestriction(int $topNewsRestriction): NewsDemand
{
$this->topNewsRestriction = $topNewsRestriction;
return $this;
}
/**
* Get top news setting
*
* @return int
*/
public function getTopNewsRestriction(): int
{
return $this->topNewsRestriction;
}
/**
* Set list of storage pages
*
* @param string $storagePage storage page list
* @return NewsDemand
*/
public function setStoragePage(string $storagePage): NewsDemand
{
$this->storagePage = $storagePage;
return $this;
}
/**
* Get list of storage pages
*
* @return string
*/
public function getStoragePage(): string
{
return $this->storagePage;
}
/**
* Get day restriction
*
* @return int
*/
public function getDay(): int
{
return $this->day;
}
/**
* Set day restriction
*
* @param int $day
* @return NewsDemand
*/
public function setDay(int $day): NewsDemand
{
$this->day = (int)$day;
return $this;
}
/**
* Get month restriction
*
* @return int
*/
public function getMonth(): int
{
return $this->month;
}
/**
* Set month restriction
*
* @param int $month month
* @return NewsDemand
*/
public function setMonth(int $month): NewsDemand
{
$this->month = (int)$month;
return $this;
}
/**
* Get year restriction
*
* @return int
*/
public function getYear(): int
{
return $this->year;
}
/**
* Set year restriction
*
* @param int $year year
* @return NewsDemand
*/
public function setYear(int $year): NewsDemand
{
$this->year = (int)$year;
return $this;
}
/**
* Set limit
*
* @param int $limit limit
* @return NewsDemand
*/
public function setLimit(int $limit): NewsDemand
{
$this->limit = $limit;
return $this;
}
/**
* Get limit
*
* @return int
*/
public function getLimit(): int
{
return $this->limit;
}
/**
* Set offset
*
* @param int $offset offset
* @return NewsDemand
*/
public function setOffset(int $offset): NewsDemand
{
$this->offset = $offset;
return $this;
}
/**
* Get offset
*
* @return int
*/
public function getOffset(): int
{
return $this->offset;
}
/**
* Set date field which is used for datemenu
*
* @param string $dateField datefield
* @return NewsDemand
*/
public function setDateField(string $dateField): NewsDemand
{
$this->dateField = $dateField;
return $this;
}
/**
* Get date field which is used for datemenu
*
* @return string
*/
public function getDateField(): string
{
if (in_array($this->dateField, ['datetime', 'archive'], true)
|| isset($GLOBALS['TCA']['tx_news_domain_model_news']['columns'][$this->dateField])) {
return $this->dateField;
}
return '';
}
/**
* Get search object
*
* @return null|Search
*/
public function getSearch(): ?Search
{
return $this->search;
}
/**
* Set search object
*
* @param null|Search $search search object
* @return NewsDemand
*/
public function setSearch(Search $search = null): NewsDemand
{
$this->search = $search;
return $this;
}
/**
* Set flag if displayed news records should be excluded
*
* @param bool $excludeAlreadyDisplayedNews
* @return NewsDemand
*/
public function setExcludeAlreadyDisplayedNews(bool $excludeAlreadyDisplayedNews): NewsDemand
{
$this->excludeAlreadyDisplayedNews = $excludeAlreadyDisplayedNews;
return $this;
}
/**
* Get flag if displayed news records should be excluded
*
* @return bool
*/
public function getExcludeAlreadyDisplayedNews(): bool
{
return $this->excludeAlreadyDisplayedNews;
}
/**
* @return string
*/
public function getHideIdList(): string
{
return $this->hideIdList;
}
/**
* @param string $hideIdList
* @return NewsDemand
*/
public function setHideIdList(string $hideIdList): NewsDemand
{
$this->hideIdList = $hideIdList;
return $this;
}
/**
* @return string
*/
public function getIdList(): string
{
return $this->idList;
}
/**
* @param string $idList
* @return NewsDemand
*/
public function setIdList(string $idList): NewsDemand
{
$this->idList = $idList;
return $this;
}
/**
* @return string
*/
public function getAction(): string
{
return $this->action;
}
/**
* @param string $action
* @return NewsDemand
*/
public function setAction(string $action): NewsDemand
{
$this->action = $action;
return $this;
}
/**
* @return string
*/
public function getClass(): string
{
return $this->class;
}
/**
* @param string $class
* @return NewsDemand
*/
public function setClass(string $class): NewsDemand
{
$this->class = $class;
return $this;
}
/**
* @param string $action
* @param string $controller
* @return NewsDemand
*/
public function setActionAndClass(string $action, string $controller): NewsDemand
{
$this->action = $action;
$this->class = $controller;
return $this;
}
/**
* Get allowed types
*
* @return array
*/
public function getTypes(): array
{
return $this->types;
}
/**
* Set allowed types
*
* @param array $types
*
* @return void
*/
public function setTypes(array $types): void
{
$this->types = $types;
}
/**
* @return array
*/
public function getCustomSettings(): array
{
return $this->_customSettings;
}
/**
* @param array $customSettings
*
* @return void
*/
public function setCustomSettings(array $customSettings): void
{
$this->_customSettings = $customSettings;
}
}

View File

@@ -0,0 +1,172 @@
<?php
namespace GeorgRinger\News\Domain\Model\Dto;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* News Demand object which holds all information to get the correct
* news records.
*/
class Search extends AbstractEntity
{
/**
* Basic search word
*
* @var string
*/
protected $subject = '';
/**
* Search fields
*
* @var string
*/
protected $fields = '';
/**
* Minimum date
*
* @var string
*/
protected $minimumDate = '';
/**
* Maximum date
*
* @var string
*/
protected $maximumDate = '';
/**
* Field using for date queries
*
* @var string
*/
protected $dateField = '';
/** @var bool */
protected $splitSubjectWords = false;
/**
* Get the subject
*
* @return string
*/
public function getSubject(): string
{
return $this->subject;
}
/**
* Set subject
*
* @param string $subject
*/
public function setSubject(string $subject): void
{
$this->subject = $subject;
}
/**
* Get fields
*
* @return string
*/
public function getFields(): string
{
return $this->fields;
}
/**
* Set fields
*
* @param string $fields
*
* @return void
*/
public function setFields(string $fields): void
{
$this->fields = $fields;
}
/**
* @param string $maximumDate
*
* @return void
*/
public function setMaximumDate($maximumDate): void
{
$this->maximumDate = $maximumDate;
}
/**
* @return string
*/
public function getMaximumDate(): string
{
return $this->maximumDate;
}
/**
* @param string $minimumDate
*
* @return void
*/
public function setMinimumDate(string $minimumDate): void
{
$this->minimumDate = $minimumDate;
}
/**
* @return string
*/
public function getMinimumDate(): string
{
return $this->minimumDate;
}
/**
* @param string $dateField
*
* @return void
*/
public function setDateField($dateField): void
{
$this->dateField = $dateField;
}
/**
* @return string
*/
public function getDateField(): string
{
return $this->dateField;
}
/**
* @return bool
*/
public function isSplitSubjectWords(): bool
{
return $this->splitSubjectWords;
}
/**
* @param bool $splitSubjectWords
*
* @return void
*/
public function setSplitSubjectWords($splitSubjectWords): void
{
$this->splitSubjectWords = $splitSubjectWords;
}
}

View File

@@ -0,0 +1,184 @@
<?php
namespace GeorgRinger\News\Domain\Model;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* File Reference
*/
class FileReference extends \TYPO3\CMS\Extbase\Domain\Model\FileReference
{
const VIEW_DETAIL_ONLY = 0;
const VIEW_LIST_AND_DETAIL = 1;
const VIEW_LIST_ONLY = 2;
/**
* Obsolete when foreign_selector is supported by ExtBase persistence layer
*
* @var int
*/
protected $uidLocal = 0;
/**
* @var string
*/
protected $title = '';
/**
* @var string
*/
protected $description = '';
/**
* @var string
*/
protected $alternative = '';
/**
* @var string
*/
protected $link = '';
/**
* @var int
*/
protected $showinpreview = 0;
/**
* Set File uid
*
* @param int $fileUid
*
* @return void
*/
public function setFileUid($fileUid): void
{
$this->uidLocal = $fileUid;
}
/**
* Get File UID
*
* @return int
*/
public function getFileUid(): int
{
return $this->uidLocal;
}
/**
* Set alternative
*
* @param string $alternative
*
* @return void
*/
public function setAlternative($alternative): void
{
$this->alternative = $alternative;
}
/**
* Get alternative
*
* @return string
*/
public function getAlternative(): string
{
return (string)($this->alternative !== '' ? $this->alternative : $this->getOriginalResource()->getAlternative());
}
/**
* Set description
*
* @param string $description
*
* @return void
*/
public function setDescription($description): void
{
$this->description = $description;
}
/**
* Get description
*
* @return string
*/
public function getDescription(): string
{
return (string)($this->description !== '' ? $this->description : $this->getOriginalResource()->getDescription());
}
/**
* Set link
*
* @param string $link
*
* @return void
*/
public function setLink($link): void
{
$this->link = $link;
}
/**
* Get link
*
* @return mixed
*/
public function getLink()
{
return (string)($this->link !== '' ? $this->link : $this->getOriginalResource()->getLink());
}
/**
* Set title
*
* @param string $title
*
* @return void
*/
public function setTitle($title): void
{
$this->title = $title;
}
/**
* Get title
*
* @return string
*/
public function getTitle(): string
{
return (string)($this->title !== '' ? $this->title : $this->getOriginalResource()->getTitle());
}
/**
* Set showinpreview
*
* @param int $showinpreview
*
* @return void
*/
public function setShowinpreview($showinpreview): void
{
$this->showinpreview = $showinpreview;
}
/**
* Get showinpreview
*
* @return int
*/
public function getShowinpreview(): int
{
return $this->showinpreview;
}
}

View File

@@ -0,0 +1,202 @@
<?php
namespace GeorgRinger\News\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Link model
*/
class Link extends AbstractValueObject
{
/**
* @var \DateTime
*/
protected $crdate;
/**
* @var \DateTime
*/
protected $tstamp;
/**
* @var string
*/
protected $title = '';
/**
* @var string
*/
protected $description = '';
/**
* @var string
*/
protected $uri = '';
/**
* @var int
*/
protected $l10nParent = 0;
/**
* Get creation date
*
* @return null|\DateTime
*/
public function getCrdate(): ?\DateTime
{
return $this->crdate;
}
/**
* Set creation date
*
* @param \DateTime $crdate creation date
*
* @return void
*/
public function setCrdate($crdate): void
{
$this->crdate = $crdate;
}
/**
* Get timestamp
*
* @return null|\DateTime
*/
public function getTstamp(): ?\DateTime
{
return $this->tstamp;
}
/**
* Set timestamp
*
* @param \DateTime $tstamp timestamp
*
* @return void
*/
public function setTstamp($tstamp): void
{
$this->tstamp = $tstamp;
}
/**
* Get title
*
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* Set title
*
* @param string $title title
*
* @return void
*/
public function setTitle($title): void
{
$this->title = $title;
}
/**
* Get description
*
* @return string
*/
public function getDescription(): string
{
return $this->description;
}
/**
* Set description
*
* @param string $description description
*
* @return void
*/
public function setDescription($description): void
{
$this->description = $description;
}
/**
* Get uri
*
* @return string
*/
public function getUri(): string
{
return $this->uri;
}
/**
* Set uri
*
* @param string $uri uri
*
* @return void
*/
public function setUri($uri): void
{
$this->uri = $uri;
}
/**
* Set sys language
*
* @param int $sysLanguageUid
*
* @return void
*/
public function setSysLanguageUid($sysLanguageUid): void
{
$this->_languageUid = $sysLanguageUid;
}
/**
* Get sys language
*
* @return int
*/
public function getSysLanguageUid(): int
{
return $this->_languageUid;
}
/**
* Set l10n parent
*
* @param int $l10nParent
*
* @return void
*/
public function setL10nParent($l10nParent): void
{
$this->l10nParent = $l10nParent;
}
/**
* Get l10n parent
*
* @return int
*/
public function getL10nParent(): int
{
return $this->l10nParent;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
<?php
namespace GeorgRinger\News\Domain\Model;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* News model for default news
*/
class NewsDefault extends News
{
}

View File

@@ -0,0 +1,17 @@
<?php
namespace GeorgRinger\News\Domain\Model;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* News model for external news
*/
class NewsExternal extends News
{
}

View File

@@ -0,0 +1,17 @@
<?php
namespace GeorgRinger\News\Domain\Model;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* News model for internal news
*/
class NewsInternal extends News
{
}

View File

@@ -0,0 +1,212 @@
<?php
namespace GeorgRinger\News\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Tag model
*/
class Tag extends AbstractValueObject
{
/**
* @var \DateTime
*/
protected $crdate;
/**
* @var \DateTime
*/
protected $tstamp;
/**
* @var string
*/
protected $title = '';
/**
* @var string
*/
protected $seoTitle = '';
/**
* @var string
*/
protected $seoDescription = '';
/**
* @var string
*/
protected $seoHeadline = '';
/**
* @var string
*/
protected $seoText = '';
/** @var string */
protected $slug = '';
/**
* Get crdate
*
* @return null|\DateTime
*/
public function getCrdate(): ?\DateTime
{
return $this->crdate;
}
/**
* Set crdate
*
* @param \DateTime $crdate crdate
*
* @return void
*/
public function setCrdate($crdate): void
{
$this->crdate = $crdate;
}
/**
* Get Tstamp
*
* @return null|\DateTime
*/
public function getTstamp(): ?\DateTime
{
return $this->tstamp;
}
/**
* Set tstamp
*
* @param \DateTime $tstamp tstamp
*
* @return void
*/
public function setTstamp($tstamp): void
{
$this->tstamp = $tstamp;
}
/**
* Get title
*
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
/**
* Set title
*
* @param string $title title
*
* @return void
*/
public function setTitle($title): void
{
$this->title = $title;
}
/**
* @return string
*/
public function getSeoTitle(): string
{
return $this->seoTitle;
}
/**
* @param string $seoTitle
*
* @return void
*/
public function setSeoTitle($seoTitle): void
{
$this->seoTitle = $seoTitle;
}
/**
* @return string
*/
public function getSeoDescription(): string
{
return $this->seoDescription;
}
/**
* @param string $seoDescription
*
* @return void
*/
public function setSeoDescription($seoDescription): void
{
$this->seoDescription = $seoDescription;
}
/**
* @return string
*/
public function getSeoHeadline(): string
{
return $this->seoHeadline;
}
/**
* @param string $seoHeadline
*
* @return void
*/
public function setSeoHeadline($seoHeadline): void
{
$this->seoHeadline = $seoHeadline;
}
/**
* @return string
*/
public function getSeoText(): string
{
return $this->seoText;
}
/**
* @param string $seoText
*
* @return void
*/
public function setSeoText($seoText): void
{
$this->seoText = $seoText;
}
/**
* @return string
*/
public function getSlug(): string
{
return $this->slug;
}
/**
* @param string $slug
*
* @return void
*/
public function setSlug($slug): void
{
$this->slug = $slug;
}
}

View File

@@ -0,0 +1,573 @@
<?php
namespace GeorgRinger\News\Domain\Model;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Model of tt_content
*/
class TtContent extends AbstractEntity
{
/**
* @var \DateTime
*/
protected $crdate;
/**
* @var \DateTime
*/
protected $tstamp;
/**
* @var string
*/
protected $CType = '';
/**
* @var string
*/
protected $header = '';
/**
* @var string
*/
protected $headerPosition = '';
/**
* @var string
*/
protected $bodytext = '';
/**
* @var int
*/
protected $colPos = 0;
/**
* @var string
*/
protected $image = '';
/**
* @var int
*/
protected $imagewidth = 0;
/**
* @var int
*/
protected $imageorient = 0;
/**
* @var string
*/
protected $imagecaption = '';
/**
* @var int
*/
protected $imagecols = 0;
/**
* @var int
*/
protected $imageborder = 0;
/**
* @var string
*/
protected $media = '';
/**
* @var string
*/
protected $layout = '';
/**
* @var int
*/
protected $cols = 0;
/**
* @var string
*/
protected $subheader = '';
/**
* @var string
*/
protected $headerLink = '';
/**
* @var string
*/
protected $imageLink = '';
/**
* @var string
*/
protected $imageZoom = '';
/**
* @var string
*/
protected $altText = '';
/**
* @var string
*/
protected $titleText = '';
/**
* @var string
*/
protected $headerLayout = '';
/**
* @var string
*/
protected $listType = '';
/**
* @return null|\DateTime
*/
public function getCrdate(): ?\DateTime
{
return $this->crdate;
}
/**
* @param \DateTime $crdate
*
* @return void
*/
public function setCrdate($crdate): void
{
$this->crdate = $crdate;
}
/**
* @return null|\DateTime
*/
public function getTstamp(): ?\DateTime
{
return $this->tstamp;
}
/**
* @param \DateTime $tstamp
*
* @return void
*/
public function setTstamp($tstamp): void
{
$this->tstamp = $tstamp;
}
/**
* @return string
*/
public function getCType(): string
{
return $this->CType;
}
/**
* @param $ctype
*
* @return void
*/
public function setCType(string $ctype): void
{
$this->CType = $ctype;
}
/**
* @return string
*/
public function getHeader(): string
{
return $this->header;
}
/**
* @param $header
*
* @return void
*/
public function setHeader(string $header): void
{
$this->header = $header;
}
/**
* @return string
*/
public function getHeaderPosition(): string
{
return $this->headerPosition;
}
/**
* @param $headerPosition
*
* @return void
*/
public function setHeaderPosition(string $headerPosition): void
{
$this->headerPosition = $headerPosition;
}
/**
* @return string
*/
public function getBodytext(): string
{
return $this->bodytext;
}
/**
* @param $bodytext
*
* @return void
*/
public function setBodytext(string $bodytext): void
{
$this->bodytext = $bodytext;
}
/**
* Get the colpos
*
* @return int
*/
public function getColPos(): int
{
return (int)$this->colPos;
}
/**
* Set colpos
*
* @param int $colPos
*
* @return void
*/
public function setColPos($colPos): void
{
$this->colPos = $colPos;
}
/**
* @return string
*/
public function getImage(): string
{
return $this->image;
}
/**
* @param $image
*
* @return void
*/
public function setImage(string $image): void
{
$this->image = $image;
}
/**
* @return int
*/
public function getImagewidth(): int
{
return $this->imagewidth;
}
/**
* @param $imagewidth
*
* @return void
*/
public function setImagewidth(int $imagewidth): void
{
$this->imagewidth = $imagewidth;
}
/**
* @return int
*/
public function getImageorient(): int
{
return $this->imageorient;
}
/**
* @param $imageorient
*
* @return void
*/
public function setImageorient(int $imageorient): void
{
$this->imageorient = $imageorient;
}
/**
* @return string
*/
public function getImagecaption(): string
{
return $this->imagecaption;
}
/**
* @param $imagecaption
*
* @return void
*/
public function setImagecaption(string $imagecaption): void
{
$this->imagecaption = $imagecaption;
}
/**
* @return int
*/
public function getImagecols(): int
{
return $this->imagecols;
}
/**
* @param $imagecols
*
* @return void
*/
public function setImagecols(int $imagecols): void
{
$this->imagecols = $imagecols;
}
/**
* @return int
*/
public function getImageborder(): int
{
return $this->imageborder;
}
/**
* @param $imageborder
*
* @return void
*/
public function setImageborder(int $imageborder): void
{
$this->imageborder = $imageborder;
}
/**
* @return string
*/
public function getMedia(): string
{
return $this->media;
}
/**
* @param $media
*
* @return void
*/
public function setMedia(string $media): void
{
$this->media = $media;
}
/**
* @return string
*/
public function getLayout(): string
{
return $this->layout;
}
/**
* @param $layout
*
* @return void
*/
public function setLayout(string $layout): void
{
$this->layout = $layout;
}
/**
* @return int
*/
public function getCols(): int
{
return $this->cols;
}
/**
* @param $cols
*
* @return void
*/
public function setCols(int $cols): void
{
$this->cols = $cols;
}
/**
* @return string
*/
public function getSubheader(): string
{
return $this->subheader;
}
/**
* @param $subheader
*
* @return void
*/
public function setSubheader(string $subheader): void
{
$this->subheader = $subheader;
}
/**
* @return string
*/
public function getHeaderLink(): string
{
return $this->headerLink;
}
/**
* @param $headerLink
*
* @return void
*/
public function setHeaderLink(string $headerLink): void
{
$this->headerLink = $headerLink;
}
/**
* @return string
*/
public function getImageLink(): string
{
return $this->imageLink;
}
/**
* @param $imageLink
*
* @return void
*/
public function setImageLink(string $imageLink): void
{
$this->imageLink = $imageLink;
}
/**
* @return string
*/
public function getImageZoom(): string
{
return $this->imageZoom;
}
/**
* @param $imageZoom
*
* @return void
*/
public function setImageZoom(string $imageZoom): void
{
$this->imageZoom = $imageZoom;
}
/**
* @return string
*/
public function getAltText(): string
{
return $this->altText;
}
/**
* @param $altText
*
* @return void
*/
public function setAltText(string $altText): void
{
$this->altText = $altText;
}
/**
* @return string
*/
public function getTitleText(): string
{
return $this->titleText;
}
/**
* @param $titleText
*
* @return void
*/
public function setTitleText(string $titleText): void
{
$this->titleText = $titleText;
}
/**
* @return string
*/
public function getHeaderLayout(): string
{
return $this->headerLayout;
}
/**
* @param $headerLayout
*
* @return void
*/
public function setHeaderLayout(string $headerLayout): void
{
$this->headerLayout = $headerLayout;
}
/**
* @return string
*/
public function getListType(): string
{
return $this->listType;
}
/**
* @param string $listType
* @return void
*/
public function setListType(string $listType): void
{
$this->listType = $listType;
}
}

View File

@@ -0,0 +1,193 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
use GeorgRinger\News\Domain\Model\DemandInterface;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbQueryParser;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Abstract demanded repository
*/
abstract class AbstractDemandedRepository extends Repository implements DemandedRepositoryInterface
{
/**
* @var \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface
*/
protected $storageBackend;
/**
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface $storageBackend
*
* @return void
*/
public function injectStorageBackend(
BackendInterface $storageBackend
): void {
$this->storageBackend = $storageBackend;
}
/**
* Returns an array of constraints created from a given demand object.
*
* @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
* @param DemandInterface $demand
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface[]
* @abstract
*/
abstract protected function createConstraintsFromDemand(
QueryInterface $query,
DemandInterface $demand
): array;
/**
* Returns an array of orderings created from a given demand object.
*
* @param DemandInterface $demand
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface[]
* @abstract
*/
abstract protected function createOrderingsFromDemand(DemandInterface $demand): array;
/**
* Returns the objects of this repository matching the demand.
*
* @param DemandInterface $demand
* @param bool $respectEnableFields
* @param bool $disableLanguageOverlayMode
*
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array
*/
public function findDemanded(DemandInterface $demand, $respectEnableFields = true, $disableLanguageOverlayMode = false)
{
$query = $this->generateQuery($demand, $respectEnableFields, $disableLanguageOverlayMode);
return $query->execute();
}
/**
* Returns the database query to get the matching result
*
* @param DemandInterface $demand
* @param bool $respectEnableFields
* @param bool $disableLanguageOverlayMode
* @return string
*/
public function findDemandedRaw(DemandInterface $demand, $respectEnableFields = true, $disableLanguageOverlayMode = false): string
{
$query = $this->generateQuery($demand, $respectEnableFields, $disableLanguageOverlayMode);
$versionInformation = GeneralUtility::makeInstance(Typo3Version::class);
if ($versionInformation->getMajorVersion() >= 11) {
$queryParser = GeneralUtility::makeInstance(Typo3DbQueryParser::class);
} else {
$queryParser = $this->objectManager->get(Typo3DbQueryParser::class);
}
$queryBuilder = $queryParser->convertQueryToDoctrineQueryBuilder($query);
$queryParameters = $queryBuilder->getParameters();
$params = [];
foreach ($queryParameters as $key => $value) {
// prefix array keys with ':'
$params[':' . $key] = (is_numeric($value)) ? $value : "'" . $value . "'"; //all non numeric values have to be quoted
unset($params[$key]);
}
// replace placeholders with real values
$query = strtr($queryBuilder->getSQL(), $params);
return $query;
}
/**
* @param DemandInterface $demand
* @param bool $respectEnableFields
* @param bool $disableLanguageOverlayMode
* @return \TYPO3\CMS\Extbase\Persistence\QueryInterface
*/
protected function generateQuery(DemandInterface $demand, $respectEnableFields = true, $disableLanguageOverlayMode = false): \TYPO3\CMS\Extbase\Persistence\QueryInterface
{
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
if ($disableLanguageOverlayMode) {
$query->getQuerySettings()->setLanguageOverlayMode(false);
}
$constraints = $this->createConstraintsFromDemand($query, $demand);
// Call hook functions for additional constraints
if ($hooks = $GLOBALS['TYPO3_CONF_VARS']['EXT']['news']['Domain/Repository/AbstractDemandedRepository.php']['findDemanded'] ?? []) {
$params = [
'demand' => $demand,
'respectEnableFields' => &$respectEnableFields,
'query' => $query,
'constraints' => &$constraints,
];
foreach ($hooks as $reference) {
GeneralUtility::callUserFunction($reference, $params, $this);
}
}
if ($respectEnableFields === false) {
$query->getQuerySettings()->setIgnoreEnableFields(true);
$constraints[] = $query->equals('deleted', 0);
}
if (!empty($constraints)) {
$query->matching(
$query->logicalAnd($constraints)
);
}
if ($orderings = $this->createOrderingsFromDemand($demand)) {
$query->setOrderings($orderings);
}
// @todo consider moving this to a separate function as well
if ($demand->getLimit() != null) {
$query->setLimit((int)$demand->getLimit());
}
// @todo consider moving this to a separate function as well
if ($demand->getOffset() != null) {
if (!$query->getLimit()) {
$query->setLimit(PHP_INT_MAX);
}
$query->setOffset((int)$demand->getOffset());
}
return $query;
}
/**
* Returns the total number objects of this repository matching the demand.
*
* @param DemandInterface $demand
* @return int
*/
public function countDemanded(DemandInterface $demand, $respectEnableFields = true, $disableLanguageOverlayMode = false): int
{
$query = $this->generateQuery($demand, $respectEnableFields, $disableLanguageOverlayMode);
if ($constraints = $this->createConstraintsFromDemand($query, $demand)) {
$query->matching(
$query->logicalAnd($constraints)
);
}
$result = $query->execute();
return $result->count();
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Get data used in the administration view
*/
class AdministrationRepository
{
public function getTotalCounts(): array
{
$count = [];
$queryBuilder = $this->getQueryBuilder('tx_news_domain_model_news');
$queryBuilder->getRestrictions()->removeAll();
$count['tx_news_domain_model_news'] = $queryBuilder
->count('*')
->from('tx_news_domain_model_news')
->execute()->fetchColumn(0);
$queryBuilder = $this->getQueryBuilder('sys_category_record_mm');
$count['category_relations'] = $queryBuilder
->count('*')
->from('sys_category_record_mm')
->where($queryBuilder->expr()->like(
'tablenames',
$queryBuilder->createNamedParameter('tx_news_domain_model_news', \PDO::PARAM_STR)
))
->execute()->fetchColumn(0);
if ($count['tx_news_domain_model_news'] > 0 && $count['category_relations']) {
$count['_both'] = true;
}
return $count;
}
private function getConnection(string $table): Connection
{
return GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
}
private function getQueryBuilder(string $table): QueryBuilder
{
return $this->getConnection($table)->createQueryBuilder();
}
}

View File

@@ -0,0 +1,252 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use Doctrine\DBAL\Connection;
use GeorgRinger\News\Domain\Model\Category;
use GeorgRinger\News\Domain\Model\DemandInterface;
use GeorgRinger\News\Service\CategoryService;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
/**
* Category repository with all callable functionality
*/
class CategoryRepository extends AbstractDemandedRepository
{
protected function createConstraintsFromDemand(QueryInterface $query, DemandInterface $demand): array
{
return [];
}
protected function createOrderingsFromDemand(DemandInterface $demand): array
{
return [];
}
/**
* Find category by import source and import id
*
* @param string $importSource import source
* @param int $importId import id
* @param bool $asArray return result as array
* @return Category|array
*/
public function findOneByImportSourceAndImportId($importSource, $importId, $asArray = false)
{
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
$query->getQuerySettings()->setIgnoreEnableFields(true);
$result = $query->matching(
$query->logicalAnd(
$query->equals('importSource', $importSource),
$query->equals('importId', $importId)
)
)->execute($asArray);
if ($asArray) {
if (isset($result[0])) {
return $result[0];
}
return [];
}
return $result->getFirst();
}
/**
* Find categories by a given pid
*
* @param int $pid pid
*
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array
*/
public function findParentCategoriesByPid($pid)
{
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
return $query->matching(
$query->logicalAnd(
$query->equals('pid', (int)$pid),
$query->equals('parentcategory', 0)
)
)->execute();
}
/**
* Find category tree
*
* @param array $rootIdList list of id s
* @return QueryInterface|array
*/
public function findTree(array $rootIdList, $startingPoint = null)
{
$subCategories = CategoryService::getChildrenCategories(implode(',', $rootIdList));
$idList = explode(',', $subCategories);
if (empty($idList)) {
return [];
}
$ordering = ['sorting' => QueryInterface::ORDER_ASCENDING];
$categories = $this->findByIdList($idList, $ordering, $startingPoint);
$flatCategories = [];
/** @var Category $category */
foreach ($categories as $category) {
$flatCategories[$category->getUid()] = [
'item' => $category,
'parent' => ($category->getParentcategory()) ? $category->getParentcategory()->getUid() : null
];
}
$tree = [];
// If leaves are selected without its parents selected, those are shown as parent
foreach ($flatCategories as $id => &$flatCategory) {
if (!isset($flatCategories[$flatCategory['parent']])) {
$flatCategory['parent'] = null;
}
}
foreach ($flatCategories as $id => &$node) {
if ($node['parent'] === null) {
$tree[$id] = &$node;
} else {
$flatCategories[$node['parent']]['children'][$id] = &$node;
}
}
return $tree;
}
/**
* Find categories by a given pid
*
* @param array $idList list of id s
* @param array $ordering ordering
*
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array
*/
public function findByIdList(array $idList, array $ordering = [], $startingPoint = null)
{
if (empty($idList)) {
throw new \InvalidArgumentException('The given id list is empty.', 1484823597);
}
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
$query->getQuerySettings()->setRespectSysLanguage(true);
if (count($ordering) > 0) {
$query->setOrderings($ordering);
}
$this->overlayTranslatedCategoryIds($idList);
$conditions = [];
$conditions[] = $query->in('uid', $idList);
if (is_null($startingPoint) === false) {
$conditions[] = $query->in('pid', GeneralUtility::trimExplode(',', $startingPoint, true));
}
return $query->matching(
$query->logicalAnd(
$conditions
)
)->execute();
}
/**
* Find categories by a given parent
*
* @param int $parent parent
*
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array
*/
public function findChildren($parent)
{
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
return $query->matching(
$query->logicalAnd(
$query->equals('parentcategory', (int)$parent)
)
)->execute();
}
/**
* Overlay the category ids with the ones from current language
*
* @param array $idList
* return void
*
* @return void
*/
protected function overlayTranslatedCategoryIds(array &$idList): void
{
$language = $this->getSysLanguageUid();
if ($language > 0 && !empty($idList)) {
if (isset($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE'])) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('sys_category');
$rows = $queryBuilder
->select('l10n_parent', 'uid', 'sys_language_uid')
->from('sys_category')
->where(
$queryBuilder->expr()->eq('sys_language_uid', $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT)),
$queryBuilder->expr()->in('l10n_parent', $queryBuilder->createNamedParameter($idList, Connection::PARAM_INT_ARRAY))
)
->execute()->fetchAll();
$idList = $this->replaceCategoryIds($idList, $rows);
}
// @todo currently only implemented for the frontend
}
}
/**
* Get the current sys language uid
*
* @return int
*/
protected function getSysLanguageUid(): int
{
$sysLanguage = 0;
if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() === 10) {
$sysLanguage = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'id');
} elseif (isset($GLOBALS['TSFE']) && is_object($GLOBALS['TSFE'])) {
$sysLanguage = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'contentId');
} elseif ((int)GeneralUtility::_GP('L')) {
$sysLanguage = (int)GeneralUtility::_GP('L');
}
return $sysLanguage;
}
/**
* Replace ids in array by the given ones
*
* @param array $idList
* @param array $rows
* @return array
*/
protected function replaceCategoryIds(array $idList, array $rows): array
{
foreach ($rows as $row) {
$pos = array_search($row['l10n_parent'], $idList);
if ($pos !== false) {
$idList[$pos] = (int)$row['uid'];
}
}
return $idList;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use GeorgRinger\News\Domain\Model\DemandInterface;
/**
* Demand domain model interface
*/
interface DemandedRepositoryInterface
{
public function findDemanded(DemandInterface $demand, $respectEnableFields = true);
public function countDemanded(DemandInterface $demand);
}

View File

@@ -0,0 +1,18 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Repository for file objects
*/
class FileRepository extends Repository
{
}

View File

@@ -0,0 +1,18 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Repository for link objects
*/
class LinkRepository extends Repository
{
}

View File

@@ -0,0 +1,18 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Repository for media objects
*/
class MediaRepository extends Repository
{
}

View File

@@ -0,0 +1,17 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* News repository with all the callable functionality
*/
class NewsDefaultRepository extends NewsRepository
{
}

View File

@@ -0,0 +1,519 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use GeorgRinger\News\Domain\Model\DemandInterface;
use GeorgRinger\News\Domain\Model\Dto\NewsDemand;
use GeorgRinger\News\Service\CategoryService;
use GeorgRinger\News\Utility\ConstraintHelper;
use GeorgRinger\News\Utility\Validation;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
/**
* News repository with all the callable functionality
*/
class NewsRepository extends AbstractDemandedRepository
{
/**
* Returns a category constraint created by
* a given list of categories and a junction string
*
* @param QueryInterface $query
* @param array $categories
* @param string $conjunction
* @param bool $includeSubCategories
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface|null
*/
protected function createCategoryConstraint(
QueryInterface $query,
$categories,
$conjunction,
$includeSubCategories = false
): ?\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface {
$constraint = null;
$categoryConstraints = [];
// If "ignore category selection" is used, nothing needs to be done
if (empty($conjunction)) {
return $constraint;
}
if (!is_array($categories)) {
$categories = GeneralUtility::intExplode(',', $categories, true);
}
foreach ($categories as $category) {
if ($includeSubCategories) {
$subCategories = GeneralUtility::trimExplode(
',',
CategoryService::getChildrenCategories($category, 0, '', true),
true
);
$subCategoryConstraint = [];
$subCategoryConstraint[] = $query->contains('categories', $category);
if (count($subCategories) > 0) {
foreach ($subCategories as $subCategory) {
$subCategoryConstraint[] = $query->contains('categories', $subCategory);
}
}
if ($subCategoryConstraint) {
$categoryConstraints[] = $query->logicalOr($subCategoryConstraint);
}
} else {
$categoryConstraints[] = $query->contains('categories', $category);
}
}
if ($categoryConstraints) {
switch (strtolower($conjunction)) {
case 'or':
$constraint = $query->logicalOr($categoryConstraints);
break;
case 'notor':
$constraint = $query->logicalNot($query->logicalOr($categoryConstraints));
break;
case 'notand':
$constraint = $query->logicalNot($query->logicalAnd($categoryConstraints));
break;
case 'and':
default:
$constraint = $query->logicalAnd($categoryConstraints);
}
}
return $constraint;
}
/**
* Returns an array of constraints created from a given demand object.
*
* @param QueryInterface $query
* @param DemandInterface $demand
*
* @throws \UnexpectedValueException
* @throws \InvalidArgumentException
* @throws \Exception
*
* @return (\TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface|null)[]
*
* @psalm-return array<string, \TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface|\TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface|null>
*/
protected function createConstraintsFromDemand(QueryInterface $query, DemandInterface $demand): array
{
/** @var NewsDemand $demand */
$constraints = [];
if ($demand->getCategories() && $demand->getCategories() !== '0') {
$constraints['categories'] = $this->createCategoryConstraint(
$query,
$demand->getCategories(),
$demand->getCategoryConjunction(),
$demand->getIncludeSubCategories()
);
}
if ($demand->getAuthor()) {
$constraints['author'] = $query->equals('author', $demand->getAuthor());
}
if ($demand->getTypes()) {
$constraints['types'] = $query->in('type', $demand->getTypes());
}
// archived
if ($demand->getArchiveRestriction() === 'archived') {
$constraints['archived'] = $query->logicalAnd(
$query->lessThan('archive', $GLOBALS['SIM_EXEC_TIME']),
$query->greaterThan('archive', 0)
);
} elseif ($demand->getArchiveRestriction() === 'active') {
$constraints['active'] = $query->logicalOr(
$query->greaterThanOrEqual('archive', $GLOBALS['SIM_EXEC_TIME']),
$query->equals('archive', 0)
);
}
// Time restriction greater than or equal
$timeRestrictionField = $demand->getDateField();
$timeRestrictionField = (empty($timeRestrictionField)) ? 'datetime' : $timeRestrictionField;
if ($demand->getTimeRestriction()) {
$timeLimit = ConstraintHelper::getTimeRestrictionLow($demand->getTimeRestriction());
$constraints['timeRestrictionGreater'] = $query->greaterThanOrEqual(
$timeRestrictionField,
$timeLimit
);
}
// Time restriction less than or equal
if ($demand->getTimeRestrictionHigh()) {
$timeLimit = ConstraintHelper::getTimeRestrictionHigh($demand->getTimeRestrictionHigh());
$constraints['timeRestrictionLess'] = $query->lessThanOrEqual(
$timeRestrictionField,
$timeLimit
);
}
// top news
if ($demand->getTopNewsRestriction() == 1) {
$constraints['topNews1'] = $query->equals('istopnews', 1);
} elseif ($demand->getTopNewsRestriction() == 2) {
$constraints['topNews2'] = $query->equals('istopnews', 0);
}
// storage page
if ($demand->getStoragePage()) {
$pidList = GeneralUtility::intExplode(',', $demand->getStoragePage(), true);
$constraints['pid'] = $query->in('pid', $pidList);
}
// month & year OR year only
if ($demand->getYear() > 0) {
if (null === $demand->getDateField()) {
throw new \InvalidArgumentException('No Datefield is set, therefore no Datemenu is possible!');
}
if ($demand->getMonth() > 0) {
if ($demand->getDay() > 0) {
$begin = mktime(0, 0, 0, $demand->getMonth(), $demand->getDay(), $demand->getYear());
$end = mktime(23, 59, 59, $demand->getMonth(), $demand->getDay(), $demand->getYear());
} else {
$begin = mktime(0, 0, 0, $demand->getMonth(), 1, $demand->getYear());
$end = mktime(23, 59, 59, ($demand->getMonth() + 1), 0, $demand->getYear());
}
} else {
$begin = mktime(0, 0, 0, 1, 1, $demand->getYear());
$end = mktime(23, 59, 59, 12, 31, $demand->getYear());
}
$constraints['datetime'] = $query->logicalAnd([
$query->greaterThanOrEqual($demand->getDateField(), $begin),
$query->lessThanOrEqual($demand->getDateField(), $end)
]);
}
// Tags
$tags = $demand->getTags();
if ($tags && is_string($tags)) {
$tagList = explode(',', $tags);
$subConstraints = [];
foreach ($tagList as $singleTag) {
$subConstraints[] = $query->contains('tags', $singleTag);
}
if (count($subConstraints) > 0) {
$constraints['tags'] = $query->logicalOr($subConstraints);
}
}
// Search
$searchConstraints = $this->getSearchConstraints($query, $demand);
if (!empty($searchConstraints)) {
$constraints['search'] = $query->logicalAnd($searchConstraints);
}
// Exclude already displayed
if ($demand->getExcludeAlreadyDisplayedNews() && isset($GLOBALS['EXT']['news']['alreadyDisplayed']) && !empty($GLOBALS['EXT']['news']['alreadyDisplayed'])) {
$constraints['excludeAlreadyDisplayedNews'] = $query->logicalNot(
$query->in(
'uid',
$GLOBALS['EXT']['news']['alreadyDisplayed']
)
);
}
// Hide id list
$hideIdList = $demand->getHideIdList();
if ($hideIdList) {
$constraints['hideIdInList'] = $query->logicalNot(
$query->in(
'uid',
GeneralUtility::intExplode(',', $hideIdList, true)
)
);
}
// Id list
$idList = $demand->getIdList();
if ($idList) {
$constraints['idList'] = $query->in('uid', GeneralUtility::intExplode(',', $idList, true));
}
// Clean not used constraints
foreach ($constraints as $key => $value) {
if (null === $value) {
unset($constraints[$key]);
}
}
return $constraints;
}
/**
* Returns an array of orderings created from a given demand object.
*
* @param DemandInterface $demand
*
* @return string[]
*
* @psalm-return array<string, string>
*/
protected function createOrderingsFromDemand(DemandInterface $demand): array
{
$orderings = [];
if ($demand->getTopNewsFirst()) {
$orderings['istopnews'] = QueryInterface::ORDER_DESCENDING;
}
if (Validation::isValidOrdering($demand->getOrder(), $demand->getOrderByAllowed())) {
$orderList = GeneralUtility::trimExplode(',', $demand->getOrder(), true);
if (!empty($orderList)) {
// go through every order statement
foreach ($orderList as $orderItem) {
$orderSplit = GeneralUtility::trimExplode(' ', $orderItem, true);
$orderField = $orderSplit[0];
$ascDesc = $orderSplit[1] ?? '';
if ($ascDesc) {
$orderings[$orderField] = ((strtolower($ascDesc) === 'desc') ?
QueryInterface::ORDER_DESCENDING :
QueryInterface::ORDER_ASCENDING);
} else {
$orderings[$orderField] = QueryInterface::ORDER_ASCENDING;
}
}
}
}
return $orderings;
}
/**
* Find first news by import and source id
*
* @param string $importSource import source
* @param int $importId import id
* @param bool $asArray return result as array
* @return \GeorgRinger\News\Domain\Model\News|array
*/
public function findOneByImportSourceAndImportId($importSource, $importId, $asArray = false)
{
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
$query->getQuerySettings()->setRespectSysLanguage(false);
$query->getQuerySettings()->setIgnoreEnableFields(true);
$result = $query->matching(
$query->logicalAnd(
$query->equals('importSource', $importSource),
$query->equals('importId', $importId)
)
)->execute($asArray);
if ($asArray) {
if (isset($result[0])) {
return $result[0];
}
return [];
}
return $result->getFirst();
}
/**
* Override default findByUid function to enable also the option to turn of
* the enableField setting
*
* @param int $uid id of record
* @param bool $respectEnableFields if set to false, hidden records are shown
* @return \GeorgRinger\News\Domain\Model\News|null
*/
public function findByUid($uid, $respectEnableFields = true): ?\GeorgRinger\News\Domain\Model\News
{
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
$query->getQuerySettings()->setRespectSysLanguage(false);
if (!$respectEnableFields) {
$query->getQuerySettings()->setIgnoreEnableFields(true);
$query->getQuerySettings()->setLanguageOverlayMode(false);
}
return $query->matching(
$query->logicalAnd(
$query->equals('uid', $uid),
$query->equals('deleted', 0)
)
)->execute()->getFirst();
}
/**
* Get the count of news records by month/year and
* returns the result compiled as array
*
* @param DemandInterface $demand
* @return array
*/
public function countByDate(DemandInterface $demand): array
{
$data = [];
$sql = $this->findDemandedRaw($demand);
// strip unwanted order by
$sql = $this->stripOrderBy($sql);
// Get the month/year into the result
$field = $demand->getDateField();
$field = empty($field) ? 'datetime' : $field;
$sql = 'SELECT MONTH(FROM_UNIXTIME(0) + INTERVAL ' . $field . ' SECOND ) AS "_Month",' .
' YEAR(FROM_UNIXTIME(0) + INTERVAL ' . $field . ' SECOND) AS "_Year" ,' .
' count(MONTH(FROM_UNIXTIME(0) + INTERVAL ' . $field . ' SECOND )) as count_month,' .
' count(YEAR(FROM_UNIXTIME(0) + INTERVAL ' . $field . ' SECOND)) as count_year' .
' FROM tx_news_domain_model_news ' . substr($sql, strpos($sql, 'WHERE '));
$connection = GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable('tx_news_domain_model_news');
if (TYPO3_MODE === 'FE') {
$sql .= $GLOBALS['TSFE']->sys_page->enableFields('tx_news_domain_model_news');
} else {
$expressionBuilder = $connection
->createQueryBuilder()
->expr();
$sql .= BackendUtility::BEenableFields('tx_news_domain_model_news') .
' AND ' . $expressionBuilder->eq('deleted', 0);
}
// group by custom month/year fields
$orderDirection = strtolower($demand->getOrder());
if ($orderDirection !== 'desc' && $orderDirection !== 'asc') {
$orderDirection = 'asc';
}
$sql .= ' GROUP BY _Month, _Year ORDER BY _Year ' . $orderDirection . ', _Month ' . $orderDirection;
$res = $connection->query($sql);
while ($row = $res->fetch()) {
$month = strlen($row['_Month']) === 1 ? ('0' . $row['_Month']) : $row['_Month'];
$data['single'][$row['_Year']][$month] = $row['count_month'];
}
// Add totals
if (is_array($data['single'])) {
foreach ($data['single'] as $year => $months) {
$countOfYear = 0;
foreach ($months as $month) {
$countOfYear += $month;
}
$data['total'][$year] = $countOfYear;
}
}
return $data;
}
/**
* Get the search constraints
*
* @param QueryInterface $query
* @param DemandInterface $demand
* @return array
* @throws \UnexpectedValueException
*/
protected function getSearchConstraints(QueryInterface $query, DemandInterface $demand): array
{
$constraints = [];
if ($demand->getSearch() === null) {
return $constraints;
}
/* @var $searchObject \GeorgRinger\News\Domain\Model\Dto\Search */
$searchObject = $demand->getSearch();
$searchSubject = $searchObject->getSubject();
if (!empty($searchSubject)) {
$queryBuilder = $this->getQueryBuilder('tx_news_domain_model_news');
$searchFields = GeneralUtility::trimExplode(',', $searchObject->getFields(), true);
$searchConstraints = [];
if (count($searchFields) === 0) {
throw new \UnexpectedValueException('No search fields defined', 1318497755);
}
$searchSubjectSplitted = str_getcsv($searchSubject, ' ');
if ($searchObject->isSplitSubjectWords()) {
foreach ($searchFields as $field) {
$subConstraints = [];
foreach ($searchSubjectSplitted as $searchSubjectSplittedPart) {
$searchSubjectSplittedPart = trim($searchSubjectSplittedPart);
if ($searchSubjectSplittedPart) {
$subConstraints[] = $query->like($field, '%' . $searchSubjectSplittedPart . '%');
}
}
$searchConstraints[] = $query->logicalAnd($subConstraints);
}
if (count($searchConstraints)) {
$constraints[] = $query->logicalOr($searchConstraints);
}
} else {
if (!empty($searchSubject)) {
foreach ($searchFields as $field) {
$searchConstraints[] = $query->like($field, '%' . $searchSubject . '%');
}
}
if (count($searchConstraints)) {
$constraints[] = $query->logicalOr($searchConstraints);
}
}
}
$minimumDate = strtotime($searchObject->getMinimumDate());
if ($minimumDate) {
$field = $searchObject->getDateField();
if (empty($field)) {
throw new \UnexpectedValueException('No date field is defined', 1396348732);
}
$constraints[] = $query->greaterThanOrEqual($field, $minimumDate);
}
$maximumDate = strtotime($searchObject->getMaximumDate());
if ($maximumDate) {
$field = $searchObject->getDateField();
if (empty($field)) {
throw new \UnexpectedValueException('No date field is defined', 1396348733);
}
$maximumDate += 86400;
$constraints[] = $query->lessThanOrEqual($field, $maximumDate);
}
return $constraints;
}
/**
* @param string $table table name
* @return QueryBuilder
*/
protected function getQueryBuilder(string $table): QueryBuilder
{
return GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
}
/**
* Return stripped order sql
*
* @param string $str
* @return string
*/
private function stripOrderBy(string $str): string
{
/** @noinspection NotOptimalRegularExpressionsInspection */
return preg_replace('/(?:ORDER[[:space:]]*BY[[:space:]]*.*)+/i', '', trim($str));
}
}

View File

@@ -0,0 +1,125 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use GeorgRinger\News\Domain\Model\DemandInterface;
use GeorgRinger\News\Utility\Validation;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
/**
* Repository for tag objects
*/
class TagRepository extends AbstractDemandedRepository
{
/**
* Find categories by a given pid
*
* @param array $idList list of id s
* @param array $ordering ordering
* @param string $startingPoint starting point uid or comma separated list
*
* @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array
*/
public function findByIdList(array $idList, array $ordering = [], $startingPoint = null)
{
if (empty($idList)) {
throw new \InvalidArgumentException('The given id list is empty.', 1484823596);
}
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(false);
$query->getQuerySettings()->setRespectSysLanguage(false);
if (count($ordering) > 0) {
$query->setOrderings($ordering);
}
$conditions = [];
$conditions[] = $query->in('uid', $idList);
if ($startingPoint !== null) {
$conditions[] = $query->in('pid', GeneralUtility::trimExplode(',', $startingPoint, true));
}
return $query->matching(
$query->logicalAnd(
$conditions
)
)->execute();
}
/**
* Returns an array of constraints created from a given demand object.
*
* @param QueryInterface $query
* @param DemandInterface $demand
* @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface[]
*/
protected function createConstraintsFromDemand(QueryInterface $query, DemandInterface $demand): array
{
$constraints = [];
// Storage page
if ($demand->getStoragePage()) {
$pidList = GeneralUtility::intExplode(',', $demand->getStoragePage(), true);
$constraints[] = $query->in('pid', $pidList);
}
// Tags
if ($demand->getTags()) {
$tagList = GeneralUtility::intExplode(',', $demand->getTags(), true);
$constraints[] = $query->in('uid', $tagList);
}
// Clean not used constraints
foreach ($constraints as $key => $value) {
if (is_null($value)) {
unset($constraints[$key]);
}
}
return $constraints;
}
/**
* Returns an array of orderings created from a given demand object.
*
* @param DemandInterface $demand
*
* @return string[]
*
* @psalm-return array<string, string>
*/
protected function createOrderingsFromDemand(DemandInterface $demand): array
{
$orderings = [];
if (Validation::isValidOrdering($demand->getOrder(), $demand->getOrderByAllowed())) {
$orderList = GeneralUtility::trimExplode(',', $demand->getOrder(), true);
if (!empty($orderList)) {
// go through every order statement
foreach ($orderList as $orderItem) {
list($orderField, $ascDesc) = GeneralUtility::trimExplode(' ', $orderItem, true);
// count == 1 means that no direction is given
if ($ascDesc) {
$orderings[$orderField] = ((strtolower($ascDesc) == 'desc') ?
QueryInterface::ORDER_DESCENDING :
QueryInterface::ORDER_ASCENDING);
} else {
$orderings[$orderField] = QueryInterface::ORDER_ASCENDING;
}
}
}
}
return $orderings;
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace GeorgRinger\News\Domain\Repository;
use TYPO3\CMS\Extbase\Persistence\Repository;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
/**
* Repository for tt_content objects
*/
class TtContentRepository extends Repository
{
protected $objectType = '\GeorgRinger\News\Domain\Model\Ttcontent';
}

View File

@@ -0,0 +1,169 @@
<?php
namespace GeorgRinger\News\Domain\Service;
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
use GeorgRinger\News\Domain\Repository\CategoryRepository;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\Index\FileIndexRepository;
use TYPO3\CMS\Core\Resource\ProcessedFile;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Resource\ResourceStorage;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
class AbstractImportService implements LoggerAwareInterface
{
use LoggerAwareTrait;
const UPLOAD_PATH = 'uploads/tx_news/';
/**
* @var ObjectManager
*/
protected $objectManager;
/**
* @var PersistenceManager
*/
protected $persistenceManager;
/**
* @var array
*/
protected $postPersistQueue = [];
/**
* @var EmConfiguration
*/
protected $emSettings;
/**
* @var Folder
*/
protected $importFolder;
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* @var CategoryRepository
*/
protected $categoryRepository;
/**
* AbstractImportService constructor.
* @param PersistenceManager $persistenceManager
* @param EmConfiguration $emSettings
* @param ObjectManager $objectManager
* @param CategoryRepository $categoryRepository
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(
PersistenceManager $persistenceManager,
ObjectManager $objectManager,
CategoryRepository $categoryRepository,
EventDispatcherInterface $eventDispatcher
) {
$this->emSettings = GeneralUtility::makeInstance(EmConfiguration::class);
$this->persistenceManager = $persistenceManager;
$this->objectManager = $objectManager;
$this->categoryRepository = $categoryRepository;
$this->eventDispatcher = $eventDispatcher;
}
/**
* Compares 2 files by using its filesize
*
* @param string $file1 Absolute path and filename to file1
* @param string $file2 Absolute path and filename to file2
* @return bool
*/
protected function filesAreEqual($file1, $file2): bool
{
return filesize($file1) === filesize($file2);
}
/**
* Find a existing file by its hash
*
* @param string $hash
*
* @return File|ProcessedFile|null
*/
protected function findFileByHash($hash)
{
$file = null;
$files = $this->getFileIndexRepository()->findByContentHash($hash);
if (count($files)) {
foreach ($files as $fileInfo) {
if ($fileInfo['storage'] > 0) {
$file = $this->getResourceFactory()->getFileObjectByStorageAndIdentifier(
$fileInfo['storage'],
$fileInfo['identifier']
);
break;
}
}
}
return $file;
}
/**
* Get import Folder
*
* TODO: catch exception when storage/folder does not exist and return readable message to the user
*
* @return Folder
*/
protected function getImportFolder(): Folder
{
if ($this->importFolder === null) {
$this->importFolder = $this->getResourceFactory()->getFolderObjectFromCombinedIdentifier($this->emSettings->getStorageUidImporter() . ':' . $this->emSettings->getResourceFolderImporter());
}
return $this->importFolder;
}
/**
* Returns an instance of the FileIndexRepository
*
* @return FileIndexRepository
*/
protected function getFileIndexRepository(): FileIndexRepository
{
return FileIndexRepository::getInstance();
}
/**
* Get resource storage
*
* @return ResourceStorage
*/
protected function getResourceStorage(): ResourceStorage
{
return $this->getResourceFactory()->getStorageObject($this->emSettings->getStorageUidImporter());
}
/**
* @return ResourceFactory
*/
protected function getResourceFactory(): ResourceFactory
{
/** @var ResourceFactory $resourceFactory */
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
return $resourceFactory;
}
}

View File

@@ -0,0 +1,263 @@
<?php
namespace GeorgRinger\News\Domain\Service;
use GeorgRinger\News\Domain\Model\Category;
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
use GeorgRinger\News\Domain\Model\FileReference;
use GeorgRinger\News\Domain\Repository\CategoryRepository;
use GeorgRinger\News\Event\CategoryImportPostHydrateEvent;
use Psr\EventDispatcher\EventDispatcherInterface;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
class CategoryImportService extends AbstractImportService
{
const ACTION_SET_PARENT_CATEGORY = 1;
const ACTION_CREATE_L10N_CHILDREN_CATEGORY = 2;
/**
* CategoryImportService constructor.
* @param PersistenceManager $persistenceManager
* @param EmConfiguration $emSettings
* @param ObjectManager $objectManager
* @param CategoryRepository $categoryRepository
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(
PersistenceManager $persistenceManager,
ObjectManager $objectManager,
CategoryRepository $categoryRepository,
EventDispatcherInterface $eventDispatcher
) {
parent::__construct($persistenceManager, $objectManager, $categoryRepository, $eventDispatcher);
}
/**
* @param array $importArray
*
* @return void
*/
public function import(array $importArray): void
{
$this->logger->info(sprintf('Starting import for %s categories', count($importArray)));
// Sort import array to import the default language first
foreach ($importArray as $importItem) {
$category = $this->hydrateCategory($importItem);
if (!empty($importItem['title_lang_ol'])) {
$this->postPersistQueue[$importItem['import_id']] = [
'category' => $category,
'importItem' => $importItem,
'action' => self::ACTION_CREATE_L10N_CHILDREN_CATEGORY,
'titleLanguageOverlay' => $importItem['title_lang_ol']
];
}
if ($importItem['parentcategory']) {
$this->postPersistQueue[$importItem['import_id']] = [
'category' => $category,
'action' => self::ACTION_SET_PARENT_CATEGORY,
'parentCategoryOriginUid' => $importItem['parentcategory']
];
}
}
$this->persistenceManager->persistAll();
foreach ($this->postPersistQueue as $queueItem) {
switch ($queueItem['action']) {
case self::ACTION_SET_PARENT_CATEGORY:
$this->setParentCategory($queueItem);
break;
case self::ACTION_CREATE_L10N_CHILDREN_CATEGORY:
$this->createL10nChildrenCategory($queueItem);
break;
default:
// do nothing
break;
}
}
$this->persistenceManager->persistAll();
}
/**
* Hydrate a category record with the given array
*
* @param array $importItem
*
* @return array|object
*/
protected function hydrateCategory(array $importItem)
{
$category = $this->categoryRepository->findOneByImportSourceAndImportId(
$importItem['import_source'],
$importItem['import_id']
);
$this->logger->info(sprintf(
'Import of category from source "%s" with id "%s"',
$importItem['import_source'],
$importItem['import_id']
));
if (is_null($category)) {
$this->logger->info('Category is new');
$category = GeneralUtility::makeInstance(Category::class);
$this->categoryRepository->add($category);
} else {
$this->logger->info(sprintf('Category exists already with id "%s".', $category->getUid()));
}
$category->setPid($importItem['pid']);
$category->setHidden($importItem['hidden']);
$category->setStarttime($importItem['starttime']);
$category->setEndtime($importItem['endtime']);
$category->setCrdate($importItem['crdate']);
$category->setTstamp($importItem['tstamp']);
$category->setTitle($importItem['title']);
$category->setDescription($importItem['description']);
if (!empty($importItem['image'])) {
$this->setFileRelationFromImage($category, $importItem['image']);
}
$category->setShortcut($importItem['shortcut']);
$category->setSinglePid($importItem['single_pid']);
$category->setImportId($importItem['import_id']);
$category->setImportSource($importItem['import_source']);
$event = $this->eventDispatcher->dispatch(new CategoryImportPostHydrateEvent($this, $importItem, $category));
return $event->getCategory();
}
/**
* Add category image when not already present
*
* @param Category $category
* @param $image
*
* @return void
*/
protected function setFileRelationFromImage($category, $image)
{
// get fileObject by given identifier (file UID, combined identifier or path/filename)
try {
$newImage = $this->getResourceFactory()->retrieveFileOrFolderObject($image);
} catch (ResourceDoesNotExistException $exception) {
$newImage = false;
}
// only proceed if image is found
if (!$newImage instanceof File) {
return;
}
// new image found check if this isn't already
$existingImages = $category->getImages();
if (!is_null($existingImages) && $existingImages->count() !== 0) {
/** @var $item FileReference */
foreach ($existingImages as $item) {
// only check already persisted items
if ($item->getFileUid() === (int)$newImage->getUid()
||
($item->getUid() &&
$item->getOriginalResource()->getName() === $newImage->getName() &&
$item->getOriginalResource()->getSize() === (int)$newImage->getSize())
) {
$newImage = false;
break;
}
}
}
if ($newImage) {
// file not inside a storage then search for existing file or copy the one form storage 0 to the import folder
if ($newImage->getStorage()->getUid() === 0) {
// search DB for same file based on hash (to prevent duplicates)
$existingFile = $this->findFileByHash($newImage->getSha1());
// no exciting file then copy file to import folder
if ($existingFile === null) {
$newImage = $this->getResourceStorage()->copyFile($newImage, $this->getImportFolder());
} else {
$newImage = $existingFile;
}
}
$fileReference = GeneralUtility::makeInstance(FileReference::class);
$fileReference->setFileUid($newImage->getUid());
$fileReference->setPid($category->getPid());
$category->addImage($fileReference);
}
}
/**
* Set parent category
*
* @param array $queueItem
*
* @return void
*/
protected function setParentCategory(array $queueItem): void
{
/** @var $category Category */
$category = $queueItem['category'];
$parentCategoryOriginUid = $queueItem['parentCategoryOriginUid'];
if (is_null($parentCategory = $this->postPersistQueue[$parentCategoryOriginUid]['category'])) {
$parentCategory = $this->categoryRepository->findOneByImportSourceAndImportId(
$category->getImportSource(),
$parentCategoryOriginUid
);
}
if ($parentCategory !== null) {
$category->setParentcategory($parentCategory);
$this->categoryRepository->update($category);
}
}
/**
* Create l10n relation
*
* @param array $queueItem
*
* @return void
*/
protected function createL10nChildrenCategory(array $queueItem): void
{
/** @var $category Category */
$category = $queueItem['category'];
$titleLanguageOverlay = GeneralUtility::trimExplode('|', $queueItem['titleLanguageOverlay']);
foreach ($titleLanguageOverlay as $key => $title) {
$sysLanguageUid = $key + 1;
$importItem = $queueItem['importItem'];
$importItem['import_id'] = $importItem['import_id'] . '|L:' . $sysLanguageUid;
/** @var $l10nChildrenCategory Category */
$l10nChildrenCategory = $this->hydrateCategory($importItem);
$this->categoryRepository->add($l10nChildrenCategory);
$l10nChildrenCategory->setTitle($title);
$l10nChildrenCategory->setL10nParent((int)$category->getUid());
$l10nChildrenCategory->setSysLanguageUid((int)$sysLanguageUid);
}
}
}

View File

@@ -0,0 +1,411 @@
<?php
namespace GeorgRinger\News\Domain\Service;
use GeorgRinger\News\Domain\Model\Dto\EmConfiguration;
use GeorgRinger\News\Domain\Model\FileReference;
use GeorgRinger\News\Domain\Model\Link;
use GeorgRinger\News\Domain\Model\News;
use GeorgRinger\News\Domain\Repository\CategoryRepository;
use GeorgRinger\News\Domain\Repository\NewsRepository;
use GeorgRinger\News\Domain\Repository\TtContentRepository;
use GeorgRinger\News\Event\NewsImportPostHydrateEvent;
use GeorgRinger\News\Event\NewsImportPreHydrateEvent;
use Psr\EventDispatcher\EventDispatcherInterface;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/**
* This file is part of the "news" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
class NewsImportService extends AbstractImportService
{
const ACTION_IMPORT_L10N_OVERLAY = 1;
/**
* @var NewsRepository
*/
protected $newsRepository;
/**
* @var TtContentRepository
*/
protected $ttContentRepository;
/**
* @var array
*/
protected $settings = [];
/**
* NewsImportService constructor.
* @param PersistenceManager $persistenceManager
* @param EmConfiguration $emSettings
* @param ObjectManager $objectManager
* @param CategoryRepository $categoryRepository
* @param EventDispatcherInterface $eventDispatcher
* @param NewsRepository $newsRepository
* @param TtContentRepository $ttContentRepository
*/
public function __construct(
PersistenceManager $persistenceManager,
ObjectManager $objectManager,
CategoryRepository $categoryRepository,
EventDispatcherInterface $eventDispatcher,
NewsRepository $newsRepository,
TtContentRepository $ttContentRepository
) {
parent::__construct($persistenceManager, $objectManager, $categoryRepository, $eventDispatcher);
$this->newsRepository = $newsRepository;
$this->ttContentRepository = $ttContentRepository;
}
/**
* @param array $importItem
*
* @return array|object
*/
protected function initializeNewsRecord(array $importItem)
{
$news = null;
$this->logger->info(sprintf(
'Import of news from source "%s" with id "%s"',
$importItem['import_source'],
$importItem['import_id']
));
if ($importItem['import_source'] && $importItem['import_id']) {
$news = $this->newsRepository->findOneByImportSourceAndImportId(
$importItem['import_source'],
$importItem['import_id']
);
}
if ($news === null) {
$news = GeneralUtility::makeInstance(News::class);
$this->newsRepository->add($news);
} else {
$this->logger->info(sprintf('News exists already with id "%s".', $news->getUid()));
$this->newsRepository->update($news);
}
return $news;
}
/**
* @param News $news
* @param array $importItem
* @param array $importItemOverwrite
* @return News
*/
protected function hydrateNewsRecord(
News $news,
array $importItem,
array $importItemOverwrite
): News {
if (!empty($importItemOverwrite)) {
$importItem = array_merge($importItem, $importItemOverwrite);
}
$news->setPid($importItem['pid']);
$news->setHidden($importItem['hidden']);
if ($importItem['starttime']) {
$news->setStarttime($importItem['starttime']);
}
if ($importItem['endtime']) {
$news->setStarttime($importItem['endtime']);
}
if (!empty($importItem['fe_group'])) {
$news->setFeGroup((string)$importItem['fe_group']);
}
$news->setTstamp($importItem['tstamp']);
$news->setCrdate($importItem['crdate']);
$news->setSysLanguageUid($importItem['sys_language_uid']);
$news->setSorting((int)$importItem['sorting']);
$news->setTitle($importItem['title']);
$news->setTeaser($importItem['teaser']);
$news->setBodytext($importItem['bodytext']);
$news->setType((string)$importItem['type']);
$news->setKeywords($importItem['keywords']);
$news->setDescription($importItem['description']);
$news->setDatetime(new \DateTime(date('Y-m-d H:i:sP', $importItem['datetime'])));
$news->setArchive(new \DateTime(date('Y-m-d H:i:sP', $importItem['archive'])));
$contentElementUidArray = GeneralUtility::trimExplode(',', $importItem['content_elements'], true);
foreach ($contentElementUidArray as $contentElementUid) {
if (is_object($contentElement = $this->ttContentRepository->findByUid($contentElementUid))) {
$news->addContentElement($contentElement);
}
}
$news->setInternalurl($importItem['internalurl']);
$news->setExternalurl($importItem['externalurl']);
$news->setAuthor($importItem['author']);
$news->setAuthorEmail($importItem['author_email']);
$news->setImportId($importItem['import_id']);
$news->setImportSource($importItem['import_source']);
$news->setPathSegment($importItem['path_segment']);
if (is_array($importItem['categories'])) {
foreach ($importItem['categories'] as $categoryUid) {
if ($this->settings['findCategoriesByImportSource']) {
$category = $this->categoryRepository->findOneByImportSourceAndImportId(
$this->settings['findCategoriesByImportSource'],
$categoryUid
);
} else {
$category = $this->categoryRepository->findByUid($categoryUid);
}
if ($category) {
$news->addCategory($category);
} else {
$this->logger->warning(sprintf('Category with ID "%s" was not found', $categoryUid));
}
}
}
// media relation
if (is_array($importItem['media'])) {
foreach ($importItem['media'] as $mediaItem) {
// get fileobject by given identifier (file UID, combined identifier or path/filename)
try {
$file = $this->getResourceFactory()->retrieveFileOrFolderObject($mediaItem['image']);
} catch (ResourceDoesNotExistException $exception) {
$file = null;
}
// no file found skip processing of this item
if ($file === null) {
continue;
}
// file not inside a storage then search for same file based on hash (to prevent duplicates)
if ($file->getStorage()->getUid() === 0) {
$existingFile = $this->findFileByHash($file->getSha1());
if ($existingFile !== null) {
$file = $existingFile;
}
}
/** @var $media FileReference */
if (!$media = $this->getIfFalRelationIfAlreadyExists($news->getFalMedia(), $file)) {
// file not inside a storage copy the one form storage 0 to the import folder
if ($file->getStorage()->getUid() === 0) {
$file = $this->getResourceStorage()->copyFile($file, $this->getImportFolder());
}
$media = GeneralUtility::makeInstance(FileReference::class);
$media->setFileUid($file->getUid());
$news->addFalMedia($media);
}
if ($media) {
$media->setTitle($mediaItem['title']);
$media->setAlternative($mediaItem['alt']);
$media->setDescription($mediaItem['caption']);
$media->setShowinpreview($mediaItem['showinpreview']);
$media->setPid($importItem['pid']);
}
}
}
// related files
if (is_array($importItem['related_files'])) {
foreach ($importItem['related_files'] as $fileItem) {
// get fileObject by given identifier (file UID, combined identifier or path/filename)
try {
$file = $this->getResourceFactory()->retrieveFileOrFolderObject($fileItem['file']);
} catch (ResourceDoesNotExistException $exception) {
$file = null;
}
// no file found skip processing of this item
if ($file === null) {
continue;
}
// file not inside a storage then search for same file based on hash (to prevent duplicates)
if ($file->getStorage()->getUid() === 0) {
$existingFile = $this->findFileByHash($file->getSha1());
if ($existingFile !== null) {
$file = $existingFile;
}
}
/** @var $relatedFile FileReference */
if (!$relatedFile = $this->getIfFalRelationIfAlreadyExists($news->getFalRelatedFiles(), $file)) {
// file not inside a storage copy the one form storage 0 to the import folder
if ($file->getStorage()->getUid() === 0) {
$file = $this->getResourceStorage()->copyFile($file, $this->getImportFolder());
}
$relatedFile = GeneralUtility::makeInstance(FileReference::class);
$relatedFile->setFileUid($file->getUid());
$news->addFalRelatedFile($relatedFile);
}
if ($relatedFile) {
$relatedFile->setTitle($fileItem['title']);
$relatedFile->setDescription($fileItem['description']);
$relatedFile->setPid($importItem['pid']);
}
}
}
if (is_array($importItem['related_links'])) {
foreach ($importItem['related_links'] as $link) {
/** @var $relatedLink Link */
if (($relatedLink = $this->getRelatedLinkIfAlreadyExists($news, $link['uri'])) === false) {
$relatedLink = GeneralUtility::makeInstance(Link::class);
$relatedLink->setUri($link['uri']);
$news->addRelatedLink($relatedLink);
}
$relatedLink->setTitle($link['title']);
$relatedLink->setDescription($link['description']);
$relatedLink->setPid($importItem['pid']);
}
}
$event = $this->eventDispatcher->dispatch(new NewsImportPostHydrateEvent($this, $importItem, $news));
return $event->getNews();
}
/**
* Import
*
* @param array $importData
* @param array $importItemOverwrite
* @param array $settings
*
* @return void
*/
public function import(array $importData, array $importItemOverwrite = [], $settings = []): void
{
$this->settings = $settings;
$this->logger->info(sprintf('Starting import for %s news', count($importData)));
foreach ($importData as $importItem) {
$event = $this->eventDispatcher->dispatch(new NewsImportPreHydrateEvent($this, $importItem));
$importItem = $event->getImportItem();
// Store language overlay in post persist queue
if ((int)$importItem['sys_language_uid'] > 0 && (string)$importItem['l10n_parent'] !== '0') {
$this->postPersistQueue[$importItem['import_id']] = [
'action' => self::ACTION_IMPORT_L10N_OVERLAY,
'category' => null,
'importItem' => $importItem
];
continue;
}
$news = $this->initializeNewsRecord($importItem);
$this->hydrateNewsRecord($news, $importItem, $importItemOverwrite);
}
$this->persistenceManager->persistAll();
foreach ($this->postPersistQueue as $queueItem) {
if ($queueItem['action'] == self::ACTION_IMPORT_L10N_OVERLAY) {
$this->importL10nOverlay($queueItem, $importItemOverwrite);
}
}
$this->persistenceManager->persistAll();
}
/**
* @param array $queueItem
* @param array $importItemOverwrite
*
* @return void
*/
protected function importL10nOverlay(array $queueItem, array $importItemOverwrite): void
{
$importItem = $queueItem['importItem'];
$parentNews = $this->newsRepository->findOneByImportSourceAndImportId(
$importItem['import_source'],
$importItem['l10n_parent'],
true
);
if (!empty($parentNews)) {
$news = $this->initializeNewsRecord($importItem);
$this->hydrateNewsRecord($news, $importItem, $importItemOverwrite);
$news->setSysLanguageUid($importItem['sys_language_uid']);
$news->setL10nParent($parentNews['uid']);
}
}
/**
* Get an existing items from the references that matches the file
*
* @param ObjectStorage $items
* @param \TYPO3\CMS\Core\Resource\File $file
*
* @return bool|FileReference
*/
protected function getIfFalRelationIfAlreadyExists(
ObjectStorage $items,
File $file
) {
$result = false;
if ($items->count() !== 0) {
/** @var $item FileReference */
foreach ($items as $item) {
// only check already persisted items
if ($item->getFileUid() === (int)$file->getUid()
||
($item->getUid() &&
$item->getOriginalResource()->getName() === $file->getName() &&
$item->getOriginalResource()->getSize() === (int)$file->getSize())
) {
$result = $item;
break;
}
}
}
return $result;
}
/**
* Get an existing related link object
*
* @param News $news
* @param string $uri
* @return bool|Link
*/
protected function getRelatedLinkIfAlreadyExists(News $news, $uri)
{
$result = false;
$links = $news->getRelatedLinks();
if (!empty($links) && $links->count() !== 0) {
foreach ($links as $link) {
if ($link->getUri() === $uri) {
$result = $link;
break;
}
}
}
return $result;
}
}