v3.1.1
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<FilesMatch "^(file\.db|updates\.json|api_keys\.txt)$">
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* index_config/bludit.php
|
||||
* ─────────────────────────────────────────────────────────────────────────────
|
||||
* Fetches the latest posts from a Bludit blog via its REST API.
|
||||
* Returns an array of post arrays, or an empty array on failure.
|
||||
*
|
||||
* Requires BLUDIT_URL, BLUDIT_API_TOKEN, BLUDIT_POST_COUNT,
|
||||
* BLUDIT_SNIPPET_LEN, BLUDIT_CACHE_TTL (all set in config.php).
|
||||
*
|
||||
* Usage (from index.php):
|
||||
* $blog_posts = bludit_get_posts();
|
||||
*/
|
||||
|
||||
function bludit_get_posts(): array {
|
||||
// Bail early if not configured
|
||||
if (!defined('BLUDIT_API_TOKEN') || BLUDIT_API_TOKEN === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$cache_file = sys_get_temp_dir() . '/tc_bludit_cache_' . md5(BLUDIT_URL) . '.json';
|
||||
$ttl = (int)(BLUDIT_CACHE_TTL ?? 300);
|
||||
|
||||
// Serve from cache if fresh
|
||||
if ($ttl > 0 && file_exists($cache_file) && (time() - filemtime($cache_file)) < $ttl) {
|
||||
$cached = json_decode(file_get_contents($cache_file), true);
|
||||
if (is_array($cached)) return $cached;
|
||||
}
|
||||
|
||||
// Build API request
|
||||
$api_url = rtrim(BLUDIT_URL, '/') . '/api/pages';
|
||||
$count = max(1, min(20, (int)(BLUDIT_POST_COUNT ?? 5)));
|
||||
|
||||
$ctx = stream_context_create([
|
||||
'http' => [
|
||||
'method' => 'GET',
|
||||
'header' => implode("\r\n", [
|
||||
'X-Bludit-API-Token: ' . BLUDIT_API_TOKEN,
|
||||
'Accept: application/json',
|
||||
]),
|
||||
'timeout' => 5,
|
||||
'ignore_errors' => true,
|
||||
],
|
||||
'ssl' => ['verify_peer' => true],
|
||||
]);
|
||||
|
||||
$raw = @file_get_contents($api_url, false, $ctx);
|
||||
if ($raw === false) return [];
|
||||
|
||||
$data = json_decode($raw, true);
|
||||
if (!isset($data['data']) || !is_array($data['data'])) return [];
|
||||
|
||||
$snippet_len = (int)(BLUDIT_SNIPPET_LEN ?? 110);
|
||||
$posts = [];
|
||||
|
||||
foreach (array_slice($data['data'], 0, $count) as $page) {
|
||||
// Skip drafts / non-published
|
||||
if (($page['type'] ?? '') !== 'published') continue;
|
||||
|
||||
// Build snippet from content (strip HTML, collapse whitespace)
|
||||
$raw_body = $page['content'] ?? $page['contentRaw'] ?? '';
|
||||
$plain = preg_replace('/\s+/', ' ', strip_tags(html_entity_decode($raw_body)));
|
||||
$snippet = mb_strlen($plain) > $snippet_len
|
||||
? mb_substr($plain, 0, $snippet_len) . '…'
|
||||
: $plain;
|
||||
|
||||
$posts[] = [
|
||||
'title' => html_entity_decode($page['title'] ?? 'Untitled'),
|
||||
'snippet' => $snippet,
|
||||
'url' => $page['permalink'] ?? (rtrim(BLUDIT_URL, '/') . '/' . ($page['key'] ?? '')),
|
||||
'date' => $page['date'] ?? '',
|
||||
'tags' => array_values((array)($page['tags'] ?? [])),
|
||||
];
|
||||
|
||||
if (count($posts) >= $count) break;
|
||||
}
|
||||
|
||||
// Cache result
|
||||
if ($ttl > 0 && !empty($posts)) {
|
||||
file_put_contents($cache_file, json_encode($posts), LOCK_EX);
|
||||
}
|
||||
|
||||
return $posts;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* index_config/config.php
|
||||
* ─────────────────────────────────────────────────────────────────────────────
|
||||
* All site-wide configuration lives here.
|
||||
* Edit this file only — nothing else needs touching for routine updates.
|
||||
*/
|
||||
|
||||
// ── Site identity ─────────────────────────────────────────────────────────────
|
||||
define('SITE_NAME', 'Ty Clifford');
|
||||
define('SITE_TAGLINE', 'Hobbies are my hobby.');
|
||||
define('SITE_URL', 'https://tyclifford.com');
|
||||
define('SITE_DESCRIPTION', 'The web space of Ty Clifford');
|
||||
define('SITE_AUTHOR', 'Ty Clifford');
|
||||
define('SITE_KEYWORDS', 'Ty Clifford, Keyser, West Virginia, United States, Nerd, TikTok, Freedom of Expression');
|
||||
define('SITE_OG_IMAGE', SITE_URL . '/images/fa2.jpg');
|
||||
define('SITE_OG_ALT', 'Profile image of Ty Clifford. Making a funny face squinting.');
|
||||
define('SITE_TWITTER', '@_tyclifford');
|
||||
|
||||
// ── Profile ───────────────────────────────────────────────────────────────────
|
||||
define('PROFILE_IMG', SITE_URL . '/images/fa2.jpg');
|
||||
define('PROFILE_IMG_ALT', 'Ty Clifford — making a funny face, squinting. Eye everything.');
|
||||
|
||||
// ── Background video ──────────────────────────────────────────────────────────
|
||||
// Set BG_VIDEO_MP4 to '' to disable the video (falls back to static gradient).
|
||||
define('BG_VIDEO_MP4', SITE_URL . '/bg/BGV2.mp4');
|
||||
define('BG_VIDEO_POSTER', SITE_URL . '/images/fa2.jpg');
|
||||
// Overlay opacity over the video: 0 = none 1 = fully black
|
||||
define('BG_OVERLAY', '0.78');
|
||||
|
||||
// ── Typed cycling strings (after "living in") ─────────────────────────────────
|
||||
// Format: ['text', 'optional_css_class']
|
||||
// CSS classes available: '' | 'grad-green' | 'grad-purple'
|
||||
define('TYPED_STRINGS', serialize([
|
||||
['West Virginia', ''],
|
||||
['Keyser.', ''],
|
||||
['curiosity.', ''],
|
||||
['wonder.', 'grad-green'],
|
||||
['the multiverse.', ''],
|
||||
['your mind now.', 'grad-purple'],
|
||||
]));
|
||||
|
||||
// ── Horizontal nav pills ──────────────────────────────────────────────────────
|
||||
// Format: ['Label', 'url', 'color_class', 'emoji or ""']
|
||||
// Color classes: col-blue | col-green | col-orange | col-pink | col-peach
|
||||
// col-red | col-hotpink | col-sky
|
||||
define('NAV_LINKS', serialize([
|
||||
['Live', 'https://tyclifford.com/live/', 'col-blue', ''],
|
||||
['Shop', 'https://shop.tyclifford.com/', 'col-green', ''],
|
||||
['Shares', 'https://tyclifford.com/trade/', 'col-red', ''],
|
||||
['World', 'https://tyclifford.com/world/', 'col-orange', ''],
|
||||
['Patreon', 'https://go.tyclifford.com/patreon', 'col-pink', ''],
|
||||
['Blog', 'https://tyclifford.com/blog', 'col-peach', ''],
|
||||
['YouTube', 'https://go.tyclifford.com/youtube', 'col-red', ''],
|
||||
['TikTok', 'https://go.tyclifford.com/tiktok', 'col-hotpink', ''],
|
||||
['Bluesky', 'https://go.tyclifford.com/bluesky', 'col-sky', ''],
|
||||
// ['Tip Jar', '/tip/', 'col-green', ''],
|
||||
]));
|
||||
|
||||
// ── Bludit blog integration ───────────────────────────────────────────────────
|
||||
// Base URL of your Bludit install (no trailing slash).
|
||||
define('BLUDIT_URL', 'https://blog.tyclifford.com');
|
||||
// Bludit API token — find it in Admin › Settings › API.
|
||||
// Set to '' to disable the blog widget entirely.
|
||||
define('BLUDIT_API_TOKEN', '');
|
||||
// Number of posts to display (max 5 recommended).
|
||||
define('BLUDIT_POST_COUNT', 5);
|
||||
// Snippet length in characters (post body is stripped of HTML).
|
||||
define('BLUDIT_SNIPPET_LEN', 110);
|
||||
// Cache TTL in seconds for the API response (0 = no cache).
|
||||
define('BLUDIT_CACHE_TTL', 300);
|
||||
|
||||
// ── Updates feed ─────────────────────────────────────────────────────────────
|
||||
// Path to the JSON file that drives the "Updates" widget (absolute path).
|
||||
define('UPDATES_JSON', __DIR__ . '/updates.json');
|
||||
|
||||
// ── Updates auto-refresh ──────────────────────────────────────────────────────
|
||||
// How often (in seconds) the browser polls for new updates.
|
||||
// Minimum enforced by JS: 5 seconds. Recommended: 30–120.
|
||||
define('UPDATES_REFRESH_INTERVAL', 30);
|
||||
|
||||
// URL to an audio file played when new updates are detected.
|
||||
// Supports any browser-compatible format: .mp3, .ogg, .wav
|
||||
// Set to '' to disable the sound notification entirely.
|
||||
define('UPDATES_REFRESH_SOUND', SITE_URL . '');
|
||||
|
||||
// How many updates to display per page in the widget.
|
||||
// Recommended: 10–15. Visitors can page through older posts.
|
||||
define('UPDATES_PER_PAGE', 10);
|
||||
|
||||
// URL to the lightweight JSON feed endpoint used by the auto-refresher.
|
||||
// Default: the updates_feed.php file inside index_config/ (no need to change).
|
||||
define('UPDATES_FEED_URL', '/index_config/updates_feed.php');
|
||||
|
||||
// ── Donation buttons ─────────────────────────────────────────────────────────
|
||||
// Format: ['Label', 'url', 'style_key']
|
||||
// style_key: 'patreon' | 'liberapay' | 'paypal' | 'generic'
|
||||
define('DONATE_LINKS', serialize([
|
||||
['Become a Patron', 'https://go.tyclifford.com/patreon', 'patreon'],
|
||||
// ['Donate via Liberapay', 'https://go.tyclifford.com/liberapay', 'liberapay'],
|
||||
['Tip via PayPal', 'https://www.paypal.com/donate/?hosted_button_id=ZDJ9TXPPMNZ6U', 'paypal'],
|
||||
]));
|
||||
|
||||
// ── Social / contact links ────────────────────────────────────────────────────
|
||||
// Format: ['Label', 'url', 'icon_key']
|
||||
// icon_key: 'github' | 'email' | 'patreon' | 'bluesky' | 'tiktok' | 'youtube' | 'web'
|
||||
define('SOCIAL_LINKS', serialize([
|
||||
['Git', 'https://git.tyclifford.com/snick/comv3', 'gitea'],
|
||||
['Bluesky', 'https://go.tyclifford.com/bluesky', 'bluesky'],
|
||||
['TikTok', 'https://go.tyclifford.com/tiktok', 'tiktok'],
|
||||
['YouTube', 'https://tyclifford.com/yt', 'youtube'],
|
||||
['ty@tyclifford.com', 'mailto:ty@tyclifford.com', 'email'],
|
||||
]));
|
||||
|
||||
// ── CTA buttons ───────────────────────────────────────────────────────────────
|
||||
// Set to '' to hide the respective button.
|
||||
define('CALENDLY_URL', 'https://calendly.com/tyclifford/list');
|
||||
define('SCHEDULE_URL', '/schedule/');
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* index_config/config.php
|
||||
* ─────────────────────────────────────────────────────────────────────────────
|
||||
* All site-wide configuration lives here.
|
||||
* Edit this file only — nothing else needs touching for routine updates.
|
||||
*/
|
||||
|
||||
// ── Site identity ─────────────────────────────────────────────────────────────
|
||||
define('SITE_NAME', 'Ty Clifford');
|
||||
define('SITE_TAGLINE', 'Hobbies are my hobby.');
|
||||
define('SITE_URL', 'https://tyclifford.com');
|
||||
define('SITE_DESCRIPTION', 'The web space of Ty Clifford');
|
||||
define('SITE_AUTHOR', 'Ty Clifford');
|
||||
define('SITE_KEYWORDS', 'Ty Clifford, Keyser, West Virginia, United States, Nerd, TikTok, Freedom of Expression');
|
||||
define('SITE_OG_IMAGE', SITE_URL . '/images/fa2.jpg');
|
||||
define('SITE_OG_ALT', 'Profile image of Ty Clifford. Making a funny face squinting.');
|
||||
define('SITE_TWITTER', '@_tyclifford');
|
||||
|
||||
// ── Profile ───────────────────────────────────────────────────────────────────
|
||||
define('PROFILE_IMG', SITE_URL . '/images/fa2.jpg');
|
||||
define('PROFILE_IMG_ALT', 'Ty Clifford — making a funny face, squinting. Eye everything.');
|
||||
|
||||
// ── Background video ──────────────────────────────────────────────────────────
|
||||
// Set BG_VIDEO_MP4 to '' to disable the video (falls back to static gradient).
|
||||
define('BG_VIDEO_MP4', SITE_URL . '/bg/BGV2.mp4');
|
||||
define('BG_VIDEO_POSTER', SITE_URL . '/images/fa2.jpg');
|
||||
// Overlay opacity over the video: 0 = none 1 = fully black
|
||||
define('BG_OVERLAY', '0.78');
|
||||
|
||||
// ── Typed cycling strings (after "living in") ─────────────────────────────────
|
||||
// Format: ['text', 'optional_css_class']
|
||||
// CSS classes available: '' | 'grad-green' | 'grad-purple'
|
||||
define('TYPED_STRINGS', serialize([
|
||||
['West Virginia', ''],
|
||||
['Keyser.', ''],
|
||||
['curiosity.', ''],
|
||||
['wonder.', 'grad-green'],
|
||||
['the multiverse.', ''],
|
||||
['your mind now.', 'grad-purple'],
|
||||
]));
|
||||
|
||||
// ── Horizontal nav pills ──────────────────────────────────────────────────────
|
||||
// Format: ['Label', 'url', 'color_class', 'emoji or ""']
|
||||
// Color classes: col-blue | col-green | col-orange | col-pink | col-peach
|
||||
// col-red | col-hotpink | col-sky
|
||||
define('NAV_LINKS', serialize([
|
||||
['Live', 'https://tyclifford.com/live/', 'col-blue', ''],
|
||||
//['Shares', 'https://tyclifford.com/trade/', 'col-green', ''],
|
||||
['
|
||||
//['World', 'https://tyclifford.com/world/', 'col-orange', ''],
|
||||
['Patreon', 'https://go.tyclifford.com/patreon', 'col-pink', ''],
|
||||
['Blog', 'https://tyclifford.com/blog', 'col-peach', ''],
|
||||
['YouTube', 'https://go.tyclifford.com/youtube', 'col-red', ''],
|
||||
['TikTok', 'https://go.tyclifford.com/tiktok', 'col-hotpink', ''],
|
||||
['Bluesky', 'https://go.tyclifford.com/bluesky', 'col-sky', ''],
|
||||
// ['Tip Jar', '/tip/', 'col-green', ''],
|
||||
]));
|
||||
|
||||
// ── Bludit blog integration ───────────────────────────────────────────────────
|
||||
// Base URL of your Bludit install (no trailing slash).
|
||||
define('BLUDIT_URL', 'https://blog.tyclifford.com');
|
||||
// Bludit API token — find it in Admin › Settings › API.
|
||||
// Set to '' to disable the blog widget entirely.
|
||||
define('BLUDIT_API_TOKEN', '');
|
||||
// Number of posts to display (max 5 recommended).
|
||||
define('BLUDIT_POST_COUNT', 5);
|
||||
// Snippet length in characters (post body is stripped of HTML).
|
||||
define('BLUDIT_SNIPPET_LEN', 110);
|
||||
// Cache TTL in seconds for the API response (0 = no cache).
|
||||
define('BLUDIT_CACHE_TTL', 300);
|
||||
|
||||
// ── Updates feed ─────────────────────────────────────────────────────────────
|
||||
// Path to the JSON file that drives the "Updates" widget (absolute path).
|
||||
define('UPDATES_JSON', __DIR__ . '/updates.json');
|
||||
|
||||
// ── Updates auto-refresh ──────────────────────────────────────────────────────
|
||||
// How often (in seconds) the browser polls for new updates.
|
||||
// Minimum enforced by JS: 5 seconds. Recommended: 30–120.
|
||||
define('UPDATES_REFRESH_INTERVAL', 30);
|
||||
|
||||
// URL to an audio file played when new updates are detected.
|
||||
// Supports any browser-compatible format: .mp3, .ogg, .wav
|
||||
// Set to '' to disable the sound notification entirely.
|
||||
define('UPDATES_REFRESH_SOUND', SITE_URL . '');
|
||||
|
||||
// How many updates to display per page in the widget.
|
||||
// Recommended: 10–15. Visitors can page through older posts.
|
||||
define('UPDATES_PER_PAGE', 10);
|
||||
|
||||
// URL to the lightweight JSON feed endpoint used by the auto-refresher.
|
||||
// Default: the updates_feed.php file inside index_config/ (no need to change).
|
||||
define('UPDATES_FEED_URL', '/index_config/updates_feed.php');
|
||||
|
||||
// ── Donation buttons ─────────────────────────────────────────────────────────
|
||||
// Format: ['Label', 'url', 'style_key']
|
||||
// style_key: 'patreon' | 'liberapay' | 'paypal' | 'generic'
|
||||
define('DONATE_LINKS', serialize([
|
||||
['Become a Patron', 'https://go.tyclifford.com/patreon', 'patreon'],
|
||||
// ['Donate via Liberapay', 'https://go.tyclifford.com/liberapay', 'liberapay'],
|
||||
['Tip via PayPal', 'https://www.paypal.com/donate/?hosted_button_id=ZDJ9TXPPMNZ6U', 'paypal'],
|
||||
]));
|
||||
|
||||
// ── Social / contact links ────────────────────────────────────────────────────
|
||||
// Format: ['Label', 'url', 'icon_key']
|
||||
// icon_key: 'github' | 'email' | 'patreon' | 'bluesky' | 'tiktok' | 'youtube' | 'web'
|
||||
define('SOCIAL_LINKS', serialize([
|
||||
['Git', 'https://git.tyclifford.com/snick/comv3', 'gitea'],
|
||||
['Bluesky', 'https://go.tyclifford.com/bluesky', 'bluesky'],
|
||||
['TikTok', 'https://go.tyclifford.com/tiktok', 'tiktok'],
|
||||
['YouTube', 'https://tyclifford.com/yt', 'youtube'],
|
||||
['ty@tyclifford.com', 'mailto:ty@tyclifford.com', 'email'],
|
||||
]));
|
||||
|
||||
// ── CTA buttons ───────────────────────────────────────────────────────────────
|
||||
// Set to '' to hide the respective button.
|
||||
define('CALENDLY_URL', 'https://calendly.com/tyclifford/list');
|
||||
define('SCHEDULE_URL', '/schedule/');
|
||||
@@ -0,0 +1,421 @@
|
||||
|
||||
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=0">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<title>Freesound - Login</title>
|
||||
<meta name="description" content="Freesound: collaborative database of creative-commons licensed sound for musicians and sound lovers. Have you freed your sound today?">
|
||||
<meta name="keywords" content="free, sound">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#fd5b65">
|
||||
<meta name="theme-color" content="#fd5b65">
|
||||
<meta name="msapplication-config" content="none" />
|
||||
|
||||
|
||||
|
||||
|
||||
<link id="style" rel="stylesheet" href="/static/bw-frontend/dist/index.e9e6280b633e.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
|
||||
|
||||
<div class="modal " id="loginModal" tabindex="-1" role="dialog" aria-label="Login modal" aria-hidden="true">
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<span class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span class="bw-icon-close ">
|
||||
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="col-12">
|
||||
<div class="text-center">
|
||||
<h4 class="v-spacing-5">Log in to Freesound</h4>
|
||||
<form id="loginForm" class="bw-form bw-form-less-spacing" method="post" action="/home/login/"><input type="hidden" name="csrfmiddlewaretoken" value="OENJEDIyCmcoXEKFCbltnYDFbsT4UjkkYSX1vJTyncQAXWDqAWh9cl1OkavuTvGp">
|
||||
<p>
|
||||
<label for="id_username">Username:</label>
|
||||
<input type="text" name="username" autofocus autocapitalize="none" autocomplete="username" maxlength="150" placeholder="Enter your email or username" required id="id_username">
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
<label for="id_password">Password:</label>
|
||||
<input type="password" name="password" autocomplete="current-password" placeholder="Enter your password" required id="id_password">
|
||||
|
||||
|
||||
|
||||
|
||||
</p>
|
||||
<input type="hidden" name="next" value="/people/InfiniteLifespan/sounds/266455/" />
|
||||
<button type="submit" class="btn-primary v-spacing-top-2">Log in</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
<a class="v-spacing-top-3" data-toggle="problems-logging-in-modal">Problems logging in?</a>
|
||||
|
||||
</div>
|
||||
<div class="modal-extra-info">
|
||||
|
||||
<a data-toggle="registration-modal">Don't have an account? Join now</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal " id="problemsLoggingInModal" tabindex="-1" role="dialog" aria-label="Problems logging in modal" aria-hidden="true">
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<span class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span class="bw-icon-close ">
|
||||
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="col-12">
|
||||
<div class="text-center">
|
||||
<h4 class="v-spacing-5">Problems logging in?</h4>
|
||||
<div class="text-grey v-spacing-top-negative-1 v-spacing-4">
|
||||
Enter your email or username below and we'll send you a link to help you login into your account.
|
||||
</div>
|
||||
<form id="problemsLoggingInModalForm" class="bw-form bw-form-less-spacing" method="post" action="/home/problems/"><input type="hidden" name="csrfmiddlewaretoken" value="OENJEDIyCmcoXEKFCbltnYDFbsT4UjkkYSX1vJTyncQAXWDqAWh9cl1OkavuTvGp">
|
||||
<p>
|
||||
|
||||
<input type="text" name="username_or_email" maxlength="254" placeholder="Your email or username" required id="id_username_or_email">
|
||||
|
||||
|
||||
|
||||
|
||||
</p>
|
||||
<button id="recovery-account" type="submit" class="btn-primary v-spacing-top-2">Send me a link</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
<a class="v-spacing-top-3" data-link="loginModal"><span class="bw-icon-chevron bw-icon__chevron_left">
|
||||
|
||||
</span> Back to log in</a>
|
||||
|
||||
</div>
|
||||
<div class="modal-extra-info">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal " id="feedbackRegistration" tabindex="-1" role="dialog" aria-label="Registration complete modal" aria-hidden="true">
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<span class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span class="bw-icon-close ">
|
||||
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="col-12">
|
||||
<div class="text-center">
|
||||
<h4 class="v-spacing-5">Almost there!</h4>
|
||||
<p class="main-text">We've sent a verification link by email</p>
|
||||
<p class="secondary-text">Didn't receive the email? Check your Spam folder, it may have been caught by a filter. If you still don't see it, you can <a href="/home/reactivate/">resend the verification email</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
</div>
|
||||
<div class="modal-extra-info">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal zindex2000" id="confirmationModal" tabindex="-1" role="dialog" aria-label="Confirmation modal" aria-hidden="true">
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<span class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span class="bw-icon-close ">
|
||||
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="text-center">
|
||||
<h4 id="confirmationModalTitle">Default title</h4>
|
||||
<div id="confirmationModalHelpText" class="v-spacing-top-2"></div>
|
||||
<div class="row v-spacing-top-4 v-spacing-3">
|
||||
<div class="col-4 offset-2">
|
||||
<form id="confirmationModalAcceptSubmitForm" method="post" action=""><input type="hidden" name="csrfmiddlewaretoken" value="OENJEDIyCmcoXEKFCbltnYDFbsT4UjkkYSX1vJTyncQAXWDqAWh9cl1OkavuTvGp">
|
||||
<button type="submit" class="btn-primary">Yes</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<button class="btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
</div>
|
||||
<div class="modal-extra-info">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="genericModalWrapper"></div>
|
||||
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="toast-body"></div>
|
||||
</div>
|
||||
<div class="notifications-wrapper"></div>
|
||||
|
||||
<div class="bw-page" aria-hidden="false">
|
||||
|
||||
|
||||
|
||||
<nav class="bw-nav">
|
||||
<div class="container bw-nav__container">
|
||||
<form method="get" action="/search/">
|
||||
<div class="bw-nav__logo-search-container">
|
||||
<div class="bw-nav__logo">
|
||||
<a href="/" class="no-hover"></a>
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<input name="q" type="search" placeholder="Search sounds..." autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<ul class="bw-nav__actions" role="menu">
|
||||
<li class="bw-nav__action d-none d-lg-flex">
|
||||
<a class="bw-link--grey font-weight-bold" href="/browse/" role="menuitem">Sounds</a>
|
||||
</li>
|
||||
<li class="bw-nav__action d-none d-lg-flex">
|
||||
<a class="bw-link--grey font-weight-bold" href="/browse/tags/" role="menuitem">Tags</a>
|
||||
</li>
|
||||
<li class="bw-nav__action d-none d-lg-flex">
|
||||
<a class="bw-link--grey font-weight-bold" href="/forum/" role="menuitem">Forum</a>
|
||||
</li>
|
||||
<li class="bw-nav__action d-none d-lg-flex">
|
||||
<a class="bw-link--grey font-weight-bold" href="/browse/geotags/" role="menuitem">Map</a>
|
||||
</li>
|
||||
<li class="bw-nav__action dropdown">
|
||||
<a class="bw-link--grey bw-nav__menu dropdown-toggle no-hover bw-icon-ellipsis" id="three-dots-menu" aria-label="Menu"
|
||||
data-toggle="dropdown" tabindex="0"></a>
|
||||
<ul class="dropdown-menu" aria-labelledby="three-dots-menu">
|
||||
<li class="bw-nav__action dropdown-item d-lg-none">
|
||||
<a class="bw-link--black" href="/browse/">Sounds</a>
|
||||
</li>
|
||||
<li class="bw-nav__action dropdown-item">
|
||||
<a class="bw-link--black" href="/browse/packs/">Packs</a>
|
||||
</li>
|
||||
<li class="bw-nav__action dropdown-item d-lg-none">
|
||||
<a class="bw-link--black" href="/forum/">Forum</a>
|
||||
</li>
|
||||
<li class="bw-nav__action dropdown-item d-lg-none">
|
||||
<a class="bw-link--black" href="/browse/geotags/">Map</a>
|
||||
</li>
|
||||
<li class="bw-nav__action dropdown-item d-lg-none">
|
||||
<a class="bw-link--black" href="/browse/tags/">Tags</a>
|
||||
</li>
|
||||
<li class="bw-nav__action dropdown-item">
|
||||
<a class="bw-link--black" href="/browse/random/">Random sound</a>
|
||||
</li>
|
||||
<li class="bw-nav__action dropdown-item">
|
||||
<a class="bw-link--black" href="/charts/">Charts</a>
|
||||
</li>
|
||||
<li class="bw-nav__action dropdown-item d-lg-none">
|
||||
<a class="bw-link--black" href="/donations/donate/">Donate</a>
|
||||
</li>
|
||||
<li class="bw-nav__action dropdown-item">
|
||||
<a class="bw-link--grey" href="/help/faq/">Help</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="bw-nav__action">
|
||||
<button class="btn-secondary" data-toggle="login-modal" data-target="#loginModal" role="menuitem">Log in</button>
|
||||
</li>
|
||||
<li class="bw-nav__action d-none d-lg-flex">
|
||||
<button class="btn-primary" data-toggle="registration-modal" data-target="#registerModal" role="menuitem">Join</button>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
<div class="navbar-space-filler v-spacing-7 v-spacing-top-5">
|
||||
|
||||
<div class="no-paddings col-12 v-spacing-3">
|
||||
<h1>Log in to Freesound</h1>
|
||||
</div>
|
||||
|
||||
<div class="col-12 no-paddings v-spacing-5">
|
||||
|
||||
<div class="login-page-form">
|
||||
<div class="row no-gutters">
|
||||
<div class="v-spacing-5 v-spacing-top-5 col-lg-4">
|
||||
<form class="bw-form bw-form-less-spacing" method="post" action="."><input type="hidden" name="csrfmiddlewaretoken" value="OENJEDIyCmcoXEKFCbltnYDFbsT4UjkkYSX1vJTyncQAXWDqAWh9cl1OkavuTvGp">
|
||||
<p>
|
||||
<label for="id_username">Username:</label>
|
||||
<input type="text" name="username" autofocus autocapitalize="none" autocomplete="username" maxlength="150" placeholder="Enter your email or username" required id="id_username">
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
<label for="id_password">Password:</label>
|
||||
<input type="password" name="password" autocomplete="current-password" placeholder="Enter your password" required id="id_password">
|
||||
|
||||
|
||||
|
||||
|
||||
</p>
|
||||
<input type="hidden" name="next" value="/people/InfiniteLifespan/sounds/266455/" />
|
||||
<button type="submit" class="btn-primary v-spacing-top-2">Log in</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a class="bw-link--grey cursor-pointer" data-toggle="problems-logging-in-modal">Problems logging in?</a>
|
||||
</div>
|
||||
<div class="v-spacing-top-2">
|
||||
<a class="bw-link--grey cursor-pointer" data-toggle="registration-modal">Don't have an account? Join now</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<footer class="bw-footer padding-7 v-spacing-top-6">
|
||||
<div class="center">
|
||||
<div class="footer-logo-container">
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<a href="https://www.upf.edu/web/mtg" class="no-hover">
|
||||
<img class="w-100" src="/static/bw-frontend/public/upf_logo.06e9f326ce68.png" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<a href="https://www.upf.edu/web/phonos/" class="no-hover">
|
||||
<img class="w-100" src="/static/bw-frontend/public/phonos_logo.7c586cfd83d2.png" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row v-spacing-top-3 center">
|
||||
<a class="bw-link--grey h-spacing-1" href="/help/about/">About Freesound</a>
|
||||
<a class="bw-link--grey h-spacing-1" href="/help/tos_web/">Terms of use</a>
|
||||
<a class="bw-link--grey h-spacing-1" href="/help/privacy/">Privacy</a>
|
||||
<a class="bw-link--grey h-spacing-1" href="/help/cookies_policy/">Cookies</a>
|
||||
<a class="bw-link--grey h-spacing-1" href="/help/developers/">Developers</a>
|
||||
<a class="bw-link--grey h-spacing-1" href="/help/faq/">Help</a>
|
||||
<a class="bw-link--grey h-spacing-1" href="/donations/donors/">Donations</a>
|
||||
<a class="bw-link--grey h-spacing-1" href="https://blog.freesound.org">Blog</a>
|
||||
<a class="bw-link--grey h-spacing-1" href="https://labs.freesound.org">Freesound Labs</a>
|
||||
<a class="bw-link--grey h-spacing-1" href="/help/tshirt/">Get your t-shirt!</a>
|
||||
</div>
|
||||
<div class="center v-spacing-top-3">
|
||||
<span>© 2026 Universitat Pompeu Fabra</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script defer data-domain="freesound.org" src="https://analytics.freesound.org/js/plausible.manual.js"></script>
|
||||
<script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script>
|
||||
<script>
|
||||
let redactedUrl = window.location.href.replace(/\/\d+\//g, "/_ID_/"); // Replace numberic IDs
|
||||
redactedUrl = redactedUrl.replace(/people\/([^/]+)\//ig, "people/_USERNAME_/"); // Replace usernames
|
||||
redactedUrl = redactedUrl.replace(/browse\/geotags\/(\w+)\//ig, "browse/geotags/_TAG_/"); // Replace tag from geotags
|
||||
redactedUrl = redactedUrl.replace(/browse\/tags\/.*/ig, "browse/tags/_TAGS_/"); // Replace multiple tags from tags page
|
||||
if (redactedUrl.includes("/forum/")){ // Replace forum names, be careful with some hard coded URLs
|
||||
if (!(redactedUrl.includes("/moderate/") || redactedUrl.includes("/forums-search/") || redactedUrl.includes("/hot-treads/"))){
|
||||
redactedUrl = redactedUrl.replace(/forum\/([^/]+)\//ig, "forum/_FORUM_NAME_/"); // Replace forum name
|
||||
}
|
||||
}
|
||||
redactedUrl = redactedUrl.replace(/activate\/([^/]+)\/([^/]+)\//ig, "activate/_USERNAME_/_CODE_/"); // Replace activation urls
|
||||
redactedUrl = redactedUrl.replace(/reset\/([^/]+)\//ig, "reset/_CODE_/"); // Replace password reset
|
||||
redactedUrl = redactedUrl.replace(/tickets\/([^/]+)\//ig, "tickets/_ID_/"); // Replace moderation tickets
|
||||
plausible('pageview', { u: redactedUrl });
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.js'></script>
|
||||
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.css' rel='stylesheet' />
|
||||
<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.7.2/mapbox-gl-geocoder.min.js'></script>
|
||||
<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.7.2/mapbox-gl-geocoder.css' type='text/css' />
|
||||
<script type="text/javascript">
|
||||
mapboxgl.accessToken = 'pk.eyJ1IjoiZnJlZXNvdW5kIiwiYSI6ImNrd3E0Mm9lbjBqM2Qyb2wwdmwxaWI3a3oifQ.MZkgLSByRuk_Xql67CySAg';
|
||||
</script>
|
||||
<script>
|
||||
|
||||
document.cookie = "preferSpectrogram=no;path=/";
|
||||
document.cookie = "disallowSimultaneousAudioPlayback=no;path=/"
|
||||
|
||||
const userIsAuthenticated = false;
|
||||
</script>
|
||||
<script src="/static/bw-frontend/dist/index.2b66cae7a11f.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* index_config/updates_feed.php
|
||||
* ─────────────────────────────────────────────────────────────────────────────
|
||||
* Lightweight JSON endpoint consumed exclusively by the JS auto-refresh in
|
||||
* index.php. Returns the full updates array (JS handles pagination client-side
|
||||
* so every poll can detect new posts regardless of which page is visible).
|
||||
*
|
||||
* Usage: GET index_config/updates_feed.php
|
||||
* GET index_config/updates_feed.php?since=<ISO-timestamp>
|
||||
*
|
||||
* The optional ?since= parameter lets the client ask "give me only posts newer
|
||||
* than this timestamp" so it can detect new arrivals without comparing full
|
||||
* arrays.
|
||||
*
|
||||
* Response:
|
||||
* {
|
||||
* "ok": true,
|
||||
* "total": <int>, // total posts in the feed
|
||||
* "new_since": <int>, // posts newer than ?since= (0 if not supplied)
|
||||
* "items": [ … ] // full array, newest first
|
||||
* }
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-store');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
|
||||
// ── Load feed ─────────────────────────────────────────────────────────────────
|
||||
$posts = [];
|
||||
if (file_exists(UPDATES_JSON)) {
|
||||
$raw = json_decode(file_get_contents(UPDATES_JSON), true);
|
||||
if (is_array($raw)) $posts = $raw;
|
||||
}
|
||||
|
||||
// ── Count posts newer than ?since= ───────────────────────────────────────────
|
||||
$since = trim($_GET['since'] ?? '');
|
||||
$new_count = 0;
|
||||
|
||||
if ($since !== '') {
|
||||
try {
|
||||
$since_dt = new DateTime($since, new DateTimeZone('UTC'));
|
||||
foreach ($posts as $p) {
|
||||
try {
|
||||
$post_dt = new DateTime($p['date'] ?? '', new DateTimeZone('UTC'));
|
||||
if ($post_dt > $since_dt) $new_count++;
|
||||
} catch (Exception $e) { /* skip malformed dates */ }
|
||||
}
|
||||
} catch (Exception $e) { /* ignore bad since param */ }
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'total' => count($posts),
|
||||
'new_since' => $new_count,
|
||||
'items' => $posts,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
Reference in New Issue
Block a user