
























共有 6 个文件被更改,包括 91 次插入 和 10 次删除
@ -92,13 +92,41 @@ class Controller {
|
||||
return $this->render('directory', ['profiles' => $profiles])->withStatus(200);
|
||||
}
|
||||
|
||||
public function random(ServerRequestInterface $request, array $args): ResponseInterface {
|
||||
// if $args['slug'] is present this is an old-style URL.
|
||||
// but we don't do anything with that knowledge so it's just a lonely comment.
|
||||
public function next(ServerRequestInterface $request, array $args): ResponseInterface {
|
||||
$fromUrl = $request->getHeader('referer')[0] ?? false;
|
||||
if (! $fromUrl) { return $this->random($request, $args); }
|
||||
|
||||
$fromSite = $this->site->getSite($fromUrl); // exact matches only sorry
|
||||
|
||||
if($fromSite) {
|
||||
// get the next active site by our current ordering
|
||||
$toUrl = $this->site->getNextSite($fromSite['url'])['url'] ?? '/';
|
||||
} else {
|
||||
// get a random active site and redirect to it!
|
||||
$toUrl = $this->site->randomActive()['url'];
|
||||
}
|
||||
|
||||
return $this->response->withHeader('Location', $toUrl)->withStatus(302);
|
||||
}
|
||||
|
||||
public function previous(ServerRequestInterface $request, array $args): ResponseInterface {
|
||||
$fromUrl = $request->getHeader('referer')[0] ?? false;
|
||||
if (! $fromUrl) { return $this->random($request, $args); }
|
||||
|
||||
// if $request->getHeader('referer')[0] is present, that's where the visitor came from.
|
||||
// but we don't do anything with that knowledge so it's just a lonely comment.
|
||||
$fromSite = $this->site->getSite($fromUrl); // exact matches only sorry
|
||||
|
||||
if($fromSite) {
|
||||
// get the next active site by our current ordering
|
||||
$site = $this->site->getPreviousSite($fromSite['url']);
|
||||
} else {
|
||||
// get a random active site and redirect to it!
|
||||
$site = $this->site->randomActive();
|
||||
}
|
||||
|
||||
return $this->response->withHeader('Location', $site['url'])->withStatus(302);
|
||||
}
|
||||
|
||||
public function random(ServerRequestInterface $request, array $args): ResponseInterface {
|
||||
// get a random active site and redirect to it!
|
||||
$site = $this->site->randomActive();
|
||||
return $this->response->withHeader('Location', $site['url'])->withStatus(302);
|
||||
|
||||
@ -31,6 +31,34 @@ class Site {
|
||||
return $query->execute() ? $query->fetch() : '/';
|
||||
}
|
||||
|
||||
public function getNextSite ($url) {
|
||||
$query = $this->db->prepare('
|
||||
SELECT * FROM Sites
|
||||
WHERE sorting > (
|
||||
SELECT sorting FROM Sites
|
||||
WHERE url = :url
|
||||
)
|
||||
ORDER BY sorting
|
||||
LIMIT 1
|
||||
');
|
||||
|
||||
return $query->execute([$url]) ? $query->fetch() : $this->randomActive();
|
||||
}
|
||||
|
||||
public function getPreviousSite ($url) {
|
||||
$query = $this->db->prepare('
|
||||
SELECT * FROM Sites
|
||||
WHERE sorting < (
|
||||
SELECT sorting FROM Sites
|
||||
WHERE url = :url
|
||||
)
|
||||
ORDER BY sorting DESC
|
||||
LIMIT 1
|
||||
');
|
||||
|
||||
return $query->execute([$url]) ? $query->fetch() : $this->randomActive();
|
||||
}
|
||||
|
||||
public function all() {
|
||||
$query = $this->db->prepare('SELECT * FROM Sites');
|
||||
return $query->execute() ? $query->fetchAll() : [];
|
||||
@ -64,6 +92,22 @@ class Site {
|
||||
return $query->execute() ? $query->fetchAll() : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all active sites to a deterministic sort ordering
|
||||
* Uses `sin` for pseudo-randomness, the timestamp that each site was added
|
||||
* to the ring as a deterministic input, and an offset of the current month
|
||||
* to make it different every month.
|
||||
*/
|
||||
public function updateSorting() {
|
||||
$offset = date('n'); // numeric month, no leading zero
|
||||
$query = $this->db->prepare("
|
||||
UPDATE Sites
|
||||
SET sorting = SIN(STRFTIME('%s', timestamp) + :offset)
|
||||
WHERE active=1
|
||||
");
|
||||
return $query->execute([$offset]);
|
||||
}
|
||||
|
||||
protected function addSite (String $url) {
|
||||
$query = $this->db->prepare('INSERT INTO Sites (url, active) VALUES (:url, 1)');
|
||||
return $query->execute([$url]) ? [ 'url' => $url, 'active' => 1 ] : false;
|
||||
|
||||
@ -33,3 +33,6 @@ if (count($argv) > 1) {
|
||||
} else {
|
||||
$gardener->garden();
|
||||
}
|
||||
|
||||
// re-order active sites
|
||||
$sites->updateSorting();
|
||||
|
||||
@ -38,6 +38,10 @@
|
||||
"scripts": {
|
||||
"test": "php -dpcov.enabled=1 -dpcov.directory=./app ./vendor/bin/phpunit --config tests/phpunit.xml",
|
||||
"test:clean": "rm -rf tests/clover.xml tests/html-coverage tests/junit.xml tests/testdox.html vendor/bin/.phpunit.result.cache tests/.phpunit.result.cache",
|
||||
"garden": "LOGFILE=data/gardener.log php bin/gardener.php"
|
||||
"garden": "LOGFILE=data/gardener.log php bin/gardener.php",
|
||||
"serve": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"cd public && php -S 127.0.0.1:1234"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,8 +36,8 @@ $app->route->get('/terms', 'App\\Controller::terms');
|
||||
$app->route->get('/directory', 'App\\Controller::directory');
|
||||
|
||||
// ring navigation
|
||||
$app->route->get('/next', 'App\\Controller::random');
|
||||
$app->route->get('/previous', 'App\\Controller::random');
|
||||
$app->route->get('/next', 'App\\Controller::next');
|
||||
$app->route->get('/previous', 'App\\Controller::previous');
|
||||
$app->route->get('/{slug}/next', 'App\\Controller::random');
|
||||
$app->route->get('/{slug}/previous', 'App\\Controller::random');
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
CREATE TABLE Sites (
|
||||
url TEXT PRIMARY KEY,
|
||||
active INTEGER, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
profile text
|
||||
active INTEGER,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
profile text,
|
||||
sorting real not null default 0
|
||||
);
|
||||
CREATE TABLE SiteChecks (
|
||||
url TEXT KEY,
|
||||
|
||||
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。