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:
parent
8fe9307b37
commit
1f263d8839
9 changed files with 280 additions and 0 deletions
|
@ -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
|
||||
|
|
44
Classes/Controller/FrontEndEditorController.php
Normal file
44
Classes/Controller/FrontEndEditorController.php
Normal 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');
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
12
Resources/Private/Layouts/FrontEndEditor.html
Normal file
12
Resources/Private/Layouts/FrontEndEditor.html
Normal 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>
|
40
Resources/Private/Templates/FrontEndEditor/Index.html
Normal file
40
Resources/Private/Templates/FrontEndEditor/Index.html
Normal 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>
|
121
Tests/Unit/Controller/FrontEndEditorControllerTest.php
Normal file
121
Tests/Unit/Controller/FrontEndEditorControllerTest.php
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
]
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue