Skip to content

Allow filtering embedded albums/streams by author using data-author attribute#4083

Merged
ildyria merged 6 commits intoLycheeOrg:masterfrom
cdzombak:cdz-embed-author-filter
Feb 12, 2026
Merged

Allow filtering embedded albums/streams by author using data-author attribute#4083
ildyria merged 6 commits intoLycheeOrg:masterfrom
cdzombak:cdz-embed-author-filter

Conversation

@cdzombak
Copy link
Contributor

@cdzombak cdzombak commented Feb 12, 2026

This PR allows filtering embedded albums/streams by setting the data-author attribute to a Lychee user's username. Only that user's photos will be included in the embed.

As of 62fa810 , multiple usernames may be included in the data-author attribute, separated by commas.

This feature was developed with Claude Code and tested manually.

Summary by CodeRabbit

  • New Features

    • Added author filter for embed configurations so embeds can show photos from specific uploader(s).
    • Added data-author HTML attribute support for embed elements; author setting is respected for album and public stream embeds.
  • Tests

    • Added tests covering single/multi-author filters, non-existent authors, and pagination with author filtering.

…ttribute

This commit allows filtering embedded albums/streams by setting the data-author attribute to a Lychee user's username. Only that user's photos will be included in the embed.

This feature was developed with Claude Code and tested manually.
@cdzombak cdzombak requested a review from a team as a code owner February 12, 2026 19:07
@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

Adds author-based filtering to embed flows: frontend UI/config/types/API now accept an optional author, which is sent as a query parameter; backend request/controller and photo queries accept and apply author filters; new tests cover single, multi, non-existent author, and pagination behaviors.

Changes

Cohort / File(s) Summary
Demo & Embed HTML
.ai/embeds/demo.html
Added Author Filter input and updated example data-attribute blocks to include data-author usage.
Frontend: config & types
resources/js/embed/types.ts, resources/js/embed/config.ts
Added optional author?: string to EmbedConfig; parse data-author and include author in normalized config.
Frontend: API & widget
resources/js/embed/api.ts, resources/js/embed/components/EmbedWidget.vue
Extended fetchAlbum and fetchStream signatures to accept optional author; include author in constructed query parameters and pass through from widget component.
Backend: request & controller
app/Http/Requests/Embed/EmbededRequest.php, app/Http/Controllers/Gallery/EmbedController.php
Added authors property to request (parses author query into array); controller methods now forward authors to photo loaders; findPublicPhotos and loadAlbumPhotos signatures updated to accept optional author list and apply whereHas/when filters.
Tests: embed album & stream
tests/Feature_v2/Embed/EmbedAlbumTest.php, tests/Feature_v2/Embed/EmbedStreamTest.php
Added tests for author filter: single-author, multi-author, non-existent user, and pagination behavior; setups add/remove extra photos to validate filtered counts and returned results.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through code with a twitch and a cheer,
I added an author so photos appear clear,
From demo to API, and queries that seek,
Tests guard the burrow — the feature's unique! 📷✨

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 93.75% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into master

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
app/Http/Requests/Embed/EmbededRequest.php (1)

82-89: Author parsing logic looks correct and well-guarded.

The is_string() check, trimming, and empty-filtering are all good defensive measures. One minor consideration: there's no upper bound on the number of authors that can be specified. A malicious client could pass a very long comma-separated list, which would generate a large WHERE IN clause downstream. Consider capping the array size (e.g., 50) similar to how limit is capped at 500.

