Commit 77e831c2 authored by Stefan Busemann's avatar Stefan Busemann
Browse files

Merge branch '521-enable-multiple-news-authors' into 'develop'

Resolve "Enable multiple News authors"

Closes #521

See merge request t3o/typo3.org!400
parents 5b520247 6b4c73d5
Pipeline #9835 passed with stages
in 5 minutes and 45 seconds
......@@ -52,12 +52,12 @@ class News extends \GeorgRinger\News\Domain\Model\News
}
/**
* @return \TYPO3\CMS\Extbase\Domain\Model\FrontendUser|null
* @return \TYPO3\CMS\Extbase\Domain\Model\FrontendUser[]
*/
public function getAuthorFeUser()
{
if (!$this->authorFeUser) {
return null;
return [];
}
$objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
......@@ -66,8 +66,15 @@ class News extends \GeorgRinger\News\Domain\Model\News
$userRepository = $objectManager->get(\TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository::class);
$userRepository->setDefaultQuerySettings($querySettings);
$authorFeUsers = [];
foreach (explode(',', $this->authorFeUser) as $username) {
$user = $userRepository->findOneByUsername($username);
if ($user !== null) {
$authorFeUsers[] = $user;
}
}
return $userRepository->findOneByUsername($this->authorFeUser);
return $authorFeUsers;
}
/**
......
......@@ -6,7 +6,7 @@ $GLOBALS['TCA']['tx_news_domain_model_news']['columns']['author_fe_user'] = [
'config' => [
'type' => 'select',
'minitems' => 0,
'maxitems' => 1,
'maxitems' => 99,
'renderType' => 'selectMultipleSideBySide',
'enableMultiSelectFilterTextfield' => true,
'itemsProcFunc' => \T3o\T3orgLayout\TCA\TcaProcFunc::class . '->feUserItems',
......
......@@ -4,6 +4,7 @@ plugin.tx_news {
templateRootPaths {
0 = EXT:news/Resources/Private/Templates/
1 = EXT:t3olayout/Resources/Private/Templates/News/
2 = EXT:t3org_layout/Resources/Private/Templates/News/
}
partialRootPaths >
......
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:n="http://typo3.org/ns/GeorgRinger/News/ViewHelpers"
xmlns:t3o="http://typo3.org/ns/T3o/T3olayout/ViewHelpers" data-namespace-typo3-fluid="true">
<!--
=====================
Partials/List/Item.html
-->
<f:variable name="breakpoints" value="{
0:{media:'max-width', size:375, maxWidth:375, cropVariant:'mobilenews'},
1:{media:'max-width', size:480, maxWidth:480, cropVariant:'mobilenews'},
2:{media:'max-width', size:767, maxWidth:510, cropVariant:'tabletnews'},
3:{media:'max-width', size:991, maxWidth:300, cropVariant:'tabletnews'},
4:{media:'max-width', size:1199, maxWidth:360, cropVariant:'desktopnews'},
5:{media:'min-width', size:1200, maxWidth:360, cropVariant:'desktopnews'}
}" />
<div class="article articletype-{newsItem.type}{f:if(condition: newsItem.istopnews, then: ' topnews')} news-list__item py-3"
data-category="{newsItem.firstCategory.title}"
data-datetime="{f:format.date(date:newsItem.datetime, format:'Y-m-d')}"
itemscope="itemscope"
itemtype="http://schema.org/Article">
<n:excludeDisplayedNews newsItem="{newsItem}" />
<div class="media">
<!-- media preview element -->
<f:if condition="{newsItem.mediaPreviews}">
<f:then>
<n:link newsItem="{newsItem}" settings="{settings}" title="{newsItem.title}" class="news-media__preview">
<f:alias map="{mediaElement: '{newsItem.mediaPreviews.0}'}">
<f:if condition="{mediaElement.originalResource.type} == 2">
<f:render partial="ContentElements/Media/Rendering/PictureSrcsetCols" section="ImageRender" arguments="{file: mediaElement, breakpoints: breakpoints}" />
</f:if>
<f:if condition="{mediaElement.originalResource.type} == 4">
<f:render partial="Detail/MediaVideo" arguments="{mediaElement: mediaElement}" />
</f:if>
<f:if condition="{mediaElement.originalResource.type} == 5">
<f:image image="{mediaElement}"
title="{mediaElement.originalResource.title}"
alt="{mediaElement.originalResource.alternative}"
width="{settings.list.media.image.width}"
height="{settings.list.media.image.height}"
class="img-fluid" />
</f:if>
</f:alias>
</n:link>
</f:then>
<f:else>
<f:if condition="{settings.displayDummyIfNoMedia}">
<n:link newsItem="{newsItem}" settings="{settings}" title="{newsItem.title}" class="news-media__no-image">
<f:if condition="{newsItem.defaultImage}">
<f:then>
<f:variable name="imageSrc" value="typo3conf/ext/t3org_layout/Resources/Public/Images/NewsDefaultImages/{newsItem.defaultImage}" />
<f:image src="{imageSrc}" title="" alt="" width="540" height="360c" class="img-fluid" />
</f:then>
<f:else>
<f:image src="{settings.list.media.dummyImage}" title="" alt="" width="540" height="360c" class="img-fluid" />
</f:else>
</f:if>
</n:link>
</f:if>
</f:else>
</f:if>
<div class="media-body">
<!-- date -->
<span class="news-list__item-date small">
<time datetime="{f:format.date(date:newsItem.datetime, format:'Y-m-d')}">
<f:format.date format="D. jS F, Y">{newsItem.datetime}</f:format.date>
<meta itemprop="datePublished" content="{f:format.date(date:newsItem.datetime, format:'Y-m-d')}" />
</time>
</span>
<h2 class="mt-md-3 mb-0">
<n:link newsItem="{newsItem}" settings="{settings}" title="{newsItem.title}">{newsItem.title}</n:link>
</h2>
<!-- Categories -->
<f:if condition="{newsItem.categories -> f:count()} > 0">
<div class="news-list__item-categories d-block small">
<span>
<f:translate key="categories" />
:
</span>
<f:for each="{newsItem.categories}" as="category" iteration="iterator">
<span>{category.title}
<f:if condition="{iterator.isLast} == false">,</f:if>
</span>
</f:for>
</div>
</f:if>
<!-- Tags -->
<f:if condition="{newsItem.tags -> f:count()} > 0">
<div class="news-list__item-tags d-block small">
<f:for each="{newsItem.tags}" as="tag" iteration="iterator">
<span><i class="fas fa-tag mr-1" aria-hidden="true"></i>{tag.title}
<f:if condition="{iterator.isLast} == false">,</f:if>
</span>
</f:for>
</div>
</f:if>
<!-- author -->
<f:if condition="{newsItem.authorFeUser -> f:count()} > 0">
<f:then>
<span class="news-list__item-author d-block small">
<f:translate key="author" arguments="{0:dd.authorFeUser.name}" />
<f:for each="{newsItem.authorFeUser}" as="authorFeUser" iteration="authorFeUserIterator">
<f:if condition="{authorFeUserIterator.isFirst} === false">
<f:if condition="{authorFeUserIterator.isLast} === true">
<f:then>
and
</f:then>
<f:else>
,
</f:else>
</f:if>
</f:if>
{authorFeUser.name}
</f:for>
</span>
</f:then>
<f:else>
<f:if condition="{newsItem.author}">
<span class="news-list__item-author d-block small">
<f:translate key="author" arguments="{0:dd.author}" />
{newsItem.author}
</span>
</f:if>
</f:else>
</f:if>
<div class="mt-3 mt-lg-4 small">
<n:removeMediaTags>
<f:if condition="{newsItem.teaser}">
<f:then>
<div itemprop="description">{newsItem.teaser -> f:format.html()}</div>
</f:then>
<f:else>
<div itemprop="description">{newsItem.bodytext -> f:format.crop(maxCharacters: '{settings.cropMaxCharacters}', respectWordBoundaries:'1') -> f:format.html()}</div>
</f:else>
</f:if>
</n:removeMediaTags>
<n:link newsItem="{newsItem}" settings="{settings}" class="btn btn-secondary" title="{newsItem.title}">
<f:translate key="more-link" />
</n:link>
</div>
</div>
</div>
</div>
</html>
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:n="http://typo3.org/ns/GeorgRinger/News/ViewHelpers"
xmlns:t3o="http://typo3.org/ns/T3o/T3olayout/ViewHelpers" data-namespace-typo3-fluid="true">
<f:layout name="Detail" />
<!--
=====================
News/Detail.html
-->
<f:section name="content">
<f:if condition="{newsItem}">
<n:format.nothing>
<n:excludeDisplayedNews newsItem="{newsItem}" />
<f:if condition="{newsItem.alternativeTitle}">
<f:then>
<n:titleTag>
<f:format.htmlentitiesDecode>{newsItem.alternativeTitle}</f:format.htmlentitiesDecode>
</n:titleTag>
</f:then>
<f:else>
<n:titleTag>
<f:format.htmlentitiesDecode>{newsItem.title}</f:format.htmlentitiesDecode>
</n:titleTag>
</f:else>
</f:if>
<f:render partial="Detail/Opengraph" arguments="{newsItem: newsItem, settings:settings}" />
</n:format.nothing>
<f:if condition="{settings.detail.showPrevNext}">
<n:simplePrevNext pidList="{newsItem.pid}" news="{newsItem}" as="paginated" sortField="datetime">
<f:if condition="{paginated}">
<ul class="pager">
<f:if condition="{paginated.prev}">
<li class="previous">
<n:link newsItem="{paginated.prev}" settings="{settings}">
<span aria-hidden="true">&larr; </span>{paginated.prev.title}
</n:link>
</li>
</f:if>
<f:if condition="{paginated.next}">
<li class="next">
<n:link newsItem="{paginated.next}" settings="{settings}" class="next">
{paginated.next.title} <span aria-hidden="true"> &rarr;</span>
</n:link>
</li>
</f:if>
</ul>
</f:if>
</n:simplePrevNext>
</f:if>
<f:if condition="{newsItem.mediaNonPreviews}">
<f:then>
<f:render section="headline" arguments="{_all}" />
<div class="row">
<div class="col-md-12 mt-3 pb-3">
<div class="thumbnail">
<f:render section="pictureset" arguments="{file:newsItem.mediaNonPreviews.0}" />
<f:if condition="{newsItem.mediaNonPreviews.0.title} || {newsItem.mediaNonPreviews.0.description}">
<div class="caption">
<f:if condition="{newsItem.mediaNonPreviews.0.description}">
<div class="caption__description">
{newsItem.mediaNonPreviews.0.description}
</div>
</f:if>
</div>
</f:if>
</div>
</div>
<div class="col-md-12">
<f:render section="inner-content" arguments="{_all}" />
</div>
<f:for each="{newsItem.mediaNonPreviews}" iteration="iterator" as="mediaElement">
<f:if condition="{iterator.isFirst}">
<f:else>
<div class="col-md-4">
<f:link.typolink parameter="{f:uri.image(image:mediaElement, width: '1950')}" additionalAttributes="{data-fancybox: 'f-{newsitem.uid}'}">
<f:media file="{mediaElement}"
width="{f:if(condition: settings.media.maxWidth, then: settings.media.maxWidth, else: settings.detail.media.image.maxWidth)}"
height="{f:if(condition: settings.media.maxHeight, then: settings.media.maxHeight, else: settings.detail.media.image.maxHeight)}"
alt="{mediaElement.alternative}" title="{mediaElement.title}"
class="img-fluid" />
</f:link.typolink>
</div>
</f:else>
</f:if>
</f:for>
</div>
</f:then>
<f:else>
<f:render section="headline" arguments="{_all}" />
<f:render section="inner-content" arguments="{_all}" />
</f:else>
</f:if>
<f:render section="discourse" arguments="{_all}" />
</f:if>
</f:section>
<f:section name="headline">
<div class="mb-3">
<time datetime="{f:format.date(date:newsItem.datetime, format:'Y-m-d')}">
<f:format.date format="D. jS F, Y">{newsItem.datetime}</f:format.date>
<meta itemprop="datePublished" content="{f:format.date(date:newsItem.datetime, format:'Y-m-d')}" />
</time>
</div>
<h1 itemprop="headline">{newsItem.title}</h1>
<f:if condition="{newsItem.categories -> f:count()} > 0">
<div class="news-single__categories d-inline-block mb-3">
<span><f:translate key="categories" />:</span>
<f:for each="{newsItem.categories}" as="category" iteration="iterator">
<span>
<f:if condition="{category.uid}=={settings.budgetCategoryID}">
<f:then>
<f:link.page title="{category.title}" class="active" pageUid="{settings.budgetListPid}">{category.title}</f:link.page>
<f:if condition="{iterator.isLast} == false">, </f:if>
</f:then>
<f:else>
<f:link.page title="{category.title}" class="active" pageUid="{settings.listPid}" additionalParams="{tx_news_pi1:{overwriteDemand:{categories: category.uid}}}">{category.title}</f:link.page>
<f:if condition="{iterator.isLast} == false">, </f:if>
</f:else>
</f:if>
</span>
</f:for>
<f:if condition="{newsItem.authorFeUser -> f:count()} > 0">
<f:then>
<span class="news-list__item-author d-block small">
<f:translate key="author" arguments="{0:dd.authorFeUser.name}" />
<f:for each="{newsItem.authorFeUser}" as="authorFeUser" iteration="authorFeUserIterator">
<f:if condition="{authorFeUserIterator.isFirst} === false">
<f:if condition="{authorFeUserIterator.isLast} === true">
<f:then>
and
</f:then>
<f:else>
,
</f:else>
</f:if>
</f:if>
{authorFeUser.name}
<f:if condition="{authorFeUser.email}">
<f:format.raw><t3o:gravatar emailAddress="{authorFeUser.email}" size="40" class="gravatar" /></f:format.raw>
</f:if>
</f:for>
</span>
</f:then>
<f:else>
<f:if condition="{newsItem.author}">
<span class="news-list__item-author d-block small">
<f:translate key="author" arguments="{0:newsItem.author}" />
<f:if condition="{newsItem.authorEmail}">
<f:format.raw><t3o:gravatar emailAddress="{newsItem.authorEmail}" size="40" class="gravatar" /></f:format.raw>
</f:if>
</span>
</f:if>
</f:else>
</f:if>
<f:if condition="{settings.detail.showSocialShareButtons}">
<f:comment>
Care about the privacy of your readers? Checkout https://typo3.org/extensions/repository/view/rx_shariff and
it will be used automatically!
</f:comment>
<n:extensionLoaded extensionKey="rx_shariff">
<div class="mt-3">
<f:render partial="Detail/Shariff" />
</div>
</n:extensionLoaded>
</f:if>
</div>
</f:if>
</f:section>
<f:section name="inner-content">
<n:renderMedia news="{newsItem}" imgClass="img-fluid" videoClass="video-wrapper" audioClass="audio-wrapper">
<f:if condition="{newsItem.teaser}">
<!-- teaser -->
<div class="lead mt-3" itemprop="description">
<f:format.html>{newsItem.teaser}</f:format.html>
</div>
</f:if>
<!-- main text -->
<div class="news-text-wrap" itemprop="articleBody">
<f:format.html>{newsItem.bodytext}</f:format.html>
</div>
<f:if condition="{newsItem.contentElements}">
<!-- content elements -->
<f:cObject typoscriptObjectPath="lib.tx_news.contentElementRendering">
{newsItem.contentElementIdList}
</f:cObject>
</f:if>
</n:renderMedia>
<f:if condition="{newsItem.contributors}">
<div class="news-list__item-author d-block small">
<b>Additional contributors for this article</b>
<ul>
<f:for each="{newsItem.contributors}" as="contributor">
<li>
<f:translate extensionName="t3org_layout" key="tx_t3org_layout.news.contributors.roles.{contributor.role}">
role
</f:translate>
:
{contributor.authorFeUser.name}
<f:if condition="{contributor.authorFeUser.email}">
<f:format.raw><t3o:gravatar emailAddress="{contributor.authorFeUser.email}" size="40" class="gravatar" /></f:format.raw>
</f:if>
</li>
</f:for>
</ul>
</div>
</f:if>
<f:if condition="{settings.backPid}">
<!-- Link Back -->
<f:link.page class="btn btn-default" pageUid="{settings.backPid}">
<f:translate key="back-link" />
</f:link.page>
</f:if>
<f:if condition="{newsItem.allRelatedSorted}">
<!-- Related news records -->
<section class="container newsrelated">
<div class="col-md-12 mt-3 mb-3">
<div class="frame frame-default frame-type-text frame-layout-0">
<header>
<h2>
<f:translate key="related-news" />
</h2>
</header>
<ul class="list-unstyled d-block">
<f:for each="{newsItem.allRelatedSorted}" as="related">
<li>
<n:link class="news-related-item d-block mb-3" newsItem="{related}"
settings="{settings}" title="{related.title}">
<small>
<time datetime="{f:format.date(date:related.datetime, format:'%d %B %Y')}">
<f:format.date format="%d %B %Y">{related.datetime}</f:format.date>
<meta itemprop="datePublished"
content="{f:format.date(date:related.datetime, format:'Y-m-d')}" />
</time>
<f:if condition="{related.categories -> f:count()} > 0">
<div class="d-inline-block news-related-item__categories">
<span><f:translate key="categories" />:</span>
<f:for each="{related.categories}" as="category" iteration="iterator">
<span>{category.title}<f:if condition="{iterator.isLast} == false">,</f:if></span>
</f:for>
</div>
</f:if>
</small>
<h5>{related.title}</h5>
</n:link>
</li>
</f:for>
</ul>
</div>
</div>
</section>
</f:if>
<f:if condition="{newsItem.relatedFiles}">
<!-- related files -->
<section class="container">
<div class="uploadsCard">
<div class="card-header">
<f:translate key="related-files" />
</div>
<ul class="list-group list-group-flush list-unstyled">
<f:render partial="General/RelatedFiles" arguments="{files: newsItem.relatedFiles}" />
</ul>
</div>
</section>
</f:if>
<f:if condition="{newsItem.relatedLinks}">
<!-- Related links -->
<section class="container">
<div class="frame frame-default frame-type-text frame-layout-0">
<header>
<h2>
<f:translate key="related-links" />
</h2>
</header>
<ul class="list-unstyled d-block">
<f:for each="{newsItem.relatedLinks}" as="relatedLink">
<li>
<f:link.page class="d-block" pageUid="{relatedLink.uri}" title="{relatedLink.title}"
target="{n:targetLink(link:relatedLink.uri)}">{f:if(condition:
relatedLink.title, then: relatedLink.title, else: relatedLink.uri)}
</f:link.page>
<f:if condition="{relatedLink.description}"><span>{relatedLink.description}</span>
</f:if>
</li>
</f:for>
</ul>
</div>
</section>
</f:if>
</f:section>
<f:section name="discourse">
<div id="discourse-comments" class="mt-5"></div>
<script>
DiscourseEmbed = {
discourseUrl: 'https://talk.typo3.org/',
discourseEmbedUrl: '<f:uri.page absolute="1" additionalParams="{tx_news_pi1: {news: newsItem.uid, controller: \"News\", action: \"detail\"}}" />'
};
(function () {
var d = document.createElement('script');
d.type = 'text/javascript';
d.async = true;
d.src = DiscourseEmbed.discourseUrl + 'javascripts/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(d);
})();
</script>
</f:section>
<f:section name="pictureset">
<picture>
<source srcset="{f:uri.image(image: file, maxWidth: 1200, cropVariant: 'default')}" media="(min-width: 1200px)">
<source srcset="{f:uri.image(image: file, maxWidth: 1200, cropVariant: 'default')}" media="(min-width: 992px)">
<source srcset="{f:uri.image(image: file, maxWidth: 992, cropVariant: 'tablet')}" media="(min-width: 768px)">
<source srcset="{f:uri.image(image: file, maxWidth: 768, cropVariant: 'mobile')}" media="(max-width: 767px)">
<!---Fallback--->
<f:image class="img-fluid" image="{file}" maxWidth="1200" cropVariant="{default}" alt="{file.alternative}" title="{file.title}" />
</picture>
</f:section>
</html>
......@@ -9,7 +9,7 @@ CREATE TABLE tx_sfeventmgt_domain_model_event (
# Table structure for table 'tx_news_domain_model_news'
#
CREATE TABLE tx_news_domain_model_news (
author_fe_user varchar(30) DEFAULT '' NOT NULL,
author_fe_user varchar(100) DEFAULT '' NOT NULL,
default_image varchar(30) DEFAULT '' NOT NULL,
contributors int DEFAULT 0
);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment