The media_types closure validator called $value->toArray(), but the
value is a plain PHP array (built via explode()), causing a fatal
'Call to a member function toArray() on array' and a 500 when opening
the Directory admin view.
Normalize to an array before in_array() checks, handling both array
and Collection inputs.
Fixes#7311
Empty lang/x/web.php values (untranslated Crowdin placeholders) were
being exported as empty strings, overriding the English UI fallback and
breaking non-English languages. Recursively strip empty strings before
writing the JSON build files, then regenerate all language files.
SnowflakeService::next() read the sequence with Cache::get() and, on the
non-initial branch, incremented the cache store but never refreshed the
local $seq. As a result the first two IDs in a fresh sequence both used
seq=1 (values ran 1, 1, 2, 3, ...). Two IDs generated in the same
millisecond with the same (randomly chosen) datacenter/worker then
produced identical snowflakes, causing intermittent
'UNIQUE constraint failed: statuses.id' errors — e.g. the flaky
SeasonalAggregationTest which rapidly creates several statuses.
Use Cache::increment()'s atomic post-increment return value as the
sequence so every call gets a distinct seq (1, 2, 3, ...), reseeding
when the store is empty/non-numeric and preserving the 4095 wraparound.
Larastan v3.12.0 added the larastan.relationExistence rule, which
detects Eloquent relations by their declared return type. statuses()
defined the hasMany relation but lacked the ': HasMany' return type, so
the rule could not recognize it and flagged $filter->load([...,
'statuses']) in CustomFilterController.php as referencing a missing
relation. Add the return type to match the sibling keywords() method.
Verified with larastan/larastan v3.12.0 (No errors).
For the rare cases where SQL must differ between MySQL and MariaDB,
add DatabaseDriver::isMysql()/isMariadb() and db_is_mysql()/db_is_mariadb()
alongside the existing grouped isMysqlMaria()/db_is_mysql_maria().
Laravel 11 exposes MariaDB as a dedicated 'mariadb' driver, so
config('database.default') === 'mysql' checks silently misclassified
MariaDB as the non-mysql (postgres) branch.
- Add App\Util\Database\DatabaseDriver with isMysqlLike()/isPgsql()
plus db_is_mysql_like()/db_is_pgsql() global helpers.
- Route all database.default driver checks through the helpers so
MySQL and MariaDB are treated as one group.
- Use '' (not null) for share/compose caption+rendered, valid whether
the column is nullable or NOT NULL (it is NOT NULL on MySQL/MariaDB).
- Guard pgsql strtolower() in registration against missing fields.
- Scope CustomEmoji::duplicateShortcodes to the grouped column for
Postgres GROUP BY validity.
- Remove stale Postgres guard in status:dedup; use havingRaw for
cross-driver HAVING.
queueDelivery() runs synchronously from the v1 follow/unfollow endpoints
(via Helpers::sendSignedObject), which commit local state before delivery
and have no try/catch. After the Http::send() rewrite, a ConnectionException
from a momentarily-unreachable remote was rethrown out of queueDelivery(),
turning a best-effort delivery into a 500 for the user after the follow/
unfollow was already persisted. For unfollows, a retry then hit the
isFollowing==false branch and never re-sent the Undo, diverging state.
Treat transport failures (ConnectionException) as best-effort on this
single-delivery path: log, record host health, and return without
propagating. Other exception types (invalid sender/destination, signing,
serialization) still throw, matching pre-rewrite precondition behavior.
Also widen SendUpdateActor's per-user catch from HttpException to Throwable
so a single bad host no longer aborts a fleet-wide actor update (the old
HttpException catch is dead for ConnectionException/invalid-destination).