mirror of
https://github.com/FriendsOfTYPO3/tea.git
synced 2024-11-22 10:16:12 +01:00
parent
1f263d8839
commit
ad460b20b3
10 changed files with 286 additions and 2 deletions
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||||
namespace TTN\Tea\Controller;
|
namespace TTN\Tea\Controller;
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use TTN\Tea\Domain\Model\Product\Tea;
|
||||||
use TTN\Tea\Domain\Repository\Product\TeaRepository;
|
use TTN\Tea\Domain\Repository\Product\TeaRepository;
|
||||||
use TYPO3\CMS\Core\Context\Context;
|
use TYPO3\CMS\Core\Context\Context;
|
||||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||||
|
@ -41,4 +42,32 @@ class FrontEndEditorController extends ActionController
|
||||||
{
|
{
|
||||||
return $this->context->getPropertyFromAspect('frontend.user', 'id');
|
return $this->context->getPropertyFromAspect('frontend.user', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function editAction(Tea $tea): ResponseInterface
|
||||||
|
{
|
||||||
|
$this->checkIfUserIsOwner($tea);
|
||||||
|
|
||||||
|
$this->view->assign('tea', $tea);
|
||||||
|
|
||||||
|
return $this->htmlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*/
|
||||||
|
private function checkIfUserIsOwner(Tea $tea): void
|
||||||
|
{
|
||||||
|
if ($tea->getOwnerUid() !== $this->getUidOfLoggedInUser()) {
|
||||||
|
throw new \RuntimeException('You do not have the permissions to edit this tea.', 1687363749);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateAction(Tea $tea): ResponseInterface
|
||||||
|
{
|
||||||
|
$this->checkIfUserIsOwner($tea);
|
||||||
|
|
||||||
|
$this->teaRepository->update($tea);
|
||||||
|
|
||||||
|
return $this->redirect('index');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,30 @@
|
||||||
<source>Title</source>
|
<source>Title</source>
|
||||||
<target>Titel</target>
|
<target>Titel</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.property.description">
|
||||||
|
<source>Description</source>
|
||||||
|
<target>Beschreibung</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.edit.heading">
|
||||||
|
<source>Edit tea</source>
|
||||||
|
<target>Tee bearbeiten</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.action.actions">
|
||||||
|
<source>Actions</source>
|
||||||
|
<target>Aktionen</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.action.edit">
|
||||||
|
<source>Edit</source>
|
||||||
|
<target>Bearbeiten</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.action.cancel">
|
||||||
|
<source>Cancel</source>
|
||||||
|
<target>Abbrechen</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.action.save">
|
||||||
|
<source>Save</source>
|
||||||
|
<target>Speichern</target>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
<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.property.description">
|
||||||
|
<source>Description</source>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="plugin.frontEndEditor.index.heading">
|
<trans-unit id="plugin.frontEndEditor.index.heading">
|
||||||
<source>My teas</source>
|
<source>My teas</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -33,6 +36,21 @@
|
||||||
<trans-unit id="plugin.frontEndEditor.property.title">
|
<trans-unit id="plugin.frontEndEditor.property.title">
|
||||||
<source>Title</source>
|
<source>Title</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.edit.heading">
|
||||||
|
<source>Edit tea</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.action.actions">
|
||||||
|
<source>Actions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.action.edit">
|
||||||
|
<source>Edit</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.action.cancel">
|
||||||
|
<source>Cancel</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="plugin.frontEndEditor.action.save">
|
||||||
|
<source>Save</source>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
36
Resources/Private/Partials/FrontEndEditor/Form.html
Normal file
36
Resources/Private/Partials/FrontEndEditor/Form.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<f:variable name="propertyLabelPrefix" value="plugin.frontEndEditor.property"/>
|
||||||
|
<f:variable name="idPrefix" value="tea-editor"/>
|
||||||
|
|
||||||
|
<fieldset class="mb-3">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="{idPrefix}-title" class="col-sm-2 col-form-label">
|
||||||
|
<f:translate key="{propertyLabelPrefix}.title"/>
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<f:form.textfield property="title" id="{idPrefix}-title" maxlength="255"
|
||||||
|
class="form-control" errorClass="is-invalid" required="required"/>
|
||||||
|
<f:render partial="FrontEndEditor/ValidationResult" arguments="{property: 'title'}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="{idPrefix}-description" class="col-sm-2 col-form-label">
|
||||||
|
<f:translate key="{propertyLabelPrefix}.description"/>
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<f:form.textarea property="description" id="{idPrefix}-description"
|
||||||
|
class="form-control" rows="5" errorClass="is-invalid"
|
||||||
|
additionalAttributes="{maxlength: 2000}"/>
|
||||||
|
<f:render partial="FrontEndEditor/ValidationResult" arguments="{property: 'description'}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end mt-3 mb-3">
|
||||||
|
<f:link.action action="index" class="btn btn-outline-secondary me-3" additionalAttributes="{role: 'button'}">
|
||||||
|
<f:translate key="plugin.frontEndEditor.action.cancel"/>
|
||||||
|
</f:link.action>
|
||||||
|
<f:form.submit value="{f:translate(key: 'plugin.frontEndEditor.action.save')}" class="btn btn-primary"/>
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||||
|
<f:form.validationResults for="tea.{property}">
|
||||||
|
<f:if condition="{validationResults.flattenedErrors}">
|
||||||
|
<f:for each="{validationResults.errors}" as="error">
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
{error.message}
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</f:if>
|
||||||
|
</f:form.validationResults>
|
||||||
|
</html>
|
14
Resources/Private/Templates/FrontEndEditor/Edit.html
Normal file
14
Resources/Private/Templates/FrontEndEditor/Edit.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<!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.edit.heading"/>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<f:form action="update" name="tea" object="{tea}">
|
||||||
|
<f:render partial="FrontEndEditor/Form" arguments="{_all}"/>
|
||||||
|
</f:form>
|
||||||
|
</f:section>
|
||||||
|
</html>
|
|
@ -17,6 +17,9 @@
|
||||||
<th scope="col">
|
<th scope="col">
|
||||||
<f:translate key="plugin.frontEndEditor.property.title"/>
|
<f:translate key="plugin.frontEndEditor.property.title"/>
|
||||||
</th>
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
<f:translate key="plugin.frontEndEditor.action.actions"/>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<f:for each="{teas}" as="tea">
|
<f:for each="{teas}" as="tea">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -26,6 +29,11 @@
|
||||||
<td>
|
<td>
|
||||||
{tea.title}
|
{tea.title}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<f:link.action action="edit" arguments="{tea: tea}">
|
||||||
|
<f:translate key="plugin.frontEndEditor.action.edit"/>
|
||||||
|
</f:link.action>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</f:for>
|
</f:for>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -6,12 +6,15 @@ namespace TTN\Tea\Tests\Unit\Controller;
|
||||||
|
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use TTN\Tea\Controller\FrontEndEditorController;
|
use TTN\Tea\Controller\FrontEndEditorController;
|
||||||
|
use TTN\Tea\Domain\Model\Product\Tea;
|
||||||
use TTN\Tea\Domain\Repository\Product\TeaRepository;
|
use TTN\Tea\Domain\Repository\Product\TeaRepository;
|
||||||
use TYPO3\CMS\Core\Context\Context;
|
use TYPO3\CMS\Core\Context\Context;
|
||||||
use TYPO3\CMS\Core\Context\UserAspect;
|
use TYPO3\CMS\Core\Context\UserAspect;
|
||||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||||
|
use TYPO3\CMS\Core\Http\RedirectResponse;
|
||||||
use TYPO3\CMS\Core\Information\Typo3Version;
|
use TYPO3\CMS\Core\Information\Typo3Version;
|
||||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||||
|
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
|
||||||
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
|
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
|
||||||
use TYPO3\CMS\Fluid\View\TemplateView;
|
use TYPO3\CMS\Fluid\View\TemplateView;
|
||||||
use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
|
use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
|
||||||
|
@ -118,4 +121,145 @@ final class FrontEndEditorControllerTest extends UnitTestCase
|
||||||
|
|
||||||
self::assertInstanceOf(HtmlResponse::class, $result);
|
self::assertInstanceOf(HtmlResponse::class, $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function editActionWithOwnTeaAssignsProvidedTeaToView(): void
|
||||||
|
{
|
||||||
|
$userUid = 5;
|
||||||
|
$this->setUidOfLoggedInUser($userUid);
|
||||||
|
$tea = new Tea();
|
||||||
|
$tea->setOwnerUid($userUid);
|
||||||
|
|
||||||
|
$this->viewMock->expects(self::once())->method('assign')->with('tea', $tea);
|
||||||
|
|
||||||
|
$this->subject->editAction($tea);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function editActionWithTeaFromOtherUserThrowsException(): void
|
||||||
|
{
|
||||||
|
$this->setUidOfLoggedInUser(1);
|
||||||
|
$tea = new Tea();
|
||||||
|
$tea->setOwnerUid(2);
|
||||||
|
|
||||||
|
$this->expectException(\RuntimeException::class);
|
||||||
|
$this->expectExceptionMessage('You do not have the permissions to edit this tea.');
|
||||||
|
$this->expectExceptionCode(1687363749);
|
||||||
|
|
||||||
|
$this->subject->editAction($tea);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function editActionWithTeaWithoutOwnerThrowsException(): void
|
||||||
|
{
|
||||||
|
$this->setUidOfLoggedInUser(1);
|
||||||
|
$tea = new Tea();
|
||||||
|
$tea->setOwnerUid(0);
|
||||||
|
|
||||||
|
$this->expectException(\RuntimeException::class);
|
||||||
|
$this->expectExceptionMessage('You do not have the permissions to edit this tea.');
|
||||||
|
$this->expectExceptionCode(1687363749);
|
||||||
|
|
||||||
|
$this->subject->editAction($tea);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function editActionForOwnTeaReturnsHtmlResponse(): void
|
||||||
|
{
|
||||||
|
$userUid = 5;
|
||||||
|
$this->setUidOfLoggedInUser($userUid);
|
||||||
|
$tea = new Tea();
|
||||||
|
$tea->setOwnerUid($userUid);
|
||||||
|
|
||||||
|
$result = $this->subject->editAction($tea);
|
||||||
|
|
||||||
|
self::assertInstanceOf(HtmlResponse::class, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function updateActionWithOwnTeaPersistsProvidedTea(): void
|
||||||
|
{
|
||||||
|
$userUid = 5;
|
||||||
|
$this->setUidOfLoggedInUser($userUid);
|
||||||
|
$tea = new Tea();
|
||||||
|
$tea->setOwnerUid($userUid);
|
||||||
|
$this->mockRedirect('index');
|
||||||
|
|
||||||
|
$this->teaRepositoryMock->expects(self::once())->method('update')->with($tea);
|
||||||
|
|
||||||
|
$this->subject->updateAction($tea);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mockRedirect(string $actionName): void
|
||||||
|
{
|
||||||
|
if ((new Typo3Version())->getMajorVersion() <= 11) {
|
||||||
|
$this->subject->expects(self::once())->method('redirect')
|
||||||
|
->with($actionName)
|
||||||
|
// @phpstan-ignore-next-line This class does not exist in V12 anymore, but this branch is V11-only.
|
||||||
|
->willThrowException(new StopActionException('redirectToUri', 1476045828));
|
||||||
|
// @phpstan-ignore-next-line This class does not exist in V12 anymore, but this branch is V11-only.
|
||||||
|
$this->expectException(StopActionException::class);
|
||||||
|
} else {
|
||||||
|
$redirectResponse = $this->createStub(RedirectResponse::class);
|
||||||
|
$this->subject->expects(self::once())->method('redirect')->with($actionName)
|
||||||
|
->willReturn($redirectResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function updateActionWithOwnTeaRedirectsToIndexAction(): void
|
||||||
|
{
|
||||||
|
$userUid = 5;
|
||||||
|
$this->setUidOfLoggedInUser($userUid);
|
||||||
|
$tea = new Tea();
|
||||||
|
$tea->setOwnerUid($userUid);
|
||||||
|
|
||||||
|
$this->mockRedirect('index');
|
||||||
|
|
||||||
|
$this->subject->updateAction($tea);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function updateActionWithTeaFromOtherUserThrowsException(): void
|
||||||
|
{
|
||||||
|
$this->setUidOfLoggedInUser(1);
|
||||||
|
$tea = new Tea();
|
||||||
|
$tea->setOwnerUid(2);
|
||||||
|
|
||||||
|
$this->expectException(\RuntimeException::class);
|
||||||
|
$this->expectExceptionMessage('You do not have the permissions to edit this tea.');
|
||||||
|
$this->expectExceptionCode(1687363749);
|
||||||
|
|
||||||
|
$this->subject->updateAction($tea);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function updateActionWithTeaWithoutOwnerThrowsException(): void
|
||||||
|
{
|
||||||
|
$this->setUidOfLoggedInUser(1);
|
||||||
|
$tea = new Tea();
|
||||||
|
$tea->setOwnerUid(0);
|
||||||
|
|
||||||
|
$this->expectException(\RuntimeException::class);
|
||||||
|
$this->expectExceptionMessage('You do not have the permissions to edit this tea.');
|
||||||
|
$this->expectExceptionCode(1687363749);
|
||||||
|
|
||||||
|
$this->subject->updateAction($tea);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,12 +42,12 @@ ExtensionUtility::configurePlugin(
|
||||||
'TeaFrontEndEditor',
|
'TeaFrontEndEditor',
|
||||||
// all actions
|
// all actions
|
||||||
[
|
[
|
||||||
FrontEndEditorController::class => 'index',
|
FrontEndEditorController::class => 'index, edit, update',
|
||||||
],
|
],
|
||||||
// non-cacheable actions
|
// non-cacheable actions
|
||||||
[
|
[
|
||||||
// All actions need to be non-cacheable because they either contain dynamic data,
|
// 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).
|
// or because they are specific to the logged-in FE user (while FE content is cached by FE groups).
|
||||||
FrontEndEditorController::class => 'index',
|
FrontEndEditorController::class => 'index, edit, update',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue