index.php
<?php
declare(strict_types=1);
defined('START_TIME') || define('START_TIME', microtime(true));
require_once __DIR__ . '/includes/core.php';
loadTranslations();
if (brivaciaNeedsInstall()) {
require_once __DIR__ . '/includes/modules/wizard.php';
return;
}
require_once __DIR__ . '/includes/archive.php';
if (brivaciaShouldCheckYearArchive()) {
archiveClosedYears(brivaciaDb());
brivaciaMarkYearArchiveChecked();
}
require_once __DIR__ . '/includes/dashboard/_init.php';
require_once __DIR__ . '/includes/dashboard/counters.php';
require_once __DIR__ . '/includes/dashboard/global.php';
require_once __DIR__ . '/includes/dashboard/privacy.php';
require_once __DIR__ . '/includes/dashboard/referrers.php';
require_once __DIR__ . '/includes/dashboard/top_pages.php';
require_once __DIR__ . '/includes/modules/update.php';
require_once __DIR__ . '/includes/version.php';
/*
|--------------------------------------------------------------------------
| Graph buttons visibility
|--------------------------------------------------------------------------
|
| Keep these rules here in index.php so the dashboard stays easy to read:
| the class is added directly on each button instead of being hidden in a
| helper function.
|
*/
$hideLineGraphs = $view === 'today';
$hideCountriesGraphs = count($countries) < 2;
$hideGlobalMapGraph = !$showCountries;
$hideReferrersGraphs = count($referrers) < 2;
$hideSearchEnginesGraphs = count($searchEngines) < 2;
$hideTopPagesGraph = $topTotal < 2;
$restoreAlert = brivaciaRestoreAlert();
?>
<!doctype html>
<html data-theme="<?= h($theme) ?>" dir="<?= h(currentDir()) ?>" lang="<?= h(currentLang()) ?>">
<head>
<title><?= h(t('title.statistics')) ?> • <?= brivacia_setting('dashboard.instance_name', 'Brivacia') ?></title>
<meta charset="utf-8">
<meta name="robots" content="noindex, nofollow, noarchive, nosnippet">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="<?= assetOrPlaceholder('favicon', brivaciaFaviconPlaceholder(), $currentSite) ?>">
<link rel="stylesheet" type="text/css" href="/assets/css/style.min.css?v=<?= microtime(true) ?>">
</head>
<body>
<header class="toolbar">
<h1 data-tooltip="<?= h(t('ui.back.home')) ?>"><a href="/"><img src="<?= assetOrPlaceholder('logo', brivaciaLogoPlaceholder(), $currentSite) ?>" alt="<?= h($siteTitle) ?>"><?= h($siteTitle) ?> <?= h(t('title.statistics')) ?></a></h1>
<div class="toolbar-filters">
<form class="date-form" method="get">
<button aria-label="<?= h(t('ui.show.calendar')) ?>" data-period-type="<?= h($view) ?>" data-period-value="<?= h($periodInputValue) ?>" data-tooltip="<?= h(t('ui.show.calendar')) ?>" data-tooltip-placement="left" data-week-label="<?= h(t('form.week')) ?>" tabindex="0" type="button"><span data-live-date><?= h($periodLabel) ?></span><small data-current-time></small></button>
<input name="view" type="hidden" value="<?= h($view) ?>">
<?php if ($view !== 'all'): ?><input data-ios-type="<?= h($periodIosInputType ?? $periodInputType) ?>" data-ios-value="<?= h($periodIosInputValue ?? $periodInputValue) ?>" id="period" name="<?= h($periodInputName) ?>" type="<?= h($periodInputType) ?>" value="<?= h($periodInputValue) ?>" onchange="this.form.submit()"><?php endif; ?>
<small class="ios-time" data-current-time></small>
</form>
<nav class="view-tabs">
<?php $viewLabels = ['today' => t('form.today'), 'week' => t('form.week'), 'month' => t('form.month'), 'year' => t('form.year'), 'all' => t('form.all'),]; ?>
<?php foreach ($viewLabels as $key => $label): ?><a class="<?= $view === $key ? 'active' : '' ?>" href="<?= h(dashboardViewUrl($key, $day, $week, $month, $year, ['site' => $currentSite])) ?>"><?= h($label) ?></a><?php endforeach; ?>
</nav>
<form method="get" class="view-select">
<select name="view" onchange="this.form.submit()">
<?php foreach ($viewLabels as $key => $label): ?>
<option value="<?= h($key) ?>" <?= $view === $key ? 'selected' : '' ?>>
<?= h($label) ?>
</option>
<?php endforeach; ?>
</select>
<?php if ($view === 'today'): ?>
<input type="hidden" name="day" value="<?= h($day) ?>">
<?php elseif ($view === 'week'): ?>
<input type="hidden" name="week" value="<?= h($week) ?>">
<?php elseif ($view === 'month'): ?>
<input type="hidden" name="month" value="<?= h($month) ?>">
<?php elseif ($view === 'year'): ?>
<input type="hidden" name="year" value="<?= h($year) ?>">
<?php endif; ?>
<?php if ($currentSite !== ''): ?>
<input type="hidden" name="site" value="<?= h($currentSite) ?>">
<?php endif; ?>
</form>
</div>
<div class="toolbar-actions">
<button data-privacy-open data-tooltip="<?= h(t('privacy.title', ['instance' => brivacia_setting('dashboard.instance_name', 'Brivacia')])) ?>" type="button"><?= icon('privacy') ?> <?= h(t('privacy.button')) ?></button>
<?php if (count(brivacia_sites()) > 1): ?>
<div class="sites-menu" data-dropdown>
<button data-dropdown-toggle data-tooltip="<?= h(t('ui.sites')) ?>" data-tooltip-placement="left" type="button"><?= icon('activity') ?> <?= h($currentSite === '' ? t('ui.sites.all') : $currentSite) ?> </button>
<div class="site-dropdown" data-dropdown-menu hidden>
<?php if ($currentSite !== ''): ?>
<a href="<?= h(dashboardViewUrl($view, $day, $week, $month, $year, ['site' => ''])) ?>"><?= h(t('ui.sites.all')) ?></a>
<?php endif; ?>
<?php foreach (array_keys(brivacia_sites()) as $siteCode): ?>
<?php if ($siteCode === $currentSite) continue; ?>
<a href="<?= h(dashboardViewUrl($view, $day, $week, $month, $year, ['site' => $siteCode])) ?>"><?= h($siteCode) ?></a>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php if (brivaciaUpdateAvailable()): ?>
<div class="notification-menu" data-dropdown>
<button aria-label="<?= h(t('update.available')) ?>" data-dropdown-toggle id="updates" title="<?= h(t('update.available')) ?>" type="button">
<?= icon('bell') ?>
<span></span>
</button>
<div class="notification-dropdown" data-dropdown-submenu hidden>
<div id="update-available">
<h3><?= h(t('update.available')) ?></h3>
<p><strong>Brivacia v<?= h(brivaciaLatestVersion()) ?></strong></p>
<p><?= h(t('update.description')) ?></p>
<button data-update-cleanup="<?= h(t('update.cleanup')) ?>" data-update-download="<?= h(t('update.download')) ?>" data-update-extract="<?= h(t('update.extract')) ?>" data-update-failed="<?= h(t('update.failed')) ?>" data-update-installing="<?= h(t('update.installing')) ?>" data-update-reload="<?= h(t('update.reload')) ?>" data-update-starting="<?= h(t('update.starting')) ?>" data-update-verify="<?= h(t('update.verify')) ?>" id="update-install" type="button">
<?= icon('export') ?>
<?= h(t('update.install')) ?>
</button>
</div>
<div id="update-progress" hidden>
<h3><?= h(t('update.running')) ?></h3>
<p id="update-progress-text"><?= h(t('update.starting')) ?></p>
<small><?= h(t('update.wait')) ?></small>
</div>
</div>
</div>
<?php endif; ?>
<div class="menu" data-dropdown>
<button data-dropdown-toggle data-tooltip="<?= h(t('ui.menu')) ?>" data-tooltip-placement="left" type="button"><?= icon('menu') ?></button>
<div class="menu-dropdown" data-dropdown-menu hidden>
<button data-modal-open="pixel-modal" data-tooltip="<?= h(t('ui.pixel.open', ['instance' => brivacia_setting('dashboard.instance_name', 'Brivacia')])) ?>" data-tooltip-placement="left" type="button"><?= icon('code') ?> <?= h(t('ui.pixel')) ?></button>
<div class="export-menu" data-dropdown>
<button data-dropdown-toggle type="button"><?= icon('export') ?> <?= h(t('ui.export')) ?></button>
<?php $exportParams = ltrim(dashboardViewUrl($view, $day, $week, $month, $year, ['site' => $currentSite]), '?'); ?>
<div class="export-dropdown" data-dropdown-submenu hidden>
<a href="/api/export.php?format=csv&<?= h($exportParams) ?>"><?= icon('csv') ?> CSV</a>
<a href="/api/export.php?format=json&<?= h($exportParams) ?>"><?= icon('json') ?> JSON</a>
<a href="/api/export.php?format=txt&<?= h($exportParams) ?>"><?= icon('text') ?> TXT</a>
</div>
</div>
<div class="import-menu" data-dropdown>
<button data-dropdown-toggle type="button"><?= icon('import') ?> <?= h(t('ui.import')) ?></button>
<div class="import-dropdown" data-dropdown-submenu hidden>
<button data-modal-open="import-modal" data-import-provider="adobe_analytics" data-tooltip="<?= h(t('import.provider.unavailable')) ?>" data-tooltip-placement="left" disabled type="button"><?= icon('adobe_analytics') ?> Adobe Analytics</button>
<button data-modal-open="import-modal" data-import-provider="google_analytics" data-tooltip="<?= h(t('import.provider.unavailable')) ?>" data-tooltip-placement="left" disabled type="button"><?= icon('google_analytics') ?> Google Analytics</button>
<button data-modal-open="import-modal" data-import-provider="matomo_db" type="button"><?= icon('matomo') ?> matomo</button>
<button data-modal-open="import-modal" data-import-provider="plausible_db" data-tooltip="<?= h(t('import.provider.unavailable')) ?>" data-tooltip-placement="left" disabled type="button"><?= icon('plausible') ?> Plausible</button>
<button data-modal-open="import-modal" data-import-provider="umami_db" data-tooltip="<?= h(t('import.provider.unavailable')) ?>" data-tooltip-placement="left" disabled type="button"><?= icon('umami') ?> Umami</button>
</div>
</div>
<button data-modal-open="settings-modal" type="button"><?= icon('settings') ?> <?= h(t('ui.settings')) ?></button>
</div>
</div>
</div>
</header>
<?php if ($restoreAlert): ?>
<div class="restore-alert" data-restore-alert>
<div>
<strong><?= h(t('ui.restore.detected')) ?></strong>
<?php $time = DateTime::createFromFormat('Y-m-d H:i:s', $restoreAlert['time']); ?>
<p class="restore-alert-time"><?= h($time?->format('d/m/Y H:i') ?? '') ?></p>
<p><?= h(t('ui.restore.description')) ?></p>
<small><?= h(t('ui.restore.backup')) ?> <?= h((string)($restoreAlert['backup'] ?? '')) ?></small>
</div>
<button aria-label="<?= h(t('ui.close')) ?>" data-restore-alert-close type="button">
<?= icon('close') ?>
</button>
</div>
<?php endif; ?>
<div class="cards">
<div class="card">
<span><?= h(t('metric.unique.visitors')) ?><button aria-label="<?= h(t('ui.show.graph.line')) ?>" <?= $hideLineGraphs ? 'class="hidden"' : '' ?> data-graph="unique_visitors" data-tooltip="<?= h(t('ui.show.graph.line')) ?>" tabindex="0" type="button"><?= icon('chart-line') ?></button></span>
<strong><?= h($today['unique_visitors'] ?? 0) ?></strong>
</div>
<div class="card">
<span><?= h(t('metric.visits')) ?><button aria-label="<?= h(t('ui.show.graph.line')) ?>" <?= $hideLineGraphs ? 'class="hidden"' : '' ?> data-graph="visits" data-tooltip="<?= h(t('ui.show.graph.line')) ?>" tabindex="0" type="button"><?= icon('chart-line') ?></button></span>
<strong><?= h($today['visits'] ?? 0) ?></strong>
</div>
<div class="card">
<span><?= h(t('metric.page.views')) ?><button aria-label="<?= h(t('ui.show.graph.line')) ?>" <?= $hideLineGraphs ? 'class="hidden"' : '' ?> data-graph="page_views" data-tooltip="<?= h(t('ui.show.graph.line')) ?>" tabindex="0" type="button"><?= icon('chart-line') ?></button></span>
<strong><?= h($today['pageviews'] ?? 0) ?></strong>
</div>
<div class="card">
<span><?= h(t('metric.bots')) ?><button aria-label="<?= h(t('ui.bots.info')) ?>" data-tooltip="<?= h(t('ui.bots.info')) ?>" tabindex="0" type="button"><?= icon('info') ?></button></span>
<strong><?= h($today['bots'] ?? 0) ?></strong>
</div>
<div class="card"><span><?= h(t('metric.average.visitors.per.day')) ?></span><strong><?= h($avgDay['avg_unique'] ?? 0) ?></strong></div>
<div class="card"><span><?= h(t('metric.average.visits.per.day')) ?></span><strong><?= h($avgDay['avg_visits'] ?? 0) ?></strong></div>
</div>
<div class="trends">
<?php if (brivacia_setting('trends.visitors', true) && $view !== 'all'): ?>
<div class="card" data-tooltip="<?= h(t('metric.change.visitors.tooltip')) ?>">
<span><?= h(t('metric.change.visitors')) ?></span>
<?php if ($visitorsChange !== null): ?>
<strong class="<?= $visitorsChange >= 0 ? 'success' : 'danger' ?>">
<?= $visitorsChange > 0 ? '+' : '' ?><?= h($visitorsChange) ?>%
</strong>
<small><?= h($visitsChangeLabel) ?></small>
<?php else: ?>
<strong class="trend-new-day"><?= icon('moon') ?><?= icon('sun') ?></strong>
<small><?= h(t('metric.change.not.enough.data')) ?></small>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (brivacia_setting('trends.visits', true) && $view !== 'all'): ?>
<div class="card" data-tooltip="<?= h(t('metric.change.visits.tooltip')) ?>">
<span><?= h(t('metric.change.visits')) ?></span>
<?php if ($visitsChange !== null): ?>
<strong class="<?= $visitsChange >= 0 ? 'success' : 'danger' ?>">
<?= $visitsChange > 0 ? '+' : '' ?><?= h($visitsChange) ?>%
</strong>
<small><?= h($visitsChangeLabel) ?></small>
<?php else: ?>
<strong class="trend-new-day"><?= icon('moon') ?><?= icon('sun') ?></strong>
<small><?= h(t('metric.change.not.enough.data')) ?></small>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (brivacia_setting('trends.pageviews', true) && $view !== 'all'): ?>
<div class="card" data-tooltip="<?= h(t('metric.change.pageviews.tooltip')) ?>">
<span><?= h(t('metric.change.pageviews')) ?></span>
<?php if ($pageviewsChange !== null): ?>
<strong class="<?= $pageviewsChange >= 0 ? 'success' : 'danger' ?>">
<?= $pageviewsChange > 0 ? '+' : '' ?><?= h($pageviewsChange) ?>%
</strong>
<small><?= h($visitsChangeLabel) ?></small>
<?php else: ?>
<strong class="trend-new-day"><?= icon('moon') ?><?= icon('sun') ?></strong>
<small><?= h(t('metric.change.not.enough.data')) ?></small>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<div class="summary-row <?= !$showCountries ? 'summary-row--no-countries' : '' ?>">
<section>
<h2>
<?= h(t('section.global.total')) ?>
<button aria-label="<?= h(t('ui.show.graph.map')) ?>" <?= $hideGlobalMapGraph ? 'class="hidden"' : '' ?> data-graph="global_map" data-tooltip="<?= h(t('ui.show.graph.map')) ?>" tabindex="0" type="button"><?= icon('chart-map') ?></button>
<button aria-label="<?= h(t('ui.expand')) ?>" data-modal-open="global-modal" data-tooltip="<?= h(t('ui.expand')) ?>" tabindex="0" type="button"><?= icon('expand') ?></button>
</h2>
<table>
<tbody>
<tr>
<td><?= icon('visitor') ?> <?= h(t('metric.unique.visitors')) ?></td>
<td><?= h($globalTotal['unique_visitors']) ?></td>
</tr>
<tr>
<td><?= icon('visits') ?> <?= h(t('metric.visits')) ?></td>
<td><?= h($globalTotal['visits']) ?></td>
</tr>
<tr>
<td><?= icon('page') ?> <?= h(t('metric.page.views')) ?></td>
<td><?= h($globalTotal['pageviews']) ?></td>
</tr>
<tr>
<td><?= icon('bot') ?> <?= h(t('metric.bots')) ?></td>
<td><?= h($globalTotal['bots'] ?? 0) ?></td>
</tr>
<?php if ($showCountries): ?>
<tr>
<td><?= icon('countries') ?> <?= h(t('section.countries')) ?></td>
<td><?= h($globalTotal['countries']) ?></td>
</tr>
<?php endif; ?>
<tr>
<td><?= icon('search') ?> <?= h(t('section.search.engines')) ?></td>
<td><?= h($globalTotal['search_engines'] ?? 0) ?></td>
</tr>
<tr>
<td><?= icon('referrals') ?> <?= h(t('section.referrers')) ?></td>
<td><?= h($globalTotal['referrers']) ?></td>
</tr>
</tbody>
</table>
</section>
<?php if ($showCountries): ?>
<section>
<h2>
<?= h(t('section.countries')) ?>
<button aria-label="<?= h(t('ui.show.graph.bar')) ?>" <?= $hideCountriesGraphs ? 'class="hidden"' : '' ?> data-graph="countries_bar" data-tooltip="<?= h(t('ui.show.graph.bar')) ?>" tabindex="0" type="button"><?= icon('chart-bar') ?></button>
<button aria-label="<?= h(t('ui.show.graph.pie')) ?>" <?= $hideCountriesGraphs ? 'class="hidden"' : '' ?> data-graph="countries_pie" data-tooltip="<?= h(t('ui.show.graph.pie')) ?>" tabindex="0" type="button"><?= icon('chart-pie') ?></button>
<button aria-label="<?= h(t('ui.expand')) ?>" data-modal-open="countries-modal" data-tooltip="<?= h(t('ui.expand')) ?>" tabindex="0" type="button"><?= icon('expand') ?></button>
</h2>
<table>
<tbody>
<?php foreach ($countries as $row): ?>
<?php $cc = strtolower((string)$row['country']); ?>
<tr>
<td>
<?= countryFlag($cc) ?> <span data-country="<?= h(strtoupper($cc)) ?>"><?= h(countryName($cc)) ?></span>
</td>
<td><?= h($row['views']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</section>
<?php endif; ?>
<section>
<h2>
<?= h(t('section.search.engines')) ?>
<button aria-label="<?= h(t('ui.show.graph.bar')) ?>" <?= $hideSearchEnginesGraphs ? 'class="hidden"' : '' ?> data-graph="search_engines_bar" data-tooltip="<?= h(t('ui.show.graph.bar')) ?>" tabindex="0" type="button"><?= icon('chart-bar') ?></button>
<button aria-label="<?= h(t('ui.show.graph.pie')) ?>" <?= $hideSearchEnginesGraphs ? 'class="hidden"' : '' ?> data-graph="search_engines_pie" data-tooltip="<?= h(t('ui.show.graph.pie')) ?>" tabindex="0" type="button"><?= icon('chart-pie') ?></button>
<button aria-label="<?= h(t('ui.expand')) ?>" data-modal-open="search-engines-modal" data-tooltip="<?= h(t('ui.expand')) ?>" tabindex="0" type="button"><?= icon('expand') ?></button>
</h2>
<table>
<tbody>
<?php foreach ($searchEngines as $row): ?>
<tr>
<td>
<?php if (($row['referrer'] ?? '') === BRIVACIA_BLOCKED): ?>
<?= icon('blocked') ?>
<?php else: ?>
<?= referrerIconHtml((string)$row['referrer']) ?>
<?php endif; ?>
<?= h((string)$row['label']) ?>
</td>
<td><?= h($row['views']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</section>
<section>
<h2>
<?= h(t('section.referrers')) ?>
<button aria-label="<?= h(t('ui.show.graph.bar')) ?>" <?= $hideReferrersGraphs ? 'class="hidden"' : '' ?> data-graph="referrers_bar" data-tooltip="<?= h(t('ui.show.graph.bar')) ?>" tabindex="0" type="button"><?= icon('chart-bar') ?></button>
<button aria-label="<?= h(t('ui.show.graph.pie')) ?>" <?= $hideReferrersGraphs ? 'class="hidden"' : '' ?> data-graph="referrers_pie" data-tooltip="<?= h(t('ui.show.graph.pie')) ?>" tabindex="0" type="button"><?= icon('chart-pie') ?></button>
<button aria-label="<?= h(t('ui.expand')) ?>" data-modal-open="referrers-modal" data-tooltip="<?= h(t('ui.expand')) ?>" tabindex="0" type="button"><?= icon('expand') ?></button>
</h2>
<table>
<tbody>
<?php foreach ($referrers as $row): ?>
<tr>
<td>
<?php if (($row['referrer'] ?? '') === BRIVACIA_BLOCKED): ?>
<?= icon('blocked') ?>
<?php else: ?>
<?= referrerIconHtml((string)$row['referrer']) ?>
<?php endif; ?>
<?= h((string)$row['label']) ?>
</td>
<td><?= h($row['views']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</section>
</div>
<section class="top-pages">
<section>
<div class="section-header">
<h2><?= h(t('section.top.pages')) ?> <button aria-label="<?= h(t('ui.show.graph.bar')) ?>" <?= $hideTopPagesGraph ? 'class="hidden"' : '' ?> data-graph="top_pages" data-tooltip="<?= h(t('ui.show.graph.bar')) ?>" tabindex="0" type="button"><?= icon('chart-bar') ?></button></h2>
<form method="get" class="per-page">
<?php if ($view === 'today'): ?>
<input name="day" type="hidden" value="<?= h($day) ?>">
<?php elseif ($view === 'week'): ?>
<input name="week" type="hidden" value="<?= h($week) ?>">
<?php elseif ($view === 'month'): ?>
<input name="month" type="hidden" value="<?= h($month) ?>">
<?php elseif ($view === 'year'): ?>
<input name="year" type="hidden" value="<?= h($year) ?>">
<?php endif; ?>
<input name="tp" type="hidden"value="1">
<input name="view" type="hidden" value="<?= h($view) ?>">
<label>
<?= h(t('form.per.page')) ?>
<select name="per" onchange="this.form.submit()">
<?php foreach ([20, 50, 100, 500, 1000] as $n): ?>
<option value="<?= $n ?>" <?= $topPerPage === $n ? 'selected' : '' ?>>
<?= $n ?>
</option>
<?php endforeach; ?>
</select>
</label>
</form>
</div>
<table>
<thead>
<tr>
<th><?= h(t('table.site')) ?></th>
<th><?= h(t('table.page')) ?></th>
<th><?= h(t('table.views')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($topPages as $row): ?>
<tr>
<td class="site"><?= h((string)$row['site']) ?></td>
<td>
<?= externalLink(
pageUrl(
(string)$row['site'],
(string)$row['page_key'],
(string)($row['url'] ?? '')
),
cleanPageTitle(
(string)($row['title'] ?: $row['page_key']),
(string)$row['site'],
(string)$row['page_key'],
(string)($row['url'] ?? '')
),
brivacia_setting('dashboard.show_external_icon_in_top_pages', true)
) ?>
</td>
<td><?= h($row['views']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php include __DIR__ . '/includes/modules/pagination.php'; ?>
</section>
</section>
<?php require __DIR__ . '/includes/modules/modals.php'; ?>
<footer>
<small>
<span><?= h(t('footer.powered')) ?><img src="<?= brivaciaLogoIcon() ?>" alt="" aria-hidden="true"><?= externalLink('https://code.breat.fr/b/brivacia/docs/changelog', 'Brivacia ' . brivacia_version(), true, t('ui.show.changelog'), 'top') ?><span aria-hidden="true" class="desktop-only"> • </span><br class="mobile-only"> <?= h(t('footer.created')) ?><img src="<?= brivaciaBreatfrLogoIcon() ?>" alt="" aria-hidden="true"><?= externalLink('https://breat.fr','breat.fr') ?></span>
<span><button data-modal-open="about-modal" type="button"><?= h(t('footer.about')) ?></button> • <?= externalLink('https://code.breat.fr/b/brivacia/docs/faq',h(t('wizard.help.link'))) ?></span>
<span><?= h(t('footer.support')) ?><br class="mobile-only"> <?= h(t('footer.via')) ?> <?= icon('ko-fi') ?> <?= externalLink('https://ko-fi.com/breatfr','Ko-fi') ?> <?= h(t('footer.or')) ?> <?= icon('paypal') ?> <?= externalLink('https://paypal.me/breat','PayPal') ?></span>
</small>
</footer>
<!-- Dashboard auto-refresh --><script>window.BRIVACIA_AUTO_REFRESH_SECONDS = <?= max(0, (int) brivacia_setting('dashboard.auto_refresh', 1) * 60) ?>;</script><!-- Dashboard auto-refresh -->
<!-- Main Script --><script defer src="/assets/js/main.min.js?v=<?= microtime(true) ?>"></script><!-- Main Script -->
<!-- Restore alert --><script>
document.querySelector('[data-restore-alert-close]')?.addEventListener('click', async () => {
await fetch('/api/restore_alert.php', { method: 'POST' });
document.querySelector('[data-restore-alert]')?.remove();
});
</script><!-- Restore alert -->
<?php if (defined('START_TIME')) {
echo "\n<!--\n";
echo "=== BRIVACIA DEBUG ===\n\n";
echo "Generated in " . round((microtime(true) - START_TIME) * 1000, 3) . " ms\n";
echo "Request ID : " . ($_SERVER['HTTP_X_REQUEST_ID'] ?? 'none') . "\n";
echo "Timestamp : " . date('Y-m-d H:i:s') . "\n\n";
echo "===================\n";
echo "-->\n";
} ?>
</body>
</html>