PriviMetrics
Version 1.0.9 • 43 files • 278.98 KB
Files
.last_check
admin.php
assets/.htaccess
assets/dashboard-chart.php
assets/dashboard-logic.php
assets/dashboard-modals.php
assets/dashboard-tables.php
assets/dashboard-template.php
assets/trends-template.php
chosen-limits.php
dashboard.php
data/.htaccess
extensions-load.php
extensions.php
extensions.xml
extensions/.htaccess
extensions/extensions_off.txt
functions.php
getCountryFrom/db-ip.php
getCountryFrom/geo-lite.php
getCountryFrom/ip-api-com.php
getCountryFrom/ip-info.php
getCountryFrom/ip-stack.php
getCountryFrom/ip2location-io.php
getCountryFrom/privacy-friendly.php
index.html
install.php
limits-options.php
new_version.php
privimetrics-div.js
privimetrics.php
public.php
scripts.js
settings-config.php
settings.php
signup.php
storage.php
styles-mobile.css
styles.css
trends.css
trends.php
updater/index.php
version.txt
assets/dashboard-template.php
<?php
// ===============================================================================
// PriviMetrics - Dashboard HTML Template
// ===============================================================================
$showSearchesSetting = strtolower(trim((string)($settings['show_searches'] ?? 'show')));
?>
<!DOCTYPE html>
<?php ext_hook('before_render'); ?>
<html lang="en" data-theme="<?= $theme ?>" data-font="<?= htmlspecialchars($settings['ui_font'] ?? 'sora') ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard - <?= sanitize($config['site_name']) ?></title>
<link rel="stylesheet" media="screen and (max-width: 900px)" href="styles-mobile.css?v=<?= time(); ?>">
<link rel="stylesheet" media="screen and (min-width: 901px)" href="styles.css?v=<?= time(); ?>" >
<link rel="preload" href="fonts/sora.ttf" as="font" type="font/ttf" crossorigin>
</head>
<body>
<?php
$UpdateNotify = true; // @user-config // Change to false to disable update notifications
// If you want to completely disable checking for updates, disable the feature in new_version.php ($enableVersionCheck)
if ($UpdateNotify && isset($newVersionAvailable) && $newVersionAvailable) { ?>
<div id="updateModal" class="update-modal">
<div class="update-modal-content">
<div class="update-modal-header">
<strong>New Version Available</strong>
<span class="close" onclick="tempCloseUpdateModal()">×</span>
</div>
<div class="update-modal-body">
<p>Current version:
<?= htmlspecialchars($currentVersion) ?>
</p>
<p>Latest version:
<?= htmlspecialchars($latestVersion) ?>
</p>
<p>Download the latest version and update your installation.</p>
<p><a href="https://weborbiton.click/update-privimetrics/" class="update-modal-text">How to update</a></p>
</div>
<div class="update-modal-footer">
<button class="btn btn-secondary" onclick="permanentCloseUpdateModal()">Close</button>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
if (!sessionStorage.getItem('updateModalClosed')) {
document.getElementById('updateModal').style.display = 'flex';
}
});
function tempCloseUpdateModal() {
document.getElementById('updateModal').style.display = 'none';
}
function permanentCloseUpdateModal() {
document.getElementById('updateModal').style.display = 'none';
sessionStorage.setItem('updateModalClosed', 'true');
}
</script>
<?php } ?>
<div class="header">
<div class="logo">
<div class="logo-icon">
<?php
$logo = $config['site_logo'];
if (str_starts_with($logo, 'img:')) {
$logoFile = substr($logo, 4);
echo '<img src="' . htmlspecialchars($logoFile) . '" alt="' . htmlspecialchars($config['site_name']) . '" style="height:40px;">';
} else {
echo '<span>' . htmlspecialchars($logo) . '</span>';
}
?>
</div>
<div class="logo-text"><?= sanitize($config['site_name']) ?></div>
</div>
<div class="header-actions">
<button class="btn btn-primary" onclick="openAddSiteModal()">+ Add Site</button>
<a href="?site=<?= $selectedSiteId ?>&range=<?= $dateRange ?>&theme=<?= $theme === 'dark' ? 'light' : 'dark' ?>&view=<?= $view ?>" class="btn btn-secondary">
<?= $theme === 'dark' ? '<svg class="icon" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="12" cy="12" r="5" /> <path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" /></svg>' : '<svg class="icon" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"> <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" /></svg>' ?>
</a>
<a href="settings.php" class="btn btn-secondary">
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
Settings
</a>
<a href="admin.php?logout=1" class="btn btn-secondary">Logout</a>
</div>
</div>
<div class="container">
<div style="text-align: right; font-size: 11px; color: var(--text-tertiary); margin-bottom: 16px;">
Last updated: <span id="lastUpdate"><?= date('H:i:s') ?></span>
</div>
<?php if (isset($success)): ?>
<div class="success-message"><?= sanitize($success) ?></div>
<?php endif; ?>
<?php if (isset($error)): ?>
<div class="error" style="margin-bottom: 20px;"><?= sanitize($error) ?></div>
<?php endif; ?>
<?php if ($currentSite): ?>
<div class="toolbar">
<div class="toolbar-left">
<select onchange="window.location.href='?site=' + this.value + '&range=<?= $dateRange ?>&theme=<?= $theme ?>&view=<?= $view ?>'">
<?php foreach ($sites->site as $site): ?>
<option value="<?= (string)$site->id ?>" <?=(string)$site->id === $selectedSiteId ? 'selected' : '' ?>>
<?= sanitize((string)$site->name) ?>
</option>
<?php endforeach; ?>
</select>
<select onchange="window.location.href='?site=<?= $selectedSiteId ?>&range=' + this.value + '&theme=<?= $theme ?>&view=<?= $view ?>'">
<option value="24h" <?=$dateRange==='24h' ? 'selected' : '' ?>>Today</option>
<option value="7d" <?=$dateRange==='7d' ? 'selected' : '' ?>>Last 7 days</option>
<option value="30d" <?=$dateRange==='30d' ? 'selected' : '' ?>>Last 30 days</option>
<option value="90d" <?=$dateRange==='90d' ? 'selected' : '' ?>>Last 90 days</option>
<option value="1y" <?=$dateRange==='1y' ? 'selected' : '' ?>>This year</option>
<option value="5y" <?=$dateRange==='5y' ? 'selected' : '' ?>>Last 5 years</option>
</select>
</div>
<div class="toolbar-left">
<button class="btn btn-sm btn-secondary" onclick="window.open('trends.php' + window.location.search, '_self')">Trends</button>
<button class="btn btn-sm btn-secondary" onclick="openTrackingModal()">Get Code</button>
<button class="btn btn-sm btn-secondary" onclick="openManageModal()">Manage</button>
<?php
$storageType = strtolower((string)$currentSite->storage);
$limit = $chosenLimits[$storageType] ?? ['requests' => 0, 'window' => 1];
?>
<span class="badge badge-<?= $limit['requests'] >= 10 ? 'success' : 'warning' ?>"
title="Current rate limit for <?= strtoupper($storageType) ?> storage"
style="cursor: help; padding: 6px 12px;">
<?= $limit['requests'] ?> req/s
</span>
</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">Total Visits</div>
<div class="stat-value"><?= formatNumber($stats['total_visits']) ?></div>
</div>
<div class="stat-card">
<div class="stat-label">Unique Pages</div>
<div class="stat-value"><?= formatNumber($stats['unique_pages']) ?></div>
</div>
<?php if (strtolower(trim((string)($settings['show_countries'] ?? 'show'))) !== 'hide'): ?>
<div class="stat-card">
<div class="stat-label">Countries</div>
<div class="stat-value"><?= count($stats['top_countries']) ?></div>
</div>
<?php endif; ?>
<?php if ($showSearchesSetting !== 'hide'): ?>
<div class="stat-card">
<div class="stat-label">Searches</div>
<div class="stat-value"><?= formatNumber(array_sum($stats['top_searches'])) ?></div>
</div>
<?php endif; ?>
<?php echo ext_hook('custom_dashboard_widget', ''); ?>
</div>
<?php if ($stats['total_visits'] > 0): ?>
<?php require __DIR__ . '/dashboard-chart.php'; ?>
<div class="tabs">
<button class="tab <?= $view === 'overview' ? 'active' : '' ?>"
onclick="window.location.href='?site=<?= $selectedSiteId ?>&range=<?= $dateRange ?>&theme=<?= $theme ?>&view=overview'">Pages</button>
<button class="tab <?= $view === 'countries' ? 'active' : '' ?>"
onclick="window.location.href='?site=<?= $selectedSiteId ?>&range=<?= $dateRange ?>&theme=<?= $theme ?>&view=countries'">Countries</button>
<button class="tab <?= $view === 'referrers' ? 'active' : '' ?>"
onclick="window.location.href='?site=<?= $selectedSiteId ?>&range=<?= $dateRange ?>&theme=<?= $theme ?>&view=referrers'">Referrers</button>
<button class="tab <?= $view === 'searches' ? 'active' : '' ?>"
onclick="window.location.href='?site=<?= $selectedSiteId ?>&range=<?= $dateRange ?>&theme=<?= $theme ?>&view=searches'">Searches</button>
</div>
<?php require __DIR__ . '/dashboard-tables.php'; ?>
<?php else: ?>
<div class="no-data">
<h3>No data available</h3>
<p>Add the tracking code to your website to start collecting analytics.</p>
<button class="btn btn-primary" onclick="openTrackingModal()" style="margin-top: 16px;">Get Tracking Code</button>
</div>
<?php endif; ?>
<?php else: ?>
<div class="no-data">
<h3>No sites configured</h3>
<p>Add your first site to start tracking analytics.</p>
<button class="btn btn-primary" onclick="openAddSiteModal()" style="margin-top: 16px;">Add Site</button>
</div>
<?php endif; ?>
</div>
<?php require __DIR__ . '/dashboard-modals.php'; ?>
<script>
function refreshStats() {
fetch(location.href)
.then(response => response.text())
.then(html => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const newStats = doc.querySelectorAll('.stat-card .stat-value');
const currentStats = document.querySelectorAll('.stat-card .stat-value');
newStats.forEach((el, i) => {
if (currentStats[i]) {
currentStats[i].innerHTML = el.innerHTML;
}
});
const newTable = doc.querySelector('.table-card table tbody');
const currentTable = document.querySelector('.table-card table tbody');
if (newTable && currentTable) {
currentTable.innerHTML = newTable.innerHTML;
}
const updateElem = document.getElementById('lastUpdate');
if (updateElem) {
updateElem.textContent = new Date().toLocaleTimeString('pl-PL');
}
})
.catch(err => {
console.error('Refresh failed:', err);
});
}
setInterval(refreshStats, <?= $refreshMs ?>);
</script>
<script src="scripts.js?v=<?= time(); ?>"></script>
</body>
</html>