fe_comment: recipients are the FE users of the feedback group
- The selectable recipients are no longer a hand-maintained setting but
every active frontend user of the feedback group (feUserGroup) with a
valid e-mail address; the id is the fe_users uid, so a stored selection
survives users joining or leaving. The `recipients` setting is marked
unused
- Each recipient in the state payload carries label, email and selected
(the stored choice), for a checkbox list with "select all" above the
submit button
- submit accepts the checked ids directly ({"recipients": [uids]}),
validates them against the group, stores and uses them, and reports
the addresses used as sentTo; no selection falls back to mailTo
- migrations/check_fecomment_user.php: all summaries and items of one
user incl. deleted ones, for diagnosis
This commit is contained in:
1 parent
eb0d0d9ebc
commit
74502e60a3
5 files changed
+114
-14
No files matched your search
@@ -76,7 +76,7 @@ final class FeedbackState
|
||||
return [
|
||||
'allowed' => true,
|
||||
'user' => ['name' => $user['name'], 'email' => $user['email']],
|
||||
'recipients' => $this->recipientOptions(),
|
||||
'recipients' => $this->recipientOptions($summaryPayload['recipients'] ?? []),
|
||||
'overviewUrl' => $this->overviewUrl(),
|
||||
'summary' => $summaryPayload,
|
||||
// the submit button may be active (the endpoint rejects an empty summary)
|
||||
@@ -91,7 +91,7 @@ final class FeedbackState
|
||||
* bodies except `upload` (multipart: file + feedback).
|
||||
*/
|
||||
private const ACTIONS = [
|
||||
'submit' => ['method' => 'POST', 'url' => '/api/fe-comment/submit'],
|
||||
'submit' => ['method' => 'POST', 'url' => '/api/fe-comment/submit', 'body' => ['recipients' => 'int[] (optional, the checked recipient ids)']],
|
||||
'updateSummary' => ['method' => 'POST', 'url' => '/api/fe-comment/summary', 'body' => ['generalNotes' => 'html', 'recipients' => 'int[]']],
|
||||
'createItem' => ['method' => 'POST', 'url' => '/api/fe-comment/feedback', 'body' => ['url' => 'string', 'note' => 'html']],
|
||||
'updateItem' => ['method' => 'POST', 'url' => '/api/fe-comment/feedback/{uid}', 'body' => ['url' => 'string', 'note' => 'html']],
|
||||
@@ -168,12 +168,63 @@ final class FeedbackState
|
||||
], $rows);
|
||||
}
|
||||
|
||||
/** @return array<int,array{id:int,label:string}> */
|
||||
private function recipientOptions(): array
|
||||
/**
|
||||
* Who a summary can be sent to: every active FE user of the feedback
|
||||
* group (extension setting feUserGroup) with an e-mail address - the
|
||||
* same group that may use the tool (decision Olli 2026-10-09). Keyed by
|
||||
* fe_users uid, which doubles as the id the front end sends back, so a
|
||||
* selection stays valid while users join or leave. Group 0 = nobody
|
||||
* (never all frontend users).
|
||||
*
|
||||
* @return array<int,array{label:string,email:string}> fe_users uid => recipient
|
||||
*/
|
||||
public function recipientMap(): array
|
||||
{
|
||||
$group = $this->settings->feUserGroup();
|
||||
if ($group <= 0) {
|
||||
return [];
|
||||
}
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('fe_users');
|
||||
$rows = $queryBuilder->select('uid', 'username', 'name', 'email')->from('fe_users')
|
||||
->where(
|
||||
$queryBuilder->expr()->inSet('usergroup', (string)$group),
|
||||
$queryBuilder->expr()->neq('email', $queryBuilder->createNamedParameter(''))
|
||||
)
|
||||
->orderBy('name')->addOrderBy('username')
|
||||
->executeQuery()->fetchAllAssociative();
|
||||
$map = [];
|
||||
foreach ($rows as $row) {
|
||||
$email = trim((string)$row['email']);
|
||||
if (!GeneralUtility::validEmail($email)) {
|
||||
continue;
|
||||
}
|
||||
$map[(int)$row['uid']] = [
|
||||
'label' => trim((string)$row['name']) ?: (string)$row['username'],
|
||||
'email' => $email,
|
||||
];
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* The recipients for the checkbox list above the submit button.
|
||||
* `selected` mirrors the summary's stored choice, so checkboxes survive
|
||||
* a reload. Addresses are shown on purpose: only logged-in members of
|
||||
* the feedback group ever see this payload.
|
||||
*
|
||||
* @param int[] $selectedIds
|
||||
* @return array<int,array{id:int,label:string,email:string,selected:bool}>
|
||||
*/
|
||||
private function recipientOptions(array $selectedIds): array
|
||||
{
|
||||
$options = [];
|
||||
foreach ($this->settings->recipients() as $id => $recipient) {
|
||||
$options[] = ['id' => $id, 'label' => $recipient['label']];
|
||||
foreach ($this->recipientMap() as $id => $recipient) {
|
||||
$options[] = [
|
||||
'id' => $id,
|
||||
'label' => $recipient['label'],
|
||||
'email' => $recipient['email'],
|
||||
'selected' => in_array($id, $selectedIds, true),
|
||||
];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user