includes/modals/settings_modal.php

<?php
/*
|--------------------------------------------------------------------------
| Settings modal
|--------------------------------------------------------------------------
|
| Renders the settings form pre-filled from brivacia_public_settings()
| (sanitized, secrets stripped — see includes/settings.php). Site rows use
| the same repeatable-fieldset + index pattern as the install wizard;
| main.js handles add/remove/reindex and the submit-to-JSON logic.
|
*/
require_once __DIR__ . '/../settings.php';

$options = function_exists('brivacia_public_settings')
    ? brivacia_public_settings()
    : [];

$sites = $options['sites'] ?? [];
$twoFactorEnabled = !empty($options['admin']['two_factor']['enabled']);
$i = 0;
?>

<dialog class="modal" id="settings-modal">
    <div>
        <h2><?= h(t('settings.title')) ?></h2>
        <p><?= h(t('settings.description')) ?></p>

        <form id="settings-form">
            <section id="settings-sites">
                <h3><?= h(t('settings.tracked_sites.title')) ?></h3>
                <p><?= h(t('settings.tracked_sites.help')) ?></p>

                <div>
                    <?php foreach ($sites as $code => $domain): ?>
                        <fieldset>
                            <label>
                                <?= h(t('settings.tracked_sites.code')) ?>
                                <small><?= h(t('settings.tracked_sites.code.help')) ?></small>
                                <input name="sites[<?= $i ?>][code]" placeholder="<?= h(t('settings.tracked_sites.code.placeholder')) ?>" required type="text" value="<?= h((string)$code) ?>">
                            </label>

                            <label>
                                <?= h(t('settings.tracked_sites.domain')) ?>
                                <small><?= h(t('settings.tracked_sites.domain.help')) ?></small>
                                <input name="sites[<?= $i ?>][domain]" placeholder="<?= h(t('settings.tracked_sites.domain.placeholder')) ?>" required type="text" value="<?= h((string)$domain) ?>">
                            </label>

                            <button type="button" value="remove-site">
                                <?= icon('trash') ?> <?= h(t('settings.tracked_sites.remove')) ?>
                            </button>
                        </fieldset>
                        <?php $i++; ?>
                    <?php endforeach; ?>
                </div>

                <button type="button" value="add-site">
                    <?= h(t('settings.tracked_sites.add')) ?>
                </button>

                <template id="settings-site-template">
                    <fieldset>
                        <label>
                            <?= h(t('settings.tracked_sites.code')) ?>
                            <small><?= h(t('settings.tracked_sites.code.help')) ?></small>
                            <input name="sites[__INDEX__][code]" placeholder="<?= h(t('settings.tracked_sites.code.placeholder')) ?>" required type="text" value="">
                        </label>

                        <label>
                            <?= h(t('settings.tracked_sites.domain')) ?>
                            <small><?= h(t('settings.tracked_sites.domain.help')) ?></small>
                            <input name="sites[__INDEX__][domain]" placeholder="<?= h(t('settings.tracked_sites.domain.placeholder')) ?>" required type="text" value="">
                        </label>

                        <button type="button" value="remove-site">
                            <?= icon('trash') ?>  <?= h(t('settings.tracked_sites.remove')) ?>
                        </button>
                    </fieldset>
                </template>
            </section>

            <section>
                <h3><?= h(t('settings.privacy.title')) ?></h3>
                <p><?= h(t('settings.privacy.help')) ?></p>

                <?php $provider = (string)($options['privacy']['country_provider'] ?? 'none'); ?>
                <label>
                    <?= h(t('settings.privacy.country_provider')) ?>
                    <small><?= h(t('settings.privacy.country_provider.help')) ?></small>
                    <span>
                        <select name="privacy.country_provider">
                            <option value="none" <?= $provider === 'none' ? 'selected' : '' ?>><?= h(t('settings.privacy.country_provider.none')) ?></option>
                            <option value="blurloc" <?= $provider === 'blurloc' ? 'selected' : '' ?>><?= h(t('settings.privacy.country_provider.blurloc')) ?></option>
                            <option value="cloudflare" <?= $provider === 'cloudflare' ? 'selected' : '' ?>><?= h(t('settings.privacy.country_provider.cloudflare')) ?></option>
                        </select>
                    </span>
                </label>

                <?php $octets = (int)($options['privacy']['ip_prefix_octets'] ?? 2); ?>
                <label>
                    <?= h(t('settings.privacy.ip_prefix_octets')) ?>
                    <small><?= h(t('settings.privacy.ip_prefix_octets.help')) ?></small>
                    <span>
                        <select name="privacy.ip_prefix_octets">
                            <option value="3" <?= $octets === 3 ? 'selected' : '' ?>><?= h(t('settings.privacy.ip_prefix_octets.3')) ?></option>
                            <option value="2" <?= $octets === 2 ? 'selected' : '' ?>><?= h(t('settings.privacy.ip_prefix_octets.2')) ?></option>
                        </select>
                    </span>
                </label>
            </section>

            <section>
                <h3><?= h(t('settings.admin.title')) ?></h3>
                <p><?= h(t('settings.admin.help')) ?></p>

                <label>
                    <?= h(t('settings.admin.ignore_cookie_years')) ?>
                    <small><?= h(t('settings.admin.ignore_cookie_years.help')) ?></small>
                    <input name="admin.ignore_cookie_years" type="number" min="1" max="20" value="<?= h((int)($options['admin']['ignore_cookie_years'] ?? 5)) ?>">
                </label>

                <div data-confirm-disable="<?= h(t('settings.two_factor.disable.confirm')) ?>" data-error="<?= h(t('settings.two_factor.error')) ?>" data-invalid-code="<?= h(t('settings.two_factor.invalid_code')) ?>" data-manual-hide="<?= h(t('settings.two_factor.manual.hide')) ?>" data-manual-show="<?= h(t('settings.two_factor.manual.show')) ?>" data-network-error="<?= h(t('settings.messages.network_error')) ?>" data-setup="<?= h(t('settings.two_factor.setup.loading')) ?>" data-status-enabled="<?= h(t('settings.two_factor.status.enabled')) ?>" data-success="<?= h(t('settings.two_factor.enabled')) ?>" id="two-factor-settings">
                    <h4><?= h(t('settings.two_factor.title')) ?></h4>
                    <p><?= h(t('settings.two_factor.help')) ?></p>
                    <p>
                        <?= h(t('settings.two_factor.status.label')) ?>
                        <span class="<?= $twoFactorEnabled ? 'success' : 'danger' ?>">
                            <?= h($twoFactorEnabled ? t('settings.two_factor.status.enabled') : t('settings.two_factor.status.disabled')) ?>
                        </span>
                    </p>

                    <?php if (!$twoFactorEnabled): ?>
                        <button type="button" value="two-factor-setup">
                            <?= h(t('settings.two_factor.setup.button')) ?>
                        </button>

                        <div hidden>
                            <p><?= h(t('settings.two_factor.setup.instructions')) ?></p>

                            <div hidden></div>

                            <button aria-expanded="false" type="button" value="two-factor-toggle-manual">
                                <?= h(t('settings.two_factor.manual.show')) ?>
                            </button>

                            <div hidden>
                                <label>
                                    <?= h(t('settings.two_factor.secret')) ?>
                                    <input readonly type="text">
                                </label>
                            </div>

                            <label>
                                <?= h(t('settings.two_factor.verify_code')) ?>
                                <small><?= h(t('settings.two_factor.verify_code.help')) ?></small>
                                <input autocomplete="one-time-code" inputmode="numeric" name="two_factor_code" spellcheck="false" type="text">
                            </label>

                            <button type="button" value="two-factor-enable">
                                <?= h(t('settings.two_factor.enable.button')) ?>
                            </button>
                        </div>

                        <div hidden>
                            <p><?= h(t('settings.two_factor.backup.instructions')) ?></p>
                            <textarea readonly rows="6"></textarea>
                            <button type="button" value="two-factor-download-backup">
                                <?= h(t('settings.two_factor.backup.download')) ?>
                            </button>
                        </div>
                    <?php else: ?>
                        <button type="button" value="two-factor-disable">
                            <?= h(t('settings.two_factor.disable.button')) ?>
                        </button>

                        <div hidden>
                            <label>
                                <?= h(t('settings.two_factor.disable.password')) ?>
                                <small><?= h(t('settings.two_factor.disable.password.help')) ?></small>
                                <input autocapitalize="off" autocomplete="current-password" autocorrect="off" name="two_factor_disable_password" spellcheck="false" type="password">
                            </label>

                            <button type="button" value="two-factor-disable-confirm">
                                <?= h(t('settings.two_factor.disable.confirm.button')) ?>
                            </button>
                        </div>
                    <?php endif; ?>

                    <p role="status"></p>
                </div>
            </section>

            <section>
                <h3><?= h(t('settings.dashboard.title')) ?></h3>
                <p><?= h(t('settings.instance.help')) ?></p>

                <label>
                    <?= h(t('settings.instance.name')) ?>
                    <small><?= h(t('settings.instance.name.help')) ?></small>
                    <input name="dashboard.instance_name" placeholder="<?= h(t('settings.instance.name.placeholder')) ?>" required type="text" value="<?= h((string)($options['dashboard']['instance_name'] ?? 'Brivacia')) ?>">
                </label>

                <p><?= h(t('settings.dashboard.help')) ?></p>

                <label>
                    <?= h(t('settings.dashboard.auto_refresh')) ?>
                    <small><?= h(t('settings.dashboard.auto_refresh.help')) ?></small>
                    <input name="dashboard.auto_refresh" type="number" min="0" max="1440" value="<?= h((int)($options['dashboard']['auto_refresh'] ?? 1)) ?>">
                </label>

                <label>
                    <input name="dashboard.light_theme" type="checkbox" <?= !empty($options['dashboard']['light_theme']) ? 'checked' : '' ?>>
                    <?= h(t('settings.dashboard.light_theme')) ?>
                    <small><?= h(t('settings.dashboard.light_theme.help')) ?></small>
                </label>

                <label>
                    <input name="dashboard.show_external_icon_in_top_pages" type="checkbox" <?= !empty($options['dashboard']['show_external_icon_in_top_pages']) ? 'checked' : '' ?>>
                    <?= h(t('settings.dashboard.show_external_icon_in_top_pages')) ?>
                    <small><?= h(t('settings.dashboard.show_external_icon_in_top_pages.help')) ?></small>
                </label>

                <label>
                    <input name="dashboard.show_page_language_in_top_pages" type="checkbox" <?= !empty($options['dashboard']['show_page_language_in_top_pages']) ? 'checked' : '' ?>>
                    <?= h(t('settings.dashboard.show_page_language_in_top_pages')) ?>
                    <small><?= h(t('settings.dashboard.show_page_language_in_top_pages.help')) ?></small>
                </label>
            </section>

            <section id="settings-trends">
                <h3><?= h(t('settings.trends.title')) ?></h3>

                <p><?= h(t('settings.trends.help')) ?></p>
                <label>
                    <input name="trends.visitors" type="checkbox" <?= !empty($options['trends']['visitors']) ? 'checked' : '' ?>>
                    <?= h(t('settings.trends.visitors')) ?>
                    <small><?= h(t('settings.trends.visitors.help')) ?></small>
                </label>

                <label>
                    <input name="trends.visits" type="checkbox" <?= !empty($options['trends']['visits']) ? 'checked' : '' ?>>
                    <?= h(t('settings.trends.visits')) ?>
                    <small><?= h(t('settings.trends.visits.help')) ?></small>
                </label>

                <label>
                    <input name="trends.pageviews" type="checkbox" <?= !empty($options['trends']['pageviews']) ? 'checked' : '' ?>>
                    <?= h(t('settings.trends.pageviews')) ?>
                    <small><?= h(t('settings.trends.pageviews.help')) ?></small>
                </label>

                <?php $trendSensitivity = (string)($options['trends']['sensitivity'] ?? 'normal'); ?>
                <label>
                    <?= h(t('settings.trends.sensitivity')) ?>
                    <small><?= h(t('settings.trends.sensitivity.help')) ?></small>
                    <select name="trends.sensitivity">
                        <option value="sensitive" <?= $trendSensitivity === 'sensitive' ? 'selected' : '' ?>><?= h(t('settings.trends.sensitivity.sensitive')) ?></option>
                        <option value="normal" <?= $trendSensitivity === 'normal' ? 'selected' : '' ?>><?= h(t('settings.trends.sensitivity.normal')) ?></option>
                        <option value="cautious" <?= $trendSensitivity === 'cautious' ? 'selected' : '' ?>><?= h(t('settings.trends.sensitivity.cautious')) ?></option>
                    </select>
                </label>

                <?php $trendPlaceholderStyle = (string)($options['trends']['placeholder_style'] ?? 'padawan'); ?>
                <label>
                    <?= h(t('settings.trends.placeholder_style')) ?>
                    <small><?= h(t('settings.trends.placeholder_style.help')) ?></small>
                    <select name="trends.placeholder_style">
                        <option value="padawan" <?= $trendPlaceholderStyle === 'padawan' ? 'selected' : '' ?>><?= h(t('settings.trends.placeholder_style.padawan')) ?></option>
                        <option value="doh" <?= $trendPlaceholderStyle === 'doh' ? 'selected' : '' ?>><?= h(t('settings.trends.placeholder_style.doh')) ?></option>
                        <option value="neutral" <?= $trendPlaceholderStyle === 'neutral' ? 'selected' : '' ?>><?= h(t('settings.trends.placeholder_style.neutral')) ?></option>
                    </select>
                </label>
            </section>

            <section>
                <h3><?= h(t('settings.referrers.title')) ?></h3>
                <p><?= h(t('settings.referrers.help')) ?></p>

                <label>
                    <input name="referrers.auto_referrers" type="checkbox" <?= !empty($options['referrers']['auto_referrers']) ? 'checked' : '' ?>>
                    <?= h(t('settings.referrers.auto_referrers')) ?>
                    <small><?= h(t('settings.referrers.auto_referrers.help')) ?></small>
                </label>

                <label>
                    <input name="referrers.auto_referrer_icons" type="checkbox" <?= !empty($options['referrers']['auto_referrer_icons']) ? 'checked' : '' ?>>
                    <?= h(t('settings.referrers.auto_referrer_icons')) ?>
                    <small><?= h(t('settings.referrers.auto_referrer_icons.help')) ?></small>
                </label>

                <label>
                    <?= h(t('settings.referrers.max_icon_mb')) ?>
                    <small><?= h(t('settings.referrers.max_icon_mb.help')) ?></small>
                    <input name="referrers.max_icon_mb" type="number" min="0.01" max="10" step="0.01" value="<?= h(number_format(((int)($options['referrers']['max_icon_bytes'] ?? 102400)) / 1048576, 2, '.', '')) ?>">
                </label>

                <label>
                    <?= h(t('settings.referrers.max_icon_size')) ?>
                    <small><?= h(t('settings.referrers.max_icon_size.help')) ?></small>
                    <input name="referrers.max_icon_size" type="number" min="16" max="512" value="<?= h((int)($options['referrers']['max_icon_size'] ?? 96)) ?>">
                </label>
            </section>

            <div>
                <button type="button" value="cancel"><?= h(t('ui.cancel')) ?></button>
                <p data-error="<?= h(t('settings.messages.error')) ?>" data-network-error="<?= h(t('settings.messages.network_error')) ?>" data-saved="<?= h(t('settings.messages.saved')) ?>" id="settings-message" role="status"></p>
                <button type="submit"><?= icon('save') ?> <?= h(t('ui.save')) ?></button>
            </div>
        </form>
    </div>
</dialog>

Contribute