💡 Optional: cap the number of authors
 		$author = $this->query('author', null);
 		if ($author !== null && is_string($author) && $author !== '') {
 			$authors = array_filter(array_map('trim', explode(',', $author)), fn ($v) => $v !== '');
-			if (count($authors) > 0) {
-				$this->authors = array_values($authors);
+			if (count($authors) > 0) {
+				$this->authors = array_values(array_slice($authors, 0, 50));
 			}
 		}
.ai/embeds/demo.html (1)

357-366: Data-attribute example for author filtering is a good addition.

The new example clearly demonstrates how to use data-author in stream mode. Consider also noting in the Stream Mode JS example (around line 328-338) that author can be passed as a config option, for completeness with both integration approaches.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
app/Http/Requests/Embed/EmbededRequest.php (1)

81-85: Consider adding length/character validation for the author parameter.

The author value is user-supplied and used directly in a database query. While Laravel's query builder prevents SQL injection, there's no upper bound on string length. Consider trimming and capping it (e.g., 255 chars to match a typical username column length), consistent with how limit and sort are validated above.

💡 Suggested hardening
 		// Parse author filter
 		$author = $this->query('author', null);
-		if ($author !== null && is_string($author) && $author !== '') {
-			$this->author = $author;
+		if ($author !== null && is_string($author) && $author !== '' && strlen($author) <= 255) {
+			$this->author = trim($author);
 		}
app/Http/Controllers/Gallery/EmbedController.php (1)

159-165: Author filter pattern in loadAlbumPhotos is functional but slightly duplicative.

The $author_filter closure is clean. Note that $album->photos()->getQuery() is called twice (lines 163 and 165), producing two independent query builder instances. This is correct, but you could save a line by extracting a base query builder if desired. Not blocking.

♻️ Optional: extract base query
 	private function loadAlbumPhotos(BaseAlbum $album, ?int $limit = null, int $offset = 0, ?string $sort = null, ?string $author = null): void
 	{
 		$author_filter = fn ($q) => $author !== null ? $q->whereHas('owner', fn ($q2) => $q2->where('username', $author)) : $q;
 
-		$total_photos = $author_filter($album->photos()->getQuery())->count();
-
-		$photos_query = $author_filter($album->photos()->getQuery());
+		$base_query = fn () => $author_filter($album->photos()->getQuery());
+
+		$total_photos = $base_query()->count();
+		$photos_query = $base_query();
tests/Feature_v2/Embed/EmbedStreamTest.php (1)

323-339: Author-filter exclusion assertion is weak — photo1 is already in a private album.

The assertion on line 338 checks that photo1 (owned by userMayUpload1) is absent, but photo1 lives in a private album, so it would be excluded even without the author filter. This doesn't actually prove the filter works for the stream endpoint.

Compare with EmbedAlbumTest::testAuthorFilterReturnsOnlyMatchingPhotos (lines 181–210 in EmbedAlbumTest.php), which creates a second photo in the same public album owned by a different user, then verifies it is excluded. Consider the same approach here: factory-create a photo owned by userMayUpload1 in album4, assert it appears without the filter, and assert it's gone with the filter.

Suggested approach
 public function testAuthorFilterReturnsOnlyMatchingPhotos(): void
 {
-    // photo4 and subPhoto4 are public, both owned by userLocked
-    $response = $this->getJson('Embed/stream?author=' . $this->userLocked->username);
+    // Add a photo owned by a different user to the same public album
+    /** `@disregard` */
+    $otherPhoto = Photo::factory()->owned_by($this->userMayUpload1)->with_GPS_coordinates()->in($this->album4)->create();
+
+    // Without author filter: should include the other user's photo
+    $response = $this->getJson('Embed/stream');
     $this->assertOk($response);
+    $allPhotoIds = collect($response->json('photos'))->pluck('id')->toArray();
+    $this->assertContains($otherPhoto->id, $allPhotoIds);
 
+    // With author filter: should return only userLocked's photos
+    $response = $this->getJson('Embed/stream?author=' . $this->userLocked->username);
+    $this->assertOk($response);
     $data = $response->json();
     $photoIds = collect($data['photos'])->pluck('id')->toArray();
 
-    // Photos owned by userLocked in public albums should be included
     $this->assertContains($this->photo4->id, $photoIds, 'photo4 owned by userLocked should be included');
     $this->assertContains($this->subPhoto4->id, $photoIds, 'subPhoto4 owned by userLocked should be included');
+    $this->assertNotContains($otherPhoto->id, $photoIds, 'otherPhoto owned by userMayUpload1 should be excluded');
 
-    // Photos owned by other users should NOT be included (they are also in private albums,
-    // but the author filter should exclude them regardless)
-    $this->assertNotContains($this->photo1->id, $photoIds, 'photo1 owned by userMayUpload1 should not be included');
+    $otherPhoto->delete();
 }

cdzombak and others added 5 commits February 12, 2026 14:18
Per review feedback from ildyria.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Per review feedback from CodeRabbit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous test asserted that photo1 was excluded, but photo1 is in a
private album and would be excluded regardless of the author filter. Now
the test creates a photo by a different user in the same public album,
verifying the author filter actually excludes it.

Per review feedback from CodeRabbit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Per review feedback from CodeRabbit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cdzombak cdzombak requested a review from ildyria February 12, 2026 19:47
@codecov
Copy link

codecov bot commented Feb 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.20%. Comparing base (26dcb76) to head (4437870).
⚠️ Report is 2 commits behind head on master.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ildyria ildyria merged commit 79f8860 into LycheeOrg:master Feb 12, 2026
44 checks passed
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.

2 participants