Skip to content

Push a full avatar URL when a group is edited - #901

Open
edwh wants to merge 1 commit into
developfrom
wordpress-group-avatar-full-url
Open

Push a full avatar URL when a group is edited#901
edwh wants to merge 1 commit into
developfrom
wordpress-group-avatar-full-url

Conversation

@edwh

@edwh edwh commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

The bug

Group avatars are broken on therestartproject.org for any group that has been edited since the Laravel port.

Root cause

The create and edit paths built group_avatar_url two different ways.

CreateWordpressPostForGroup uses the model:

['key' => 'group_avatar_url', 'value' => $group->groupImagePath()],   // asset('/uploads/mid_'.$path) → absolute

EditWordpressPostForGroup used the event payload, which API\GroupController::updateGroup built like this:

$group_avatar = env('UPLOADS_URL').'mid_'.$group_avatar;

UPLOADS_URL has never existed in the Laravel app. It was a PHP constant in the pre-Laravel Fixometer
(f0dac97) and was not carried over — it is not in .env, .env.example, config/, or any of the fly
configs. So env('UPLOADS_URL') returns null and the pushed value collapses to a bare mid_….png, with no
host and not even the /uploads/ directory. WordPress renders that verbatim, so the image 404s.

Approving a group wrote a good URL; the next edit overwrote it with a broken one. That's exactly the
difference between the two pages above.

(The else branch had the same problem, and sent the literal string 'null' for a group with no image.)

The fix

EditWordpressPostForGroup now reads its payload off the group — which updateGroup has already saved —
instead of off the event array, so it can't drift from CreateWordpressPostForGroup again. That also removes
a latent Undefined array key crash: the listener indexed $data['website'], $data['latitude'] etc.
directly, so any caller firing EditGroup with a partial array would have failed the queued job.

The dead UPLOADS_URL URL-building in API\GroupController::updateGroup is gone; the image upload itself
(the only part with a side effect) stays.

Backfill

php artisan wordpress:group:fix-avatars

Walks groups with a wordpress_post_id, reads the post's group_avatar_url, and rewrites any value that
isn't an absolute URL to $group->groupImagePath(). It sends only that one custom field, carrying the
field's existing WordPress id so the value is replaced rather than duplicated.

--dry-run    report without touching WordPress
--id=        restrict to specific group ids (repeatable)
--limit=     stop after N groups
--sleep=     seconds between groups

The client is injected into handle() rather than the constructor. Artisan resolves commands when it boots,
which can be long before the command runs — with a constructor dependency the command kept whatever was bound
at boot time, and a test binding a mock afterwards got ignored and made a real XML-RPC call out to
therestartproject.test. That only showed up when another test had already booted Artisan (via
processQueuedNotifications() in TestCase::setUp), so the test passed alone and failed in suite order.

Suggested run:

php artisan wordpress:group:fix-avatars --dry-run
php artisan wordpress:group:fix-avatars --sleep=0.5

Scale on live

I swept all 623 group pages in https://therestartproject.org/group-sitemap.xml. 22 are broken; the other 601
are fine.

group id slug name
10 restarters-torino Restarters Torino
49 portsmouth-repair-cafe Repair Café Portsmouth
58 restarters-tooting Restarters Tooting
140 repair-cafe-pavia Repair Cafe Pavia
423 west-central-london-fixers-north-kensington West Central London Fixers – Kensington & Chelsea
483 walton-on-thames-repair-cafe Repair Cafe Walton-on-Thames
529 ulverston-repair-cafe Ulverston Repair Café
824 sustainable-napier Napier Repair Cafe
969 tower-hamlets-fixers Tower Hamlets Repair Cafes
1040 winterbourne-repair-cafe Winterbourne Repair Cafe
1086 bowes-bounds-repair-cafe Bowes & Bounds Repair Café
1221 ramsgate-repair-cafe-and-town-shed Ramsgate Repair Cafe
1247 repair-cafe-mo-annoeulin Repair Café MO Annoeullin
1269 godmanchester-repair-cafe Godmanchester Repair Cafe
1299 kidbrooke-repair-cafe Kidbrooke Repair Café
1305 remake-mending-circle Remake Mending Circle
1313 centre-for-computing-history Centre for Computing History
1383 tamaki-zero-waste-hub-repair-cafe Tāmaki Zero Waste Hub Repair Café
1401 tullymeadow-repair-cafe Tullymeadow Repair Cafe
1402 newlands-and-waterlooville-repair-cafe Newlands and Waterlooville Repair Cafe
1426 repair-cafe-titchfield Repair Café Titchfield
1447 wimbledon-repair-cafe Wimbledon Repair Cafe

So a targeted run is also an option:

php artisan wordpress:group:fix-avatars --id=10 --id=49 --id=58 --id=140 --id=423 --id=483 --id=529 \
  --id=824 --id=969 --id=1040 --id=1086 --id=1221 --id=1247 --id=1269 --id=1299 --id=1305 --id=1313 \
  --id=1383 --id=1401 --id=1402 --id=1426 --id=1447

Tests

WordpressGroupPushTest gains three cases: the avatar pushed on edit must be the absolute
groupImagePath(), a group with no image must still get an absolute URL rather than 'null', and create and
edit must push the same value. All three fail on the old listener (the first with
Failed asserting that two strings are equal).

WordpressFixGroupAvatarsTest covers the backfill: relative paths and the literal 'null' are rewritten,
already-correct posts are left alone, --dry-run writes nothing, a group whose post has been deleted is
reported without aborting the run, and the command refuses when wordpress_integration is off.

Not affected

The event/party push (CreateWordpressPostForEvent, EditWordpressPostForEvent) never sends an image field,
and SyncGroups doesn't touch group_avatar_url, so neither could cause or repair this.


🤖 Generated with Claude Code

https://claude.ai/code/session_01WRv64fqMT3fAveD6NFgnmR

Editing a group pushed group_avatar_url to WordPress as
env('UPLOADS_URL').'mid_'.$path.  UPLOADS_URL was a constant in the
pre-Laravel Fixometer and was never carried over, so the value collapsed
to a bare "mid_xxx.png" with no host, and WordPress rendered an image
that 404s.  Approving a group wrote a good URL via groupImagePath(); the
next edit overwrote it with a broken one.  22 of the 623 group pages on
therestartproject.org are currently affected.

EditWordpressPostForGroup now reads its whole payload off the group,
which updateGroup has already saved, rather than off the event array, so
it can't drift from CreateWordpressPostForGroup again.  That also
removes a latent Undefined array key crash on a partial payload.

Adds wordpress:group:fix-avatars to backfill the posts that are already
wrong.  Its WordpressClient is injected into handle() rather than the
constructor: Artisan resolves commands when it boots, so a constructor
dependency is pinned to whatever was bound at boot time, and a test
binding a mock afterwards was ignored and made a live XML-RPC call.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WRv64fqMT3fAveD6NFgnmR
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
8.2% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant