fe_comment: Feedback Overview content element in the page JSON
- New content element "Feedback Overview" (CType fecomment_overview):
the current FE user's feedback state under content.feedbackOverview,
so the overview page renders like any other page instead of a
front-end special case
- Rendered as USER_INT inside the cached page JSON (EXT:headless replaces
the field per request): data is per user, visitors without a session
get {"allowed": false} - verified that nothing leaks from the cache
- One implementation for both consumers: the new FeedbackState service
builds the state payload; the API middleware delegates to it
- State payload (API and element) gained canSubmit and actions (all
write endpoints with method and URL), so the front end needs no
hard-coded endpoints for the submit button below the ticket list
- New site set evomedien/fe-comment carries the TypoScript, added to the
site configuration; icon fecomment-overview
This commit is contained in:
1 parent
887caed2aa
commit
eb0d0d9ebc
9 files changed
+301
-110
No files matched your search
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Evomedien\FeComment\Middleware;
|
||||
|
||||
use Evomedien\FeComment\Service\ExtensionSettings;
|
||||
use Evomedien\FeComment\Service\FeedbackState;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
@@ -91,23 +92,13 @@ final class FeedbackApiMiddleware implements MiddlewareInterface
|
||||
/** @return array{uid:int,username:string,name:string,email:string}|null */
|
||||
private function allowedUser(ServerRequestInterface $request): ?array
|
||||
{
|
||||
$frontendUser = $request->getAttribute('frontend.user');
|
||||
if (!$frontendUser instanceof FrontendUserAuthentication || empty($frontendUser->user['uid'])) {
|
||||
return null;
|
||||
}
|
||||
$requiredGroup = $this->settings->feUserGroup();
|
||||
if ($requiredGroup > 0) {
|
||||
$groups = GeneralUtility::intExplode(',', (string)($frontendUser->user['usergroup'] ?? ''), true);
|
||||
if (!in_array($requiredGroup, $groups, true)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return [
|
||||
'uid' => (int)$frontendUser->user['uid'],
|
||||
'username' => (string)($frontendUser->user['username'] ?? ''),
|
||||
'name' => trim((string)($frontendUser->user['name'] ?? '')) ?: (string)($frontendUser->user['username'] ?? ''),
|
||||
'email' => (string)($frontendUser->user['email'] ?? ''),
|
||||
];
|
||||
return $this->stateService()->allowedUser($request);
|
||||
}
|
||||
|
||||
/** Shared with the "Feedback Overview" content element - one implementation. */
|
||||
private function stateService(): FeedbackState
|
||||
{
|
||||
return GeneralUtility::makeInstance(FeedbackState::class, $this->settings);
|
||||
}
|
||||
|
||||
// ---- routes -------------------------------------------------------------
|
||||
@@ -115,31 +106,7 @@ final class FeedbackApiMiddleware implements MiddlewareInterface
|
||||
/** @param array{uid:int,username:string,name:string,email:string} $user */
|
||||
private function state(array $user): ResponseInterface
|
||||
{
|
||||
$summary = $this->openSummary($user['uid'], false);
|
||||
return new JsonResponse([
|
||||
'allowed' => true,
|
||||
'user' => ['name' => $user['name'], 'email' => $user['email']],
|
||||
'recipients' => $this->recipientOptions(),
|
||||
'overviewUrl' => $this->overviewUrl(),
|
||||
'summary' => $summary === null ? null : $this->summaryPayload($summary),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor-facing URL of the feedback overview page (extension setting
|
||||
* overviewPid), resolved via the page slug. Empty string when unset.
|
||||
*/
|
||||
private function overviewUrl(): string
|
||||
{
|
||||
$pid = $this->settings->overviewPid();
|
||||
if ($pid <= 0) {
|
||||
return '';
|
||||
}
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
|
||||
$slug = $queryBuilder->select('slug')->from('pages')
|
||||
->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($pid, \Doctrine\DBAL\ParameterType::INTEGER)))
|
||||
->executeQuery()->fetchOne();
|
||||
return is_string($slug) ? $slug : '';
|
||||
return new JsonResponse($this->stateService()->payload($user));
|
||||
}
|
||||
|
||||
/** @param array{uid:int,username:string,name:string,email:string} $user */
|
||||
@@ -375,69 +342,16 @@ final class FeedbackApiMiddleware implements MiddlewareInterface
|
||||
/** @return array<string,mixed>|null */
|
||||
private function openSummary(int $userUid, bool $createIfMissing): ?array
|
||||
{
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable(self::T_SUMMARY);
|
||||
$summary = $connection->fetchAssociative(
|
||||
'SELECT * FROM ' . self::T_SUMMARY . " WHERE deleted = 0 AND status = 'open' AND fe_user = ? ORDER BY uid DESC",
|
||||
[$userUid]
|
||||
);
|
||||
if ($summary) {
|
||||
return $summary;
|
||||
}
|
||||
if (!$createIfMissing) {
|
||||
return null;
|
||||
}
|
||||
$connection->insert(self::T_SUMMARY, [
|
||||
'pid' => $this->settings->storagePid(),
|
||||
'tstamp' => time(),
|
||||
'crdate' => time(),
|
||||
'fe_user' => $userUid,
|
||||
'status' => 'open',
|
||||
]);
|
||||
return $this->openSummary($userUid, false);
|
||||
return $this->stateService()->openSummary($userUid, $createIfMissing);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $summary
|
||||
* @return array{uid:int,generalNotes:string,recipients:int[],items:array<int,array{uid:int,url:string,note:string,attachments:array<int,array{refUid:int,name:string,url:string}>}>}
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function summaryPayload(array $summary): array
|
||||
{
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable(self::T_FEEDBACK);
|
||||
$items = [];
|
||||
foreach ($connection->fetchAllAssociative(
|
||||
'SELECT uid, url, note FROM ' . self::T_FEEDBACK . ' WHERE deleted = 0 AND summary = ? ORDER BY sorting',
|
||||
[(int)$summary['uid']]
|
||||
) as $row) {
|
||||
$items[] = [
|
||||
'uid' => (int)$row['uid'],
|
||||
'url' => (string)$row['url'],
|
||||
'note' => (string)$row['note'],
|
||||
'attachments' => $this->attachments((int)$row['uid']),
|
||||
];
|
||||
}
|
||||
return [
|
||||
'uid' => (int)$summary['uid'],
|
||||
'generalNotes' => (string)($summary['general_notes'] ?? ''),
|
||||
'recipients' => GeneralUtility::intExplode(',', (string)($summary['recipients'] ?? ''), true),
|
||||
'items' => $items,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<int,array{refUid:int,name:string,url:string}> */
|
||||
private function attachments(int $feedbackUid): array
|
||||
{
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_file_reference');
|
||||
$rows = $connection->fetchAllAssociative(
|
||||
'SELECT r.uid AS ref_uid, f.name, f.identifier FROM sys_file_reference r
|
||||
JOIN sys_file f ON f.uid = r.uid_local
|
||||
WHERE r.deleted = 0 AND r.tablenames = ? AND r.fieldname = ? AND r.uid_foreign = ? ORDER BY r.uid',
|
||||
[self::T_FEEDBACK, 'attachments', $feedbackUid]
|
||||
);
|
||||
return array_map(static fn(array $r): array => [
|
||||
'refUid' => (int)$r['ref_uid'],
|
||||
'name' => (string)$r['name'],
|
||||
'url' => '/fileadmin' . $r['identifier'],
|
||||
], $rows);
|
||||
return $this->stateService()->summaryPayload($summary);
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> the feedback row - throws when not the user's */
|
||||
@@ -471,16 +385,6 @@ final class FeedbackApiMiddleware implements MiddlewareInterface
|
||||
return $this->settings->recipients();
|
||||
}
|
||||
|
||||
/** @return array<int,array{id:int,label:string}> */
|
||||
private function recipientOptions(): array
|
||||
{
|
||||
$options = [];
|
||||
foreach ($this->recipientMap() as $id => $recipient) {
|
||||
$options[] = ['id' => $id, 'label' => $recipient['label']];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private function jsonBody(ServerRequestInterface $request): array
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user