diff --git a/core/database.php b/core/database.php index 66a39d22..acfdd904 100644 --- a/core/database.php +++ b/core/database.php @@ -439,4 +439,19 @@ class Database $this->execute("ALTER TABLE $table RENAME COLUMN {$column}_b TO $column"); } } + + /** + * Generates a deterministic pseudorandom order based on a seed and a column ID + */ + public function seeded_random(int $seed, string $id_column): string + { + $d = $this->get_driver_id(); + if ($d == DatabaseDriverID::MYSQL) { + // MySQL supports RAND(n), where n is a random seed. Use that. + return "RAND($seed)"; + } + + // As fallback, use MD5 as a DRBG. + return "MD5(CONCAT($seed, CONCAT('+', $id_column)))"; + } } diff --git a/ext/index/main.php b/ext/index/main.php index 49de8519..f6c5ad90 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -155,6 +155,8 @@ class Index extends Extension public function onSearchTermParse(SearchTermParseEvent $event): void { + global $database; + if (is_null($event->term)) { return; } @@ -235,14 +237,14 @@ class Index extends Extension } elseif (preg_match("/^order[=|:]random[_]([0-9]{1,4})$/i", $event->term, $matches)) { // requires a seed to avoid duplicates // since the tag can't be changed during the parseevent, we instead generate the seed during submit using js - $seed = $matches[1]; - $event->order = "RAND($seed)"; + $seed = (int)$matches[1]; + $event->order = $database->seeded_random($seed, "images.id"); } elseif (preg_match("/^order[=|:]dailyshuffle$/i", $event->term, $matches)) { // will use today's date as seed, thus allowing for a dynamic randomized list without outside intervention. // This way the list will change every day, giving a more dynamic feel to the imageboard. // recommended to change homepage to "post/list/order:dailyshuffle/1" - $seed = date("Ymd"); - $event->order = "RAND($seed)"; + $seed = (int)date("Ymd"); + $event->order = $database->seeded_random($seed, "images.id"); } // If we've reached this far, and nobody else has done anything with this term, then treat it as a tag