diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 2a779198..de07ad1d 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -2,11 +2,14 @@ name: auto-release on: pull_request: - types: [closed] + branches: + - main + types: + - closed jobs: create-release: - if: github.event.pull_request.merged + if: github.event.pull_request.merged == true runs-on: ubuntu-latest steps: - diff --git a/VERSION b/VERSION index 0501b79e..56fea8a0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.3.6 \ No newline at end of file +3.0.0 \ No newline at end of file diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 diff --git a/composer.lock b/composer.lock index d289834b..0472842e 100644 --- a/composer.lock +++ b/composer.lock @@ -8,11 +8,11 @@ "packages": [ { "name": "phpstan/phpstan", - "version": "2.1.40", + "version": "2.1.54", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", - "reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8be50c3992107dc837b17da4d140fbbdf9a5c5bd", + "reference": "8be50c3992107dc837b17da4d140fbbdf9a5c5bd", "shasum": "" }, "require": { @@ -57,7 +57,7 @@ "type": "github" } ], - "time": "2026-02-23T15:04:35+00:00" + "time": "2026-04-29T13:31:09+00:00" } ], "packages-dev": [], diff --git a/obadiah.code-workspace b/obadiah.code-workspace index 32e6d823..9c7adf4a 100644 --- a/obadiah.code-workspace +++ b/obadiah.code-workspace @@ -7,13 +7,5 @@ "settings": { "editor.renderWhitespace": "boundary", "files.trimTrailingWhitespace": true, - "nushellLanguageServer.includeDirs": [ - "Q:/src/docker/alpine/overlay/etc/nu/scripts", - "Q:/src/docker/nginx-php/overlay/etc/nu/scripts", - "Q:/src/docker/php/overlay/etc/nu/scripts", - "/Users/bcg/src/docker/alpine/overlay/etc/nu/scripts", - "/Users/bcg/src/docker/nginx-php/overlay/etc/nu/scripts", - "/Users/bcg/src/docker/php/overlay/etc/nu/scripts" - ] } } diff --git a/src/api/safeguarding/safeguarding.class.php b/src/api/safeguarding/safeguarding.class.php index 75720343..32cde7ed 100644 --- a/src/api/safeguarding/safeguarding.class.php +++ b/src/api/safeguarding/safeguarding.class.php @@ -157,7 +157,7 @@ public function reference_post(): Json Log::debug("Reference form received: %s", json_encode($form)); // remove hyphens from select values - $get_select = fn(int $num) => str_replace("-", " ", Arr::get($form, "select_$num")); + $get_select = fn(int $num) => str_replace("-", " ", Arr::get($form, "select_$num", "")); // map JSON to Baserow table fields $row = array( @@ -170,12 +170,12 @@ public function reference_post(): Json "Equality" => $get_select(4), "Honesty etc" => $get_select(5), "Comments" => Arr::get($form, "textarea_1"), - "Health" => Arr::get($form, "select_6"), + "Health" => $get_select(6), "Health Details" => Arr::get($form, "textarea_2"), - "Unsuitability" => Arr::get($form, "select_7"), + "Unsuitability" => $get_select(7), "Unsuitability Details" => Arr::get($form, "textarea_3"), "Referee Full Name" => Arr::get($form, "name_2"), - "Confirm" => Arr::get($form, "checkbox_1") == "true", + "Confirm" => Arr::get_boolean($form, "checkbox_1"), "Referee Email" => Arr::get($form, "email_1"), "Referee Phone" => Arr::get($form, "phone_1"), ); diff --git a/src/classes/cache/cache.class.php b/src/classes/cache/cache.class.php index 14190448..acd114f8 100644 --- a/src/classes/cache/cache.class.php +++ b/src/classes/cache/cache.class.php @@ -353,19 +353,23 @@ private static function get_or_set(string $id, callable $callable, array $args = // get path to cache file $path = self::get_cache_file_path($id); + $get_current_value = fn() => Serialise::parse(IO::file_get_contents($path)); // if the file exists, and the cache file has not expired, read and unserialise the value $last_modified = self::get_last_modified($path); if (time() - $last_modified < self::$duration_in_seconds) { - return Serialise::parse(IO::file_get_contents($path)); + return $get_current_value(); } // get a fresh value and serialise it to the cache $value = call_user_func($callable, ...$args); - file_put_contents($path, Serialise::store($value)); + if ($value !== false) { + file_put_contents($path, Serialise::store($value)); + return $value; + } - // return value - return $value; + // failed to get fresh value so return current cache value + return $get_current_value(); } /** diff --git a/src/classes/preload/preload.class.php b/src/classes/preload/preload.class.php index b7885931..c553647d 100644 --- a/src/classes/preload/preload.class.php +++ b/src/classes/preload/preload.class.php @@ -34,13 +34,8 @@ public static function get_events(): array // clear events cache Cache::clear_events(); - // build query to preload events for this month - $today = new DateTimeImmutable(timezone: C::$events->timezone); - $start_of_month = $today->modify("first day of")->format(C::$formats->sortable_date); - $query = http_build_query(array("date_start" => $start_of_month)); - // get events - Cache::get_events($query, fn($q) => Events::get_events($q), true); + Cache::get_events(Events::get_default_query(), fn($q) => Events::get_events($q), true); }); } diff --git a/src/pages/events/events.class.php b/src/pages/events/events.class.php index 15c344b5..b1242086 100644 --- a/src/pages/events/events.class.php +++ b/src/pages/events/events.class.php @@ -76,6 +76,18 @@ private static function get_query(array $values): string return http_build_query($query); } + /** + * Return default events query to retrieve events for the current month. + * + * @return string URL-encoded query (using http_build_query()). + */ + public static function get_default_query(): string + { + $today = new DateTimeImmutable(timezone: C::$events->timezone); + $start_of_month = $today->modify("first day of")->format(C::$formats->sortable_date); + return self::get_query(array("date_start" => $start_of_month)); + } + /** * Get events from Church Suite matching the query. * diff --git a/src/pages/home/home.class.php b/src/pages/home/home.class.php index ce53ca7e..9290d5df 100644 --- a/src/pages/home/home.class.php +++ b/src/pages/home/home.class.php @@ -3,9 +3,12 @@ namespace Obadiah\Pages\Home; use DateInterval; +use DateTime; use DateTimeImmutable; use Obadiah\App; +use Obadiah\Cache\Cache; use Obadiah\Config\Config as C; +use Obadiah\Pages\Events\Events; use Obadiah\Pages\Home\Index_Model; use Obadiah\Response\View; use Obadiah\Rota\Rota; @@ -36,11 +39,23 @@ public function index_get(): View "api" => C::$login->api ); + $stale_cache = new DateTime("now", C::$events->timezone); + $stale_cache->sub(new DateInterval(sprintf("PT%sS", C::$cache->duration_in_seconds))); + $check_cache = fn(int $timestamp) => $timestamp > $stale_cache->getTimestamp(); + return new View("home", model: new Index_Model( this_week: $this_week, upcoming: Rota::upcoming_sundays(), refresh_print: $refresh_print, - refresh_feed: $refresh_feed + refresh_feed: $refresh_feed, + caches_check: array( + "bible" => $check_cache(Cache::get_bible_plan_last_modified()), + "events" => $check_cache(Cache::get_events_last_modified(Events::get_default_query())), + "lectionary" => $check_cache(Cache::get_lectionary_last_modified()), + "people" => $check_cache(Cache::get_people_last_modified()), + "refresh" => $check_cache(Cache::get_refresh_last_modified()), + "rota" => $check_cache(Cache::get_rota_last_modified()) + ) )); } } diff --git a/src/pages/home/index-model.class.php b/src/pages/home/index-model.class.php index f3118505..1c1ee8c7 100644 --- a/src/pages/home/index-model.class.php +++ b/src/pages/home/index-model.class.php @@ -15,11 +15,13 @@ class Index_Model * @param mixed[] $upcoming Rota filter values to show upcoming Sunday services. * @param mixed[] $refresh_print Query values to link to printable version of this month's refresh calendar. * @param mixed[] $refresh_feed Query values to enable refresh ICS feed. + * @param mixed[] $caches_check Whether or not caches have been updated recently. */ public function __construct( public readonly array $this_week, public readonly array $upcoming, public readonly array $refresh_print, - public readonly array $refresh_feed + public readonly array $refresh_feed, + public readonly array $caches_check, ) {} } diff --git a/src/pages/home/index-view.php b/src/pages/home/index-view.php index ecb1bfc1..c409ad05 100644 --- a/src/pages/home/index-view.php +++ b/src/pages/home/index-view.php @@ -34,6 +34,11 @@ is_admin) : ?>

Caches

+

+ caches_check as $key => $value): ?> + ✓" : "", ucfirst($key)); ?>
+ +

Reload caches (this happens automatically every duration_in_seconds / 60); ?> minutes).

diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 1608b909..a50f1521 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -2,12 +2,12 @@ "packages": [ { "name": "phpstan/phpstan", - "version": "2.1.40", - "version_normalized": "2.1.40.0", + "version": "2.1.54", + "version_normalized": "2.1.54.0", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", - "reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8be50c3992107dc837b17da4d140fbbdf9a5c5bd", + "reference": "8be50c3992107dc837b17da4d140fbbdf9a5c5bd", "shasum": "" }, "require": { @@ -16,7 +16,7 @@ "conflict": { "phpstan/phpstan-shim": "*" }, - "time": "2026-02-23T15:04:35+00:00", + "time": "2026-04-29T13:31:09+00:00", "bin": [ "phpstan", "phpstan.phar" diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index e0ffa247..3a259bce 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -1,9 +1,9 @@ array( 'name' => '__root__', - 'pretty_version' => '2.3.2.x-dev', - 'version' => '2.3.2.9999999-dev', - 'reference' => 'cd0e079c93e8c974483a2d9b19f8b8f0f453b8cf', + 'pretty_version' => '3.0.0.x-dev', + 'version' => '3.0.0.9999999-dev', + 'reference' => '88f6c2aba24a686009f9c616ef96da63664040d4', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -11,18 +11,18 @@ ), 'versions' => array( '__root__' => array( - 'pretty_version' => '2.3.2.x-dev', - 'version' => '2.3.2.9999999-dev', - 'reference' => 'cd0e079c93e8c974483a2d9b19f8b8f0f453b8cf', + 'pretty_version' => '3.0.0.x-dev', + 'version' => '3.0.0.9999999-dev', + 'reference' => '88f6c2aba24a686009f9c616ef96da63664040d4', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => false, ), 'phpstan/phpstan' => array( - 'pretty_version' => '2.1.40', - 'version' => '2.1.40.0', - 'reference' => '9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b', + 'pretty_version' => '2.1.54', + 'version' => '2.1.54.0', + 'reference' => '8be50c3992107dc837b17da4d140fbbdf9a5c5bd', 'type' => 'library', 'install_path' => __DIR__ . '/../phpstan/phpstan', 'aliases' => array(), diff --git a/vendor/phpstan/phpstan/bootstrap.php b/vendor/phpstan/phpstan/bootstrap.php index 889755e6..53370b56 100644 --- a/vendor/phpstan/phpstan/bootstrap.php +++ b/vendor/phpstan/phpstan/bootstrap.php @@ -27,8 +27,9 @@ final public static function loadClass(string $class): void { if (self::$composerAutoloader === null) { self::$composerAutoloader = require 'phar://' . __DIR__ . '/phpstan.phar/vendor/autoload.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/jetbrains/phpstorm-stubs/PhpStormStubsMap.php'; - require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/async/src/functions_include.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise/src/functions_include.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/ralouphie/getallheaders/src/getallheaders.php'; + require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/guzzlehttp/guzzle/src/functions_include.php'; } self::$composerAutoloader->loadClass($class); @@ -106,7 +107,7 @@ final public static function loadClass(string $class): void { if ( PHP_VERSION_ID < 80400 && empty ($GLOBALS['__composer_autoload_files']['9d2b9fc6db0f153a0a149fefb182415e']) - && !class_exists(\Symfony\Polyfill\Php83\Php84::class, false) + && !class_exists(\Symfony\Polyfill\Php84\Php84::class, false) ) { $GLOBALS['__composer_autoload_files']['9d2b9fc6db0f153a0a149fefb182415e'] = true; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php84/Php84.php'; @@ -116,7 +117,7 @@ final public static function loadClass(string $class): void { if ( PHP_VERSION_ID < 80500 && empty ($GLOBALS['__composer_autoload_files']['606a39d89246991a373564698c2d8383']) - && !class_exists(\Symfony\Polyfill\Php83\Php85::class, false) + && !class_exists(\Symfony\Polyfill\Php85\Php85::class, false) ) { $GLOBALS['__composer_autoload_files']['606a39d89246991a373564698c2d8383'] = true; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php85/Php85.php'; diff --git a/vendor/phpstan/phpstan/phpstan.phar b/vendor/phpstan/phpstan/phpstan.phar index c14530e6..d5e9ae7f 100755 Binary files a/vendor/phpstan/phpstan/phpstan.phar and b/vendor/phpstan/phpstan/phpstan.phar differ diff --git a/vendor/phpstan/phpstan/phpstan.phar.asc b/vendor/phpstan/phpstan/phpstan.phar.asc index 55bd625b..1de20682 100644 --- a/vendor/phpstan/phpstan/phpstan.phar.asc +++ b/vendor/phpstan/phpstan/phpstan.phar.asc @@ -1,16 +1,16 @@ -----BEGIN PGP SIGNATURE----- -iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmmcbFMACgkQUcZzBf/C -5cCXJQ//dTcq8n9tnP+EKcwojw4dFGwRrTZbCJ1qT2Je2pNx02xCINc3nLuVVRCj -gvWllF1k2pWCYWMIKNPkHYVHq22TcbBsqlx25LS6USV767gI+qW0HoiG9ed+fmws -GCfC7grDh33PUGKusjogOh8pRKmVWYzQKWDefRodgeNBjDiMJv65QGARFsaPuQcy -DujnO4ERCNalWyrVnzEZtM1PMp7y7lLPbedbzXW9Aq0oybKu/JGQILeIcFlqZblB -iumI2Z/afTeDHQJ1faJdR3XPDKvwHE1R9FRoJBeQptazueloWB5gdW5VOf4Wsyq2 -fzYEkYLpOwQSZpfzS/HUvBJv7jaYXMAyb1Kt2ABJd+TvyNCGIS9z1Z+amjuYHKXh -IgbOAdoqS6yvh6frTH2qoCqmjjPkvX10nDb7trKcYDuW66rG22Vf5M2878Z+c5vf -siY7PjoH5OJcyaz8ptqQX5p5Mhx8owO4R9uVN5D9oEX0/V5SLBawJK/8DIdDkaLw -OzDVclHwQqHWhYHSsFfdaTtOGr1CXbJlwmRgU/yPisrCnqNjidAgTN7ta+9slxY2 -GbrwCfCQ+V/Wm01xDGMMFxzO85pWOdtfuLx2yekJyYNcKgjQshUGWnwQPcx5aRls -+dOBGMmRftJRaHjZWA+/feJOPrj4DfylE3YaUv3p8e0RLrp2zwk= -=Be8w +iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmnyB+sACgkQUcZzBf/C +5cCc/w//RkO2T6MfMe/Fh1MeWAtdvqVCBDG0x1SwP0Z4UmhW2/EyxPlgP7LtyZOr +IiivLmTQlv9VgNsyKrVJjvo6WH4mQ39s6yyGHsRJxtQqeZRHQWFkCz/SykwCFJ8e +X7vhJsW6UzvboA1/zvjkuRnOr1JVt2DWRVN2P5STK+hk/hizBE/ec404Boc/iD7A +0WGEnhnDGsyKfwB1NHhiWiqUrZ3Wo8x17wyeZsvmH/SL2oimfEjPe3oGm37CKbjr +ztSGnCXfO5EWHxFOIs5O1xbW7Lz13EboQNzpsJyQVpjVwEdLq8xt2LWxnRN7gQTd +5JV5pygmAFhrf44xoFa20HLK2ia14izA+dMTXvTBRHDPJzPaVo2wW3Nt4qg+WCdT +TrN7IAWx88OjZ/4rAKIp3SDzNj3TsN9KxzriCokzZmExipkZL4C+rDLkqhwEhrwE +W+kSGTz0egQeNwBA1FBWKmiBzMYr+rF2uwy7Oud2BHwnq1N0n5tpS5Djsti0fFcp +YQdNt27e7yjV4LO6O7h7uK0r7u0QZ7Pw0En9sU9FC5PQsEmzP22aUoFpqEnzNI2g +7MbHRO5qKmYI7f991RFOj/utbuk5d17ByAOLroJ8Ub8B2qIVEczTPwtYJ06Wf59I +ZRQDRoWjv4q8JosQ9xclS2nt6S//SMmeXrJLMh5t3u65vQN9OcI= +=H59Q -----END PGP SIGNATURE-----