00478e2466
Admin-facing simplification:
- Dropped separate \"lookahead\" and \"historical lookahead\" tree
prefs (and the once-per-month historical gate). A single
\"send every N days\" number now drives both the cron cadence
and the window each issue looks ahead for living + deceased
events.
- Default 14, range 1–90, applies uniformly.
User-facing addition:
- The /my-account/{tree} subscription card gained an \"Email
frequency\" select with options: use site default, weekly,
every 2 weeks, monthly, every 2 months, quarterly. Stored as
a per-tree-per-user preference.
- Dispatch now checks each recipient's own cadence against
their own last-sent timestamp. Admin-added external addresses
with no webtrees account always receive every run (no
per-user state).
- Newsletter footer now reads \"You can change how often you
receive this email, or unsubscribe entirely, in the Newsletter
subscription section on your My account page\" — true now
that the control exists.
German translations updated for the new strings; stale ones
removed.
238 lines
12 KiB
PHTML
238 lines
12 KiB
PHTML
<?php
|
||
|
||
/**
|
||
* Customised version of webtrees' built-in edit-account-page view.
|
||
*
|
||
* Adds a "Newsletter subscription" fieldset that lets each user opt
|
||
* in or out per tree. The accompanying form field is processed by
|
||
* EmailNewsletter\Http\AccountUpdateDecorator after the built-in
|
||
* AccountUpdate handler has run.
|
||
*
|
||
* Keep the rest of this file in sync with the upstream view from
|
||
* webtrees 2.2 — resources/views/edit-account-page.phtml.
|
||
*/
|
||
|
||
declare(strict_types=1);
|
||
|
||
use EmailNewsletter\Configuration;
|
||
use Fisharebest\Webtrees\Contracts\UserInterface;
|
||
use Fisharebest\Webtrees\Http\RequestHandlers\AccountDelete;
|
||
use Fisharebest\Webtrees\I18N;
|
||
use Fisharebest\Webtrees\Individual;
|
||
use Fisharebest\Webtrees\Tree;
|
||
|
||
/**
|
||
* @var array<string,string> $contact_methods
|
||
* @var Individual|null $default_individual
|
||
* @var array<string,string> $languages
|
||
* @var Individual|null $my_individual_record
|
||
* @var bool $show_delete_option
|
||
* @var array<string,string> $timezones
|
||
* @var string $title
|
||
* @var Tree|null $tree
|
||
* @var UserInterface $user
|
||
*/
|
||
|
||
?>
|
||
|
||
<h2 class="wt-page-title">
|
||
<?= $title ?>
|
||
</h2>
|
||
|
||
<form method="post" class="wt-page-options wt-page-options-my-account">
|
||
<div class="row">
|
||
<label class="col-sm-3 col-form-label wt-page-options-label" for="user-name">
|
||
<?= I18N::translate('Username') ?>
|
||
</label>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<input type="text" class="form-control" id="user-name" name="user_name" value="<?= e($user->userName()) ?>" dir="auto" aria-describedby="username-description" required="required">
|
||
<div class="form-text" id="username-description">
|
||
<?= I18N::translate('Usernames are case-insensitive and ignore accented letters, so that “chloe”, “chloë”, and “Chloe” are considered to be the same.') ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<label class="col-sm-3 col-form-label wt-page-options-label" for="real-name">
|
||
<?= I18N::translate('Real name') ?>
|
||
</label>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<input type="text" class="form-control" id="real-name" name="real_name" value="<?= e($user->realName()) ?>" dir="auto" aria-describedby="real-name-description" required="required">
|
||
<div class="form-text" id="real-name-description">
|
||
<?= I18N::translate('This is your real name, as you would like it displayed on screen.') ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if ($tree instanceof Tree) : ?>
|
||
<div class="row">
|
||
<label class="col-sm-3 col-form-label wt-page-options-label" for="gedcom-id">
|
||
<?= I18N::translate('Individual record') ?>
|
||
</label>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<select class="form-select" id="gedcom-id" aria-describedby="gedcom-id-description" disabled>
|
||
<?php if ($my_individual_record !== null) : ?>
|
||
<option value=""><?= $my_individual_record->fullName() ?></option>
|
||
<?php else : ?>
|
||
<option value=""><?= I18N::translateContext('unknown people', 'Unknown') ?></option>
|
||
<?php endif ?>
|
||
</select>
|
||
<div class="form-text" id="gedcom-id-description">
|
||
<?= I18N::translate('This is a link to your own record in the family tree. If this is the wrong individual, contact an administrator.') ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<label class="col-sm-3 col-form-label wt-page-options-label" for="default-xref">
|
||
<?= I18N::translate('Default individual') ?>
|
||
</label>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<?= view('components/select-individual', ['name' => 'default-xref', 'id' => 'default-xref', 'individual' => $default_individual, 'tree' => $tree]) ?>
|
||
<div class="form-text" id="default-xref-description">
|
||
<?= I18N::translate('This individual will be selected by default when viewing charts and reports.') ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endif ?>
|
||
|
||
<div class="row">
|
||
<label class="col-sm-3 col-form-label wt-page-options-label" for="password">
|
||
<?= I18N::translate('Password') ?>
|
||
</label>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<input class="form-control" type="password" id="password" name="password" aria-describedby="password-description" autocomplete="new-password" data-wt-show-password-text="<?= e(I18N::translate('show')) ?>" data-wt-show-password-title="<?= e(I18N::translate('Show password')) ?>" data-wt-hide-password-text="<?= e(I18N::translate('hide')) ?>" data-wt-hide-password-title="<?= e(I18N::translate('Hide password')) ?>">
|
||
<div class="form-text" id="password-description">
|
||
<?= I18N::translate('Passwords must be at least 8 characters long and are case-sensitive, so that “secret” is different from “SECRET”.') ?>
|
||
<br>
|
||
<?= I18N::translate('Leave the password blank if you want to keep the current password.') ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<label class="col-sm-3 col-form-label wt-page-options-label" for="language">
|
||
<?= I18N::translate('Language') ?>
|
||
</label>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<?= view('components/select', ['name' => 'language', 'selected' => $user->getPreference(UserInterface::PREF_LANGUAGE, 'en-US'), 'options' => $languages]) ?>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<label class="col-sm-3 col-form-label wt-page-options-label" for="timezone">
|
||
<?= I18N::translate('Time zone') ?>
|
||
</label>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<?= view('components/select', ['name' => 'timezone', 'selected' => $user->getPreference(UserInterface::PREF_TIME_ZONE, 'UTC'), 'options' => $timezones]) ?>
|
||
<div class="form-text" id="timezone-description">
|
||
<?= I18N::translate('The time zone is required for date calculations, such as knowing today’s date.') ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<label class="col-sm-3 col-form-label wt-page-options-label" for="email">
|
||
<?= I18N::translate('Email address') ?>
|
||
</label>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<input class="form-control" type="email" id="email" name="email" value="<?= e($user->email()) ?>" aria-describedby="email-description">
|
||
<div class="form-text" id="email-description">
|
||
<?= I18N::translate('This email address will be used to send password reminders, website notifications, and messages from other family members who are registered on the website.') ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<label class="col-sm-3 col-form-label wt-page-options-label" for="contact-method">
|
||
<?= I18N::translate('Contact method') ?>
|
||
</label>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<?= view('components/select', ['name' => 'contact-method', 'id' => 'contact-method', 'selected' => $user->getPreference(UserInterface::PREF_CONTACT_METHOD), 'options' => $contact_methods]) ?>
|
||
<div class="form-text" id="contact-method-description">
|
||
<?= I18N::translate('Site members can send each other messages. You can choose to how these messages are sent to you, or choose not receive them at all.') ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<fieldset class="row">
|
||
<legend class="col-sm-3 col-form-label wt-page-options-label">
|
||
<?= I18N::translate('Visible online') ?>
|
||
</legend>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<?= view('components/checkbox', ['label' => I18N::translate('Visible to other users when online'), 'name' => 'visible-online', 'checked' => (bool) $user->getPreference(UserInterface::PREF_IS_VISIBLE_ONLINE)]) ?>
|
||
<div class="form-text" id="visible-online-description">
|
||
<?= I18N::translate('You can choose whether to appear in the list of users who are currently signed-in.') ?>
|
||
</div>
|
||
</div>
|
||
</fieldset>
|
||
|
||
<?php if ($tree instanceof Tree && Configuration::isEnabled($tree)) : ?>
|
||
<fieldset class="row">
|
||
<legend class="col-sm-3 col-form-label wt-page-options-label">
|
||
<?= I18N::translate('Newsletter subscription') ?>
|
||
</legend>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<?= view('components/checkbox', [
|
||
'label' => I18N::translate('Subscribe to the newsletter'),
|
||
'name' => 'newsletter_subscribed',
|
||
'checked' => $tree->getUserPreference($user, Configuration::USER_PREF_SUBSCRIBED) === '1',
|
||
]) ?>
|
||
<div class="form-text">
|
||
<?= I18N::translate('You will receive a periodic email with upcoming birthdays and other family events from %s.', e($tree->title())) ?>
|
||
</div>
|
||
|
||
<?php
|
||
$current_freq = Configuration::userFrequencyDays($tree, $user);
|
||
$tree_freq = Configuration::frequencyDays($tree);
|
||
$freq_labels = [
|
||
0 => I18N::translate('Use site default (every %d days)', $tree_freq),
|
||
7 => I18N::translate('Weekly'),
|
||
14 => I18N::translate('Every 2 weeks'),
|
||
30 => I18N::translate('Monthly'),
|
||
60 => I18N::translate('Every 2 months'),
|
||
90 => I18N::translate('Quarterly'),
|
||
];
|
||
?>
|
||
<div class="mt-3">
|
||
<label for="newsletter_frequency" class="form-label">
|
||
<?= I18N::translate('Email frequency') ?>
|
||
</label>
|
||
<select class="form-select" id="newsletter_frequency"
|
||
name="newsletter_frequency" style="max-width: 22rem;">
|
||
<?php foreach ($freq_labels as $days => $label) : ?>
|
||
<option value="<?= $days ?>" <?= $days === $current_freq ? 'selected' : '' ?>>
|
||
<?= e($label) ?>
|
||
</option>
|
||
<?php endforeach ?>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</fieldset>
|
||
<?php endif ?>
|
||
|
||
<div class="row mb-3">
|
||
<div class="col-sm-3 wt-page-options-label"></div>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<button type="submit" class="btn btn-primary">
|
||
<?= view('icons/save') ?>
|
||
<?= I18N::translate('save') ?>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<?= csrf_field() ?>
|
||
</form>
|
||
|
||
<?php if ($show_delete_option) : ?>
|
||
<div class="row mb-3">
|
||
<div class="col-sm-3 wt-page-options-label"></div>
|
||
<div class="col-sm-9 wt-page-options-value">
|
||
<a href="#" class="btn btn-danger" data-wt-confirm="<?= I18N::translate('Are you sure you want to delete “%s”?', e($user->userName())) ?>" data-wt-post-url="<?= e(route(AccountDelete::class)) ?>">
|
||
<?= view('icons/delete') ?>
|
||
<?= I18N::translate('Delete your account') ?>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
<?php endif ?>
|