mirror of
https://github.com/FriendsOfTYPO3/tea.git
synced 2024-11-10 03:56:12 +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
|
## x.y.z
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- Add a FE editor (#864)
|
||||||
- Add automerging of green Dependabot PRs (#756)
|
- Add automerging of green Dependabot PRs (#756)
|
||||||
|
|
||||||
### Changed
|
### 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'
|
'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.
|
// This removes the default controls from the plugin.
|
||||||
$controlsToRemove = 'recursive,select_key,pages';
|
$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_teaindex'] = $controlsToRemove;
|
||||||
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist']['tea_teashow'] = $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>
|
<source>Tea single view</source>
|
||||||
<target>Tee-Einzelansicht</target>
|
<target>Tee-Einzelansicht</target>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="plugin.tea.heading">
|
||||||
<source>Our selection of assorted teas</source>
|
<source>Our selection of assorted teas</source>
|
||||||
<target>Unsere Auswahl an erlesenen Tees</target>
|
<target>Unsere Auswahl an erlesenen Tees</target>
|
||||||
|
@ -23,6 +27,22 @@
|
||||||
<source>Title</source>
|
<source>Title</source>
|
||||||
<target>Titel</target>
|
<target>Titel</target>
|
||||||
</trans-unit>
|
</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>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
<trans-unit id="plugin.tea_show">
|
<trans-unit id="plugin.tea_show">
|
||||||
<source>Tea single view</source>
|
<source>Tea single view</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.tea_frontend_editor">
|
||||||
|
<source>Tea front-end editor</source>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="plugin.tea.heading">
|
<trans-unit id="plugin.tea.heading">
|
||||||
<source>Our selection of assorted teas</source>
|
<source>Our selection of assorted teas</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -18,6 +21,18 @@
|
||||||
<trans-unit id="plugin.tea.property.title">
|
<trans-unit id="plugin.tea.property.title">
|
||||||
<source>Title</source>
|
<source>Title</source>
|
||||||
</trans-unit>
|
</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>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use TTN\Tea\Controller\FrontEndEditorController;
|
||||||
use TTN\Tea\Controller\TeaController;
|
use TTN\Tea\Controller\TeaController;
|
||||||
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
|
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
|
||||||
|
|
||||||
|
@ -32,3 +33,21 @@ ExtensionUtility::configurePlugin(
|
||||||
TeaController::class => '',
|
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