mirror of https://github.com/FriendsOfTYPO3/tea.git synced 2024-09-19 23:16:12 +02:00

[FEATURE] Add FE editing for existing tea records (#872)

Part of #871
This commit is contained in:
Oliver Klee 2023-06-22 14:20:14 +02:00 committed by GitHub
parent 1f263d8839
commit ad460b20b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 286 additions and 2 deletions

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace TTN\Tea\Controller;
use Psr\Http\Message\ResponseInterface;
use TTN\Tea\Domain\Model\Product\Tea;
use TTN\Tea\Domain\Repository\Product\TeaRepository;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
@ -41,4 +42,32 @@ class FrontEndEditorController extends ActionController
{
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');
}
}

View file

@ -43,6 +43,30 @@
<source>Title</source>
<target>Titel</target>
</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>
</file>
</xliff>

View file

@ -21,6 +21,9 @@
<trans-unit id="plugin.tea.property.title">
<source>Title</source>
</trans-unit>
<trans-unit id="plugin.frontEndEditor.property.description">
<source>Description</source>
</trans-unit>
<trans-unit id="plugin.frontEndEditor.index.heading">
<source>My teas</source>
</trans-unit>
@ -33,6 +36,21 @@
<trans-unit id="plugin.frontEndEditor.property.title">
<source>Title</source>
</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>
</file>
</xliff>

View 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>

View file

@ -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>

View 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>

View file

@ -17,6 +17,9 @@
<th scope="col">
<f:translate key="plugin.frontEndEditor.property.title"/>
</th>
<th scope="col">
<f:translate key="plugin.frontEndEditor.action.actions"/>
</th>
</tr>
<f:for each="{teas}" as="tea">
<tr>
@ -26,6 +29,11 @@
<td>
{tea.title}
</td>
<td>
<f:link.action action="edit" arguments="{tea: tea}">
<f:translate key="plugin.frontEndEditor.action.edit"/>
</f:link.action>
</td>
</tr>
</f:for>
</table>

View file

@ -6,12 +6,15 @@ namespace TTN\Tea\Tests\Unit\Controller;
use PHPUnit\Framework\MockObject\MockObject;
use TTN\Tea\Controller\FrontEndEditorController;
use TTN\Tea\Domain\Model\Product\Tea;
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\Http\RedirectResponse;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use TYPO3\CMS\Fluid\View\TemplateView;
use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
@ -118,4 +121,145 @@ final class FrontEndEditorControllerTest extends UnitTestCase
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);
}
}

View file

@ -42,12 +42,12 @@ ExtensionUtility::configurePlugin(
'TeaFrontEndEditor',
// all actions
[
FrontEndEditorController::class => 'index',
FrontEndEditorController::class => 'index, edit, update',
],
// 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',
FrontEndEditorController::class => 'index, edit, update',
]
);