Admin user roster; per-locale subject; SiteUser as From
- Admin preferences page can now subscribe existing webtrees users per tree, not just external addresses. - Subject prefix is now configurable per locale (en/de), and the date in the subject is formatted via IntlDateFormatter in the recipient's locale. - "From:" header now uses SiteUser (SMTP_FROM_NAME/SMTP_DISP_NAME) to match webtrees' own system-mail convention; the tree contact becomes the Reply-To.
This commit is contained in:
+55
-2
@@ -28,6 +28,8 @@ use Fisharebest\Webtrees\Module\ModuleCustomTrait;
|
||||
use Fisharebest\Webtrees\Module\ModuleMenuInterface;
|
||||
use Fisharebest\Webtrees\Module\ModuleMenuTrait;
|
||||
use Fisharebest\Webtrees\Services\TreeService;
|
||||
use Fisharebest\Webtrees\Services\UserService;
|
||||
use Fisharebest\Webtrees\User;
|
||||
use Fisharebest\Webtrees\Tree;
|
||||
use Fisharebest\Webtrees\Validator;
|
||||
use Fisharebest\Webtrees\View;
|
||||
@@ -45,6 +47,7 @@ class Module extends AbstractModule implements ModuleCustomInterface, ModuleConf
|
||||
public function __construct(
|
||||
private readonly NewsletterDispatchService $dispatch_service,
|
||||
private readonly TreeService $tree_service,
|
||||
private readonly UserService $user_service,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -160,6 +163,13 @@ class Module extends AbstractModule implements ModuleCustomInterface, ModuleConf
|
||||
=> 'Richten Sie System-Cron, systemd-Timer oder einen externen Scheduler so ein, dass er die untenstehende URL aufruft. Der Versandplan entscheidet, wann tatsächlich gesendet wird — häufiger aufrufen ist unbedenklich.',
|
||||
'Send the newsletter now for every enabled tree?'
|
||||
=> 'Newsletter jetzt für jeden aktivierten Baum senden?',
|
||||
'Subscribed users' => 'Abonnierte Nutzer',
|
||||
'No users with email addresses found.' => 'Keine Nutzer mit E-Mail-Adresse gefunden.',
|
||||
'Tick a user to subscribe them to this tree’s newsletter. Users can still adjust their own subscription on their account page.'
|
||||
=> 'Setzen Sie einen Haken, um den Nutzer für den Newsletter dieses Stammbaums zu abonnieren. Nutzer können ihr Abonnement weiterhin selbst auf ihrer Kontoseite anpassen.',
|
||||
'Prepended to the email subject line. Leave a field blank to fall back to the generic prefix below.'
|
||||
=> 'Wird der E-Mail-Betreffzeile vorangestellt. Ein leeres Feld greift auf das generische Präfix unten zurück.',
|
||||
'Generic' => 'Allgemein',
|
||||
],
|
||||
'nl' => [
|
||||
'Email Newsletter' => 'E-mailnieuwsbrief',
|
||||
@@ -219,10 +229,20 @@ class Module extends AbstractModule implements ModuleCustomInterface, ModuleConf
|
||||
{
|
||||
$this->layout = 'layouts/administration';
|
||||
|
||||
// Surface every webtrees user with an email so the admin can
|
||||
// toggle subscription per tree without having to ask each
|
||||
// member to opt in themselves. Sorted alphabetically by real
|
||||
// name so the list stays scannable in long member rosters.
|
||||
$users = $this->user_service->all()
|
||||
->filter(static fn (User $user): bool => trim($user->email()) !== '')
|
||||
->sortBy(static fn (User $user): string => mb_strtolower($user->realName()))
|
||||
->values();
|
||||
|
||||
return $this->viewResponse($this->name() . '::admin', [
|
||||
'title' => I18N::translate('Email Newsletter') . ' — ' . I18N::translate('Preferences'),
|
||||
'module' => $this,
|
||||
'all_trees' => $this->tree_service->all(),
|
||||
'all_users' => $users,
|
||||
'cron_token' => $this->cronToken(),
|
||||
'cron_url' => $this->cronUrl(),
|
||||
]);
|
||||
@@ -230,6 +250,10 @@ class Module extends AbstractModule implements ModuleCustomInterface, ModuleConf
|
||||
|
||||
public function postAdminAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
// Cache the user list so we don't query it once per tree.
|
||||
$users = $this->user_service->all()
|
||||
->filter(static fn (User $user): bool => trim($user->email()) !== '');
|
||||
|
||||
foreach ($this->tree_service->all() as $tree) {
|
||||
$id = $tree->id();
|
||||
$enabled = Validator::parsedBody($request)->string('enabled-' . $id, '0') === '1';
|
||||
@@ -249,8 +273,37 @@ class Module extends AbstractModule implements ModuleCustomInterface, ModuleConf
|
||||
$tree->setPreference(Configuration::PREF_INCLUDE_ANNIVERSARIES, $annivs ? '1' : '0');
|
||||
$tree->setPreference(Configuration::PREF_EXTRA_RECIPIENTS, $extras);
|
||||
|
||||
if ($subject !== '') {
|
||||
$tree->setPreference(Configuration::PREF_SUBJECT_PREFIX, $subject);
|
||||
// Generic prefix — used when no per-locale override is set.
|
||||
// We always write it (even empty) so admins can clear a
|
||||
// previously-saved value.
|
||||
$tree->setPreference(Configuration::PREF_SUBJECT_PREFIX, $subject);
|
||||
|
||||
foreach (array_keys(Configuration::supportedSubjectLocales()) as $code) {
|
||||
$locale_prefix = Validator::parsedBody($request)
|
||||
->string('subject-' . $id . '-' . $code, '');
|
||||
|
||||
Configuration::setSubjectPrefixForLocale($tree, $code, $locale_prefix);
|
||||
}
|
||||
|
||||
// Per-user subscription toggles. A users-roster marker is
|
||||
// always submitted (hidden field "users-submitted-<id>")
|
||||
// so we can tell an unchecked-everyone POST apart from a
|
||||
// legacy form that omits the section entirely — we only
|
||||
// touch subscriptions when the marker is present.
|
||||
$roster_present = Validator::parsedBody($request)
|
||||
->string('users-submitted-' . $id, '0') === '1';
|
||||
|
||||
if ($roster_present) {
|
||||
foreach ($users as $user) {
|
||||
$field = 'subscribe-' . $id . '-' . $user->id();
|
||||
$subscribed = Validator::parsedBody($request)->string($field, '0') === '1';
|
||||
|
||||
$tree->setUserPreference(
|
||||
$user,
|
||||
Configuration::USER_PREF_SUBSCRIBED,
|
||||
$subscribed ? '1' : '0',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user