Files
VITEC-website/packages/fe_comment/Classes/Service/ExtensionSettings.php
T
orasche 872c6e715c fe_comment v2: FE-user feedback tickets with summary, uploads and API
- Replace the v1 submit endpoint with a full JSON API under
  /api/fe-comment/ (state, feedback create/update/delete, uploads,
  summary notes/recipients, submit), guarded by frontend login and a
  configurable FE user group; unauthorized callers get {"allowed":false}
- New models: tx_fecomment_summary (one open ticket per user, status
  open/sent) and tx_fecomment_feedback (url, RTE note, FAL attachments),
  both editable in the backend on the ticket storage page
- FeedbackApiMiddleware registered after frontend authentication (the
  frontend.user attribute does not exist earlier); uploads land in
  fileadmin/fe_comment/summary-<uid>/ with mime/extension check, 20 MB cap
- Submit sends one HTML mail (author block, items with attachment links,
  reply-to = FE user mail) to selected recipients with mailTo fallback,
  then closes the summary
- felogin wired React-compatible: felogin.pid=520 plus headless FeLogin
  JSON templates via site settings - felogin v14 and EXT:headless both
  claim templateRootPaths.10, the felogin.view.* settings feed exactly
  that index, so the structured JSON form (incl. __RequestToken) wins
- Read the sys_file_reference insert id before syncAttachmentCount():
  DBAL throws "no identity value" once the count UPDATE has run
- migrations: drop_fecomment_tables.php (repairs the no-uid tables,
  only drops when empty), check_feuser.php (login diagnosis),
  fec_login_test.sh / fec_api_test.sh (curl chains incl. CSRF token flow)
2026-10-02 16:37:01 +02:00

108 lines
3.2 KiB
PHP

<?php
declare(strict_types=1);
namespace Evomedien\FeComment\Service;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Typed access to the extension configuration (Settings > Extension
* Configuration > fe_comment). Every accessor is failure-tolerant: an
* unconfigured extension behaves like "everything empty, headless mode".
*/
final class ExtensionSettings
{
/** @var array<string,mixed> */
private array $settings;
public function __construct()
{
try {
$this->settings = (array)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('fe_comment');
} catch (\Throwable) {
$this->settings = [];
}
}
/** @return string[] */
public function mailTo(): array
{
return $this->addressList('mailTo');
}
/** @return string[] */
public function mailCc(): array
{
return $this->addressList('mailCc');
}
/** @return string[] */
public function mailBcc(): array
{
return $this->addressList('mailBcc');
}
public function subject(): string
{
$subject = trim((string)($this->settings['subject'] ?? ''));
return $subject !== '' ? $subject : 'Page feedback';
}
/** Master switch: off serves an empty widget and rejects submissions. */
public function enabled(): bool
{
return (bool)($this->settings['enabled'] ?? true);
}
/** FE user group required to use the widget; 0 = any logged-in user. */
public function feUserGroup(): int
{
return (int)($this->settings['feUserGroup'] ?? 0);
}
/** Storage pid for the feedback records (backend visibility). */
public function storagePid(): int
{
return (int)($this->settings['storagePid'] ?? 0);
}
/**
* Selectable recipients, parsed from "Label <mail>; Label2 <mail2>".
* The index doubles as the id the widget sends back.
*
* @return array<int,array{label:string,email:string}>
*/
public function recipients(): array
{
$recipients = [];
foreach (GeneralUtility::trimExplode(';', (string)($this->settings['recipients'] ?? ''), true) as $entry) {
if (preg_match('/^(.*)<([^>]+)>\s*$/', $entry, $m) && GeneralUtility::validEmail(trim($m[2]))) {
$recipients[] = ['label' => trim($m[1]) ?: trim($m[2]), 'email' => trim($m[2])];
} elseif (GeneralUtility::validEmail(trim($entry))) {
$recipients[] = ['label' => trim($entry), 'email' => trim($entry)];
}
}
return $recipients;
}
/** true = headless: TYPO3 injects nothing, the front end embeds the widget itself. */
public function jsonOutput(): bool
{
return (bool)($this->settings['jsonOutput'] ?? true);
}
/** @return string[] valid mail addresses only */
private function addressList(string $key): array
{
$addresses = [];
foreach (GeneralUtility::trimExplode(',', (string)($this->settings[$key] ?? ''), true) as $address) {
if (GeneralUtility::validEmail($address)) {
$addresses[] = $address;
}
}
return $addresses;
}
}