mirror of https://github.com/FriendsOfTYPO3/tea.git synced 2024-12-22 12:46:11 +01:00

[FEATURE] Add a list view for the FE tea editor (#864)

This is one more part of building CRUD functionality.
This commit is contained in:
Oliver Klee 2023-06-22 13:17:51 +02:00 committed by GitHub
parent 8fe9307b37
commit 1f263d8839
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 280 additions and 0 deletions

View file

@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## x.y.z
### Added
- Add a FE editor (#864)
- Add automerging of green Dependabot PRs (#756)
### Changed

View file

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace TTN\Tea\Controller;
use Psr\Http\Message\ResponseInterface;
use TTN\Tea\Domain\Repository\Product\TeaRepository;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
/**
* Controller for a CRUD FE editor for teas.
*/
class FrontEndEditorController extends ActionController
{
private Context $context;
private TeaRepository $teaRepository;
public function __construct(Context $context, TeaRepository $teaRepository)
{
$this->context = $context;
$this->teaRepository = $teaRepository;
}
public function indexAction(): ResponseInterface
{
$userUid = $this->getUidOfLoggedInUser();
if ($userUid > 0) {
$this->view->assign('teas', $this->teaRepository->findByOwnerUid($userUid));
}
return $this->htmlResponse();
}
/**
* @return int<0, max>
*/
private function getUidOfLoggedInUser(): int
{
return $this->context->getPropertyFromAspect('frontend.user', 'id');
}
}

View file

@ -23,9 +23,17 @@ call_user_func(
'EXT:tea/Resources/Public/Icons/Extension.svg'
);
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
'Tea',
'TeaFrontEndEditor',
'LLL:EXT:tea/Resources/Private/Language/locallang.xlf:plugin.tea_frontend_editor',
'EXT:tea/Resources/Public/Icons/Extension.svg'
);
// This removes the default controls from the plugin.
$controlsToRemove = 'recursive,select_key,pages';
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist']['tea_teaindex'] = $controlsToRemove;
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist']['tea_teashow'] = $controlsToRemove;
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist']['tea_teafrontendeditor'] = $controlsToRemove;
}
);

View file

@ -11,6 +11,10 @@
<source>Tea single view</source>
<target>Tee-Einzelansicht</target>
</trans-unit>
<trans-unit id="plugin.tea_frontend_editor">
<source>Tea front-end editor</source>
<target>Frontend-Editor für Tee</target>
</trans-unit>
<trans-unit id="plugin.tea.heading">
<source>Our selection of assorted teas</source>
<target>Unsere Auswahl an erlesenen Tees</target>
@ -23,6 +27,22 @@
<source>Title</source>
<target>Titel</target>
</trans-unit>
<trans-unit id="plugin.frontEndEditor.index.heading">
<source>My teas</source>
<target>Meine Tees</target>
</trans-unit>
<trans-unit id="plugin.frontEndEditor.message.noTeas">
<source>You have not created any teas yet.</source>
<target>Du hast noch keine Tees angelegt.</target>
</trans-unit>
<trans-unit id="plugin.frontEndEditor.property.uid">
<source>UID</source>
<target>UID</target>
</trans-unit>
<trans-unit id="plugin.frontEndEditor.property.title">
<source>Title</source>
<target>Titel</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -9,6 +9,9 @@
<trans-unit id="plugin.tea_show">
<source>Tea single view</source>
</trans-unit>
<trans-unit id="plugin.tea_frontend_editor">
<source>Tea front-end editor</source>
</trans-unit>
<trans-unit id="plugin.tea.heading">
<source>Our selection of assorted teas</source>
</trans-unit>
@ -18,6 +21,18 @@
<trans-unit id="plugin.tea.property.title">
<source>Title</source>
</trans-unit>
<trans-unit id="plugin.frontEndEditor.index.heading">
<source>My teas</source>
</trans-unit>
<trans-unit id="plugin.frontEndEditor.message.noTeas">
<source>You have not created any teas yet.</source>
</trans-unit>
<trans-unit id="plugin.frontEndEditor.property.uid">
<source>UID</source>
</trans-unit>
<trans-unit id="plugin.frontEndEditor.property.title">
<source>Title</source>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -0,0 +1,12 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:security.ifAuthenticated>
<f:then>
<f:render section="main"/>
</f:then>
<f:else>
<div class="alert alert-danger" role="alert">
Please configure this plugin to be only visible if a website user is logged in.
</div>
</f:else>
</f:security.ifAuthenticated>
</html>

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:layout name="FrontEndEditor"/>
<f:section name="main">
<h2>
<f:translate key="plugin.frontEndEditor.index.heading"/>
</h2>
<f:if condition="{teas}">
<f:then>
<table class="table table-striped table-hover">
<tr>
<th scope="col">
<f:translate key="plugin.frontEndEditor.property.uid"/>
</th>
<th scope="col">
<f:translate key="plugin.frontEndEditor.property.title"/>
</th>
</tr>
<f:for each="{teas}" as="tea">
<tr>
<td>
{tea.uid}
</td>
<td>
{tea.title}
</td>
</tr>
</f:for>
</table>
</f:then>
<f:else>
<p class="alert alert-info" role="alert">
<f:translate key="plugin.frontEndEditor.message.noTeas"/>
</p>
</f:else>
</f:if>
</f:section>
</html>

View file

@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace TTN\Tea\Tests\Unit\Controller;
use PHPUnit\Framework\MockObject\MockObject;
use TTN\Tea\Controller\FrontEndEditorController;
use TTN\Tea\Domain\Repository\Product\TeaRepository;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\UserAspect;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Fluid\View\TemplateView;
use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
/**
* @covers \TTN\Tea\Controller\FrontEndEditorController
*/
final class FrontEndEditorControllerTest extends UnitTestCase
{
/**
* @var FrontEndEditorController&MockObject&AccessibleObjectInterface
*/
private FrontEndEditorController $subject;
/**
* @var TemplateView&MockObject
*/
private TemplateView $viewMock;
private Context $context;
/**
* @var TeaRepository&MockObject
*/
private TeaRepository $teaRepositoryMock;
protected function setUp(): void
{
parent::setUp();
$this->context = new Context();
$this->teaRepositoryMock = $this->createMock(TeaRepository::class);
// We need to create an accessible mock in order to be able to set the protected `view`.
$methodsToMock = ['htmlResponse', 'redirect', 'redirectToUri'];
if ((new Typo3Version())->getMajorVersion() <= 11) {
$methodsToMock[] = 'forward';
}
$this->subject = $this->getAccessibleMock(
FrontEndEditorController::class,
$methodsToMock,
[$this->context, $this->teaRepositoryMock]
);
$this->viewMock = $this->createMock(TemplateView::class);
$this->subject->_set('view', $this->viewMock);
$responseStub = $this->createStub(HtmlResponse::class);
$this->subject->method('htmlResponse')->willReturn($responseStub);
}
/**
* @param int<0, max> $userUid
*/
private function setUidOfLoggedInUser(int $userUid): void
{
$userAspectMock = $this->createMock(UserAspect::class);
$userAspectMock->method('get')->with('id')->willReturn($userUid);
$this->context->setAspect('frontend.user', $userAspectMock);
}
/**
* @test
*/
public function isActionController(): void
{
self::assertInstanceOf(ActionController::class, $this->subject);
}
/**
* @test
*/
public function indexActionForNoLoggedInUserAssignsNothingToView(): void
{
$this->setUidOfLoggedInUser(0);
$this->viewMock->expects(self::never())->method('assign');
$this->subject->indexAction();
}
/**
* @test
*/
public function indexActionForLoggedInUserAssignsTeasOwnedByTheLoggedInUserToView(): void
{
$userUid = 5;
$this->setUidOfLoggedInUser($userUid);
$teas = $this->createMock(QueryResultInterface::class);
$this->teaRepositoryMock->method('findByOwnerUid')->with($userUid)->willReturn($teas);
$this->viewMock->expects(self::once())->method('assign')->with('teas', $teas);
$this->subject->indexAction();
}
/**
* @test
*/
public function indexActionReturnsHtmlResponse(): void
{
$result = $this->subject->indexAction();
self::assertInstanceOf(HtmlResponse::class, $result);
}
}

View file

@ -2,6 +2,7 @@
declare(strict_types=1);
use TTN\Tea\Controller\FrontEndEditorController;
use TTN\Tea\Controller\TeaController;
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
@ -32,3 +33,21 @@ ExtensionUtility::configurePlugin(
TeaController::class => '',
]
);
// This makes the plugin available for front-end rendering.
ExtensionUtility::configurePlugin(
// extension name, matching the PHP namespaces (but without the vendor)
'Tea',
// arbitrary, but unique plugin name (not visible in the BE)
'TeaFrontEndEditor',
// all actions
[
FrontEndEditorController::class => 'index',
],
// non-cacheable actions
[
// All actions need to be non-cacheable because they either contain dynamic data,
// or because they are specific to the logged-in FE user (while FE content is cached by FE groups).
FrontEndEditorController::class => 'index',
]
);