diff --git a/app/Enum/SmartAlbumType.php b/app/Enum/SmartAlbumType.php index c6d6f72ea73..f917b6d37f1 100644 --- a/app/Enum/SmartAlbumType.php +++ b/app/Enum/SmartAlbumType.php @@ -10,6 +10,7 @@ use App\Enum\Traits\DecorateBackedEnum; use App\Repositories\ConfigManager; +use Illuminate\Support\Facades\Auth; use LycheeVerify\Contract\VerifyInterface; /** @@ -31,6 +32,8 @@ enum SmartAlbumType: string case FOUR_STARS = 'four_stars'; case FIVE_STARS = 'five_stars'; case BEST_PICTURES = 'best_pictures'; + case MY_RATED_PICTURES = 'my_rated_pictures'; + case MY_BEST_PICTURES = 'my_best_pictures'; /** * Return whether the smart album is enabled. @@ -53,6 +56,10 @@ public function is_enabled(ConfigManager $config_manager): bool self::FIVE_STARS => $config_manager->getValueAsBool('enable_5_stars'), // Best Pictures requires both config AND Lychee SE license self::BEST_PICTURES => $config_manager->getValueAsBool('enable_best_pictures') && $this->isLycheeSEActive(), + // My Rated Pictures shows all photos the user has rated (authenticated users only) + self::MY_RATED_PICTURES => Auth::check() && $config_manager->getValueAsBool('enable_my_rated_pictures'), + // My Best Pictures requires authenticated user, config, AND Lychee SE license + self::MY_BEST_PICTURES => Auth::check() && $config_manager->getValueAsBool('enable_my_best_pictures') && $this->isLycheeSEActive(), }; } @@ -71,4 +78,29 @@ private function isLycheeSEActive(): bool return false; } } + + /** + * Whether the album requires the user to have upload rights. + * + * @return bool + */ + public function require_upload_rights(): bool + { + return match ($this) { + self::UNSORTED, + self::STARRED, + self::RECENT, + self::ON_THIS_DAY, + self::UNRATED, + self::UNTAGGED => true, + self::ONE_STAR, + self::TWO_STARS, + self::THREE_STARS, + self::FOUR_STARS, + self::FIVE_STARS, + self::BEST_PICTURES, + self::MY_RATED_PICTURES, + self::MY_BEST_PICTURES => false, + }; + } } \ No newline at end of file diff --git a/app/Factories/AlbumFactory.php b/app/Factories/AlbumFactory.php index 21087ca02a5..1ce8753869f 100644 --- a/app/Factories/AlbumFactory.php +++ b/app/Factories/AlbumFactory.php @@ -20,6 +20,8 @@ use App\SmartAlbums\BestPicturesAlbum; use App\SmartAlbums\FiveStarsAlbum; use App\SmartAlbums\FourStarsAlbum; +use App\SmartAlbums\MyBestPicturesAlbum; +use App\SmartAlbums\MyRatedPicturesAlbum; use App\SmartAlbums\OneStarAlbum; use App\SmartAlbums\OnThisDayAlbum; use App\SmartAlbums\RecentAlbum; @@ -47,6 +49,8 @@ class AlbumFactory SmartAlbumType::FOUR_STARS->value => FourStarsAlbum::class, SmartAlbumType::FIVE_STARS->value => FiveStarsAlbum::class, SmartAlbumType::BEST_PICTURES->value => BestPicturesAlbum::class, + SmartAlbumType::MY_RATED_PICTURES->value => MyRatedPicturesAlbum::class, + SmartAlbumType::MY_BEST_PICTURES->value => MyBestPicturesAlbum::class, ]; public function __construct( diff --git a/app/Http/Middleware/ConfigIntegrity.php b/app/Http/Middleware/ConfigIntegrity.php index 6506f059722..86e981f36e3 100644 --- a/app/Http/Middleware/ConfigIntegrity.php +++ b/app/Http/Middleware/ConfigIntegrity.php @@ -98,6 +98,8 @@ class ConfigIntegrity 'rating_album_view_mode', 'enable_best_pictures', 'best_pictures_count', + 'enable_my_best_pictures', + 'my_best_pictures_count', ]; public const PRO_FIELDS = [ diff --git a/app/Policies/AlbumPolicy.php b/app/Policies/AlbumPolicy.php index c339698b95d..add1569e127 100644 --- a/app/Policies/AlbumPolicy.php +++ b/app/Policies/AlbumPolicy.php @@ -73,7 +73,12 @@ public function isOwner(?User $user, BaseAlbum|BaseAlbumImpl $album): bool */ public function canSee(?User $user, BaseSmartAlbum $smart_album): bool { + // We do not require upload rights for all albums + $require_upload_rights = SmartAlbumType::from($smart_album->get_id())->require_upload_rights(); + return ($user?->may_upload === true) || + ($user?->may_upload === false && !$require_upload_rights) || + // if $user is null then we require that the album is public. $smart_album->public_permissions() !== null; } diff --git a/app/SmartAlbums/BaseSmartAlbum.php b/app/SmartAlbums/BaseSmartAlbum.php index 7a018df2edc..25d1d820591 100644 --- a/app/SmartAlbums/BaseSmartAlbum.php +++ b/app/SmartAlbums/BaseSmartAlbum.php @@ -24,6 +24,7 @@ use App\Models\Extensions\ToArrayThrowsNotImplemented; use App\Models\Extensions\UTCBasedTimes; use App\Models\Photo; +use App\Models\User; use App\Policies\AlbumPolicy; use App\Policies\PhotoQueryPolicy; use App\Repositories\ConfigManager; @@ -108,6 +109,7 @@ public function get_photos(): LengthAwarePaginator */ public function photos(): Builder { + /** @var ?User $user */ $user = Auth::user(); $unlocked_album_ids = AlbumPolicy::getUnlockedAlbumIDs(); diff --git a/app/SmartAlbums/MyBestPicturesAlbum.php b/app/SmartAlbums/MyBestPicturesAlbum.php new file mode 100644 index 00000000000..c58a314a44b --- /dev/null +++ b/app/SmartAlbums/MyBestPicturesAlbum.php @@ -0,0 +1,135 @@ +value; + + /** + * @throws ConfigurationKeyMissingException + * @throws FrameworkException + */ + protected function __construct() + { + // The condition filters for photos that have been rated by current user + // The tie-inclusion logic is handled in getPhotosAttribute() + parent::__construct( + id: SmartAlbumType::MY_BEST_PICTURES, + smart_condition: fn (Builder $q) => $q->whereHas('ratings', function ($query): void { + $query->where('user_id', '=', Auth::id() ?? 0); + }) + ); + } + + public static function getInstance(): self + { + return new self(); + } + + /** + * Override to implement tie-inclusion logic for top N photos rated by current user. + * + * @return LengthAwarePaginator + * + * @throws InvalidOrderDirectionException + * @throws InvalidQueryModelException + */ + protected function getPhotosAttribute(): LengthAwarePaginator + { + if ($this->photos !== null) { + return $this->photos; + } + + $limit = $this->config_manager->getValueAsInt('my_best_pictures_count'); + + // Get the Nth photo's rating to determine the cutoff + $cutoff_rating = $this->getCutoffRating($limit); + + if ($cutoff_rating === null) { + // No photos with ratings from this user, return empty paginator + $this->photos = new LengthAwarePaginator([], 0, $limit); + + return $this->photos; + } + + // Include all photos with user rating >= cutoff (this handles ties) + // We need to join with photo_ratings to filter by the user's rating + $query = $this->photos() + ->join('photo_ratings as pr_filter', function ($join) use ($cutoff_rating): void { + $join->on('photos.id', '=', 'pr_filter.photo_id') + ->where('pr_filter.user_id', '=', Auth::id() ?? 0) + ->where('pr_filter.rating', '>=', $cutoff_rating); + }) + ->select('photos.*'); // Ensure we only select photo columns + + // Always sort by user's rating DESC for My Best Pictures + // We already have the join from above, so order by that rating + $query = $query->orderByRaw('pr_filter.rating DESC') + ->orderBy('photos.created_at', 'DESC'); + + /** @var LengthAwarePaginator $photos */ + $photos = $query->paginate($this->config_manager->getValueAsInt('photos_pagination_limit')); + + $this->photos = $photos; + + return $this->photos; + } + + /** + * Get the rating of the Nth photo (by current user) to use as the cutoff. + * + * @param int $limit the N for "top N photos" + * + * @return int|null the rating at position N, or null if fewer than N rated photos exist + */ + private function getCutoffRating(int $limit): ?int + { + $user_id = Auth::id() ?? 0; + + // Get the Nth highest rating from current user + $nth_rating = DB::table('photo_ratings') + ->where('user_id', '=', $user_id) + ->join('photos', 'photo_ratings.photo_id', '=', 'photos.id') + ->orderBy('photo_ratings.rating', 'DESC') + ->skip($limit - 1) + ->take(1) + ->value('photo_ratings.rating'); + + if ($nth_rating === null) { + // Fewer than N photos with ratings from this user exist + // Get the lowest rating among existing rated photos from this user + $lowest_rating = DB::table('photo_ratings') + ->where('user_id', '=', $user_id) + ->join('photos', 'photo_ratings.photo_id', '=', 'photos.id') + ->orderBy('photo_ratings.rating', 'ASC') + ->value('photo_ratings.rating'); + + return $lowest_rating; + } + + return $nth_rating; + } +} diff --git a/app/SmartAlbums/MyRatedPicturesAlbum.php b/app/SmartAlbums/MyRatedPicturesAlbum.php new file mode 100644 index 00000000000..92c0c78cd85 --- /dev/null +++ b/app/SmartAlbums/MyRatedPicturesAlbum.php @@ -0,0 +1,71 @@ +value; + + /** + * @throws ConfigurationKeyMissingException + * @throws FrameworkException + */ + protected function __construct() + { + // The condition filters for photos that have been rated by current user + parent::__construct( + id: SmartAlbumType::MY_RATED_PICTURES, + smart_condition: fn (Builder $q) => $q->whereHas('ratings', function ($query): void { + $query->where('user_id', '=', Auth::id() ?? 0); + }) + ); + } + + public static function getInstance(): self + { + return new self(); + } + + /** + * Override to add custom ordering: user's rating DESC, then created_at DESC. + * + * @return \App\Eloquent\FixedQueryBuilder + * + * @throws InternalLycheeException + */ + public function photos(): Builder + { + // Get base query from parent (includes security filtering and smart condition) + $query = parent::photos(); + + // Add join with photo_ratings to access the user's rating for ordering + // Use leftJoin since the whereHas in smart_condition already ensures ratings exist + return $query + ->leftJoin('photo_ratings', function ($join): void { + $join->on('photos.id', '=', 'photo_ratings.photo_id') + ->where('photo_ratings.user_id', '=', Auth::id() ?? 0); + }) + ->orderByRaw('photo_ratings.rating DESC') + ->orderBy('photos.created_at', 'DESC') + ->select('photos.*'); // Ensure we only select photo columns + } +} diff --git a/composer.json b/composer.json index cb9ab9e0f37..abc88761e18 100644 --- a/composer.json +++ b/composer.json @@ -117,6 +117,7 @@ "mockery/mockery": "^1.5", "nunomaduro/collision": "^8.3", "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^11.0", "rector/rector": "^2.0", "tomasvotruba/class-leak": "^2.0" diff --git a/composer.lock b/composer.lock index 1fa76d7cf13..a3893e36683 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "88b1230fae44d4ff8f0bcbda09e6a7ee", + "content-hash": "eafed810f9420c9b19b56de549745c63", "packages": [ { "name": "apimatic/core", diff --git a/database/migrations/2026_01_28_163552_add_my_rated_pictures_config_keys.php b/database/migrations/2026_01_28_163552_add_my_rated_pictures_config_keys.php new file mode 100644 index 00000000000..55f9054ae4f --- /dev/null +++ b/database/migrations/2026_01_28_163552_add_my_rated_pictures_config_keys.php @@ -0,0 +1,61 @@ + + */ + public function getConfigs(): array + { + return [ + [ + 'key' => 'enable_my_rated_pictures', + 'value' => '1', + 'cat' => self::SMART_ALBUMS, + 'type_range' => self::BOOL, + 'description' => 'Enable My Rated Pictures smart album.', + 'details' => 'Shows all photos rated by the current user.', + 'is_secret' => false, + 'is_expert' => false, + 'order' => 50, + 'not_on_docker' => false, + 'level' => 0, + ], + [ + 'key' => 'enable_my_best_pictures', + 'value' => '1', + 'cat' => self::SMART_ALBUMS, + 'type_range' => self::BOOL, + 'description' => 'Enable My Best Pictures smart album.', + 'details' => 'Show top-rated photos by the current user.', + 'is_secret' => false, + 'is_expert' => false, + 'order' => 51, + 'not_on_docker' => false, + 'level' => 1, + ], + [ + 'key' => 'my_best_pictures_count', + 'value' => '50', + 'cat' => self::SMART_ALBUMS, + 'type_range' => self::POSITIVE, + 'description' => 'Number of photos in My Best Pictures album.', + 'details' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', + 'is_secret' => false, + 'is_expert' => false, + 'order' => 52, + 'not_on_docker' => false, + 'level' => 1, + ], + ]; + } +}; diff --git a/docs/specs/4-architecture/features/011-my-rated-smart-albums/plan.md b/docs/specs/4-architecture/features/011-my-rated-smart-albums/plan.md new file mode 100644 index 00000000000..10b778cbc91 --- /dev/null +++ b/docs/specs/4-architecture/features/011-my-rated-smart-albums/plan.md @@ -0,0 +1,357 @@ +# Feature Plan 011 – My Rated Pictures Smart Albums + +| Field | Value | +|-------|-------| +| Feature ID | 011 | +| Status | In Progress | +| Last updated | 2026-01-28 | +| Linked spec | [spec.md](./spec.md) | +| Linked tasks | [tasks.md](./tasks.md) | + +## Overview + +Implement two new smart albums that filter photos based on the current user's ratings: "My Rated Pictures" (all photos rated by user) and "My Best Pictures" (top N photos rated by user with tie-inclusive logic). These albums use `whereHas` queries on the `ratings` relationship to filter by `user_id` instead of `owner_id`. + +## Implementation Approach + +Follow existing smart album patterns: +1. Extend `BaseSmartAlbum` similar to `StarredAlbum` and `BestPicturesAlbum` +2. Add enum cases to `SmartAlbumType` +3. Add configuration keys for enable/disable and count +4. Add translation keys +5. Implement query logic with proper user filtering +6. Write comprehensive tests + +## Dependencies + +- Feature 001 (Photo Star Rating) - requires `photo_ratings` table and `PhotoRating` model +- `BaseSmartAlbum` infrastructure +- `PhotoQueryPolicy` for security filtering +- ConfigManager for settings + +## Increment Map + +### I1 – Add SmartAlbumType Enum Cases + +**Goal:** Add MY_RATED_PICTURES and MY_BEST_PICTURES to SmartAlbumType enum + +**Preconditions:** None + +**Steps:** +1. Read `app/Enum/SmartAlbumType.php` +2. Add `case MY_RATED_PICTURES = 'my_rated_pictures';` after BEST_PICTURES +3. Add `case MY_BEST_PICTURES = 'my_best_pictures';` after MY_RATED_PICTURES +4. Update `is_enabled()` method to include both new cases +5. For both cases, check Auth::check() first (hide from guest users) +6. For MY_BEST_PICTURES, also check config flag AND SE license (like BEST_PICTURES) +7. Add `use Illuminate\Support\Facades\Auth;` import at top of file + +**Commands:** +- `make phpstan` (verify types) +- `php artisan test --filter=SmartAlbumType` (if tests exist) + +**Exit:** SmartAlbumType enum has two new cases with proper enable checks + +**Implements:** FR-011-06, ENUM-011-01 + +--- + +### I2 – Add Translation Keys + +**Goal:** Add translation keys for album names and config descriptions + +**Preconditions:** I1 complete + +**Steps:** +1. Edit `lang/en/gallery.php`, add to `smart_album` array: + - `'my_rated_pictures' => 'My Rated Pictures',` + - `'my_best_pictures' => 'My Best Pictures',` +2. Edit `lang/en/all_settings.php`, add to `descriptions` array: + - `'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.',` + - `'enable_my_best_pictures' => 'Enable My Best Pictures smart album.',` + - `'my_best_pictures_count' => 'Number of top-rated photos to display in My Best Pictures smart album.',` + +**Commands:** +- None (no tests for translations) + +**Exit:** Translation keys exist for both albums + +**Implements:** NFR-011-04, I18N-011-01 through I18N-011-04 + +--- + +### I3 – Add Configuration Keys (Migration) + +**Goal:** Add database config entries for the new smart albums + +**Preconditions:** I2 complete + +**Steps:** +1. Create migration: `php artisan make:migration add_my_rated_pictures_config_keys` +2. Add three config entries in `up()`: + ```php + Configs::create([ + 'key' => 'enable_my_rated_pictures', + 'value' => '1', + 'cat' => 'Smart Albums', + 'type_range' => 'bool', + 'description' => 'Enable My Rated Pictures smart album.' + ]); + + Configs::create([ + 'key' => 'enable_my_best_pictures', + 'value' => '1', + 'cat' => 'Smart Albums', + 'type_range' => 'bool', + 'description' => 'Enable My Best Pictures smart album.' + ]); + + Configs::create([ + 'key' => 'my_best_pictures_count', + 'value' => '50', + 'cat' => 'Smart Albums', + 'type_range' => 'positive', + 'description' => 'Number of photos in My Best Pictures album.' + ]); + ``` +3. Add `down()` method to remove config keys +4. Run migration: `php artisan migrate` + +**Commands:** +- `php artisan migrate` +- `php artisan test` (verify no breaks) + +**Exit:** Three config keys exist in database + +**Implements:** CFG-011-01, CFG-011-02, CFG-011-03 + +--- + +### I4 – Implement MyRatedPicturesAlbum + +**Goal:** Create MyRatedPicturesAlbum smart album class + +**Preconditions:** I1-I3 complete + +**Steps:** +1. Create `app/SmartAlbums/MyRatedPicturesAlbum.php` +2. Extend BaseSmartAlbum +3. Implement singleton pattern with getInstance() +4. Override photos() method to add join with photo_ratings and user_id filter +5. Add sorting: order by rating DESC, created_at DESC +6. Ensure query only runs for authenticated users (return empty if guest) + +**Implementation:** +```php +value; + + protected function __construct() + { + parent::__construct( + id: SmartAlbumType::MY_RATED_PICTURES, + smart_condition: fn (Builder $q) => $q->whereHas('ratings', function ($query) { + $query->where('user_id', '=', Auth::id() ?? 0); + }) + ); + } + + public static function getInstance(): self + { + return new self(); + } + + public function photos(): Builder + { + // Get base query from parent + $query = parent::photos(); + + // Add ordering: user's rating DESC, then created_at DESC + // Join with photo_ratings to access the user's rating + return $query->leftJoin('photo_ratings', function ($join) { + $join->on('photos.id', '=', 'photo_ratings.photo_id') + ->where('photo_ratings.user_id', '=', Auth::id() ?? 0); + }) + ->orderByRaw('photo_ratings.rating DESC') + ->orderBy('photos.created_at', 'desc'); + } +} +``` + +**Commands:** +- `make phpstan` +- `php artisan test --filter=MyRatedPicturesAlbum` (after tests written) + +**Exit:** MyRatedPicturesAlbum class exists and compiles + +**Implements:** DO-011-01, FR-011-01 + +--- + +### I5 – Implement MyBestPicturesAlbum + +**Goal:** Create MyBestPicturesAlbum with tie-inclusive logic + +**Preconditions:** I4 complete + +**Steps:** +1. Create `app/SmartAlbums/MyBestPicturesAlbum.php` +2. Follow BestPicturesAlbum pattern but filter by user_id in ratings +3. Override getPhotosAttribute() to implement tie logic +4. Add getCutoffRating() helper method +5. Use my_best_pictures_count config + +**Implementation:** Similar to BestPicturesAlbum but with user_id filtering in whereHas + +**Commands:** +- `make phpstan` +- `php artisan test --filter=MyBestPicturesAlbum` (after tests written) + +**Exit:** MyBestPicturesAlbum class exists with tie logic + +**Implements:** DO-011-02, FR-011-02 + +--- + +### I6 – Unit Tests for MyRatedPicturesAlbum + +**Goal:** Test MyRatedPicturesAlbum query logic and edge cases + +**Preconditions:** I4 complete + +**Steps:** +1. Create `tests/Feature_v2/SmartAlbums/MyRatedPicturesAlbumTest.php` +2. Extend BaseApiWithDataTest +3. Test scenarios: + - S-011-01: Authenticated user sees rated photos + - S-011-03: Guest user cannot see album (not in list) + - S-011-05: User with 0 ratings sees empty result (but album is visible) + - S-011-06: User rates photo, appears in album + - S-011-12: Private photo not shown if no permission +4. Verify sorting: highest rated first, then newest + +**Commands:** +- `php artisan test --filter=MyRatedPicturesAlbumTest` + +**Exit:** All tests pass, coverage for scenarios + +**Implements:** Test strategy, S-011-01, S-011-03, S-011-05, S-011-06, S-011-12 + +--- + +### I7 – Unit Tests for MyBestPicturesAlbum + +**Goal:** Test MyBestPicturesAlbum tie logic and edge cases + +**Preconditions:** I5 complete + +**Steps:** +1. Create `tests/Feature_v2/SmartAlbums/MyBestPicturesAlbumTest.php` +2. Extend BaseApiWithDataTest +3. Test scenarios: + - S-011-02: Top N photos with ties + - S-011-04: Guest user cannot see album (not in list) + - S-011-07: All same rating, all included + - S-011-08: Exact N photos, no ties + - S-011-09: Tie at cutoff, all included + - S-011-11: No SE license, album disabled + +**Commands:** +- `php artisan test --filter=MyBestPicturesAlbumTest` + +**Exit:** All tests pass, tie logic verified + +**Implements:** Test strategy, S-011-02, S-011-04, S-011-07, S-011-08, S-011-09, S-011-11 + +--- + +### I8 – Integration Tests + +**Goal:** Test end-to-end album behavior with config flags + +**Preconditions:** I6-I7 complete + +**Steps:** +1. Create `tests/Feature_v2/SmartAlbums/MyRatedSmartAlbumsIntegrationTest.php` +2. Test config enable/disable (S-011-10) +3. Test SE license requirement for My Best Pictures (S-011-11) +4. Test photo visibility filtering (S-011-12) +5. Test interaction with rating updates + +**Commands:** +- `php artisan test --filter=MyRatedSmartAlbumsIntegrationTest` + +**Exit:** Integration tests pass + +**Implements:** S-011-10, S-011-11, FR-011-03, FR-011-04, FR-011-05 + +--- + +### I9 – Quality Gate & Documentation + +**Goal:** Run full quality gate and finalize documentation + +**Preconditions:** I1-I8 complete + +**Steps:** +1. Run PHP quality checks: + - `vendor/bin/php-cs-fixer fix` + - `php artisan test` + - `make phpstan` +2. Update knowledge map if needed +3. Mark all tasks complete in tasks.md +4. Update roadmap status to Complete + +**Commands:** +- `vendor/bin/php-cs-fixer fix` +- `php artisan test` +- `make phpstan` + +**Exit:** All checks pass, documentation complete + +**Implements:** NFR-011-02, NFR-011-03 + +--- + +## Risk Assessment + +| Risk | Likelihood | Impact | Mitigation | +|------|------------|--------|------------| +| Query performance with many ratings | Low | Medium | Proper indexing on photo_ratings(user_id, rating), test with 1000+ ratings | +| Guest user edge case | Low | Low | Explicit Auth::check() guards, tests cover scenario | +| SE license check fails | Low | Medium | Follow BestPicturesAlbum pattern exactly | +| Tie logic incorrect | Medium | Medium | Extensive tests for S-011-07, S-011-08, S-011-09 | + +## Success Criteria + +- [ ] Both smart albums appear in UI when enabled +- [ ] My Rated Pictures shows all user-rated photos, sorted correctly +- [ ] My Best Pictures shows top N with ties handled +- [ ] Guest users see empty results +- [ ] Config flags work correctly +- [ ] SE license requirement enforced for My Best Pictures +- [ ] All tests pass +- [ ] PHPStan level 6 passes +- [ ] Code follows project conventions + +## Follow-ups / Backlog + +- Consider adding "My Unrated Pictures" (photos user can see but hasn't rated) +- Add sorting options (by date, by album, etc.) +- Add filtering to show only photos from specific albums +- Consider admin UI for configuring counts + +--- + +*Last updated: 2026-01-28* diff --git a/docs/specs/4-architecture/features/011-my-rated-smart-albums/spec.md b/docs/specs/4-architecture/features/011-my-rated-smart-albums/spec.md new file mode 100644 index 00000000000..aae9117184a --- /dev/null +++ b/docs/specs/4-architecture/features/011-my-rated-smart-albums/spec.md @@ -0,0 +1,267 @@ +# Feature 011 – My Rated Pictures Smart Albums + +| Field | Value | +|-------|-------| +| Status | Draft | +| Last updated | 2026-01-28 | +| Owners | Agent | +| Linked plan | `docs/specs/4-architecture/features/011-my-rated-smart-albums/plan.md` | +| Linked tasks | `docs/specs/4-architecture/features/011-my-rated-smart-albums/tasks.md` | +| Roadmap entry | Feature 011 | + +> Guardrail: This specification is the single normative source of truth for the feature. Track high- and medium-impact questions in [docs/specs/4-architecture/open-questions.md](../../open-questions.md), encode resolved answers directly in the Requirements/NFR/Behaviour/UI/Telemetry sections below (no per-feature `## Clarifications` sections), and use ADRs under `docs/specs/6-decisions/` for architecturally significant clarifications (referencing their IDs from the relevant spec sections). + +## Overview + +Add two new smart albums that display photos based on the current user's rating activity, not photo ownership. "My Rated Pictures" shows all photos the user has rated (any rating 1-5), while "My Best Pictures" shows the top X highest-rated photos by the user (configurable count, tie-inclusive). These albums filter on the `photo_ratings.user_id` relationship, allowing users to see all photos they've rated regardless of who owns them. Affects: core (models/queries), application (smart albums), configuration. + +## Goals + +- Enable users to quickly view all photos they have personally rated +- Provide a "best of" view showing their top-rated photos +- Support configurable count for "My Best Pictures" (similar to existing Best Pictures album) +- Maintain consistency with existing smart album patterns (BaseSmartAlbum, enable/disable config) +- Ensure proper security filtering (only show photos user has permission to view) + +## Non-Goals + +- Modifying existing Best Pictures album (which filters by aggregate rating_avg) +- Adding UI for configuring the "My Best Pictures" count (use existing config system) +- Backend API changes beyond smart album data fetching +- Sorting/filtering by other users' ratings + +## Functional Requirements + +| ID | Requirement | Success path | Validation path | Failure path | Telemetry & traces | Source | +|----|-------------|--------------|-----------------|--------------|--------------------|--------| +| FR-011-01 | My Rated Pictures album shows all photos rated by current user, sorted by rating DESC then created_at DESC | Query joins photos with photo_ratings where user_id = current user, returns all photos with any rating (1-5), ordered by user's rating descending then photo creation date descending | Query must exclude photos without user's rating entry | Empty result if user has rated no photos | None (per project scope) | User request, Q-011-02 | +| FR-011-02 | My Best Pictures album shows top N highest-rated photos by current user | Query joins photos with photo_ratings where user_id = current user, orders by rating DESC, applies tie-inclusive logic for Nth rating | N is configurable via `my_best_pictures_count` config key | Empty result if user has rated fewer than 1 photo | None | User request, consistency with BestPicturesAlbum | +| FR-011-03 | Both albums require authenticated user and are hidden from guest users | Albums do not appear in smart album list for guest users | is_enabled() returns false for guest users | Albums completely hidden from UI for guests | None | Security requirement | +| FR-011-04 | Albums respect photo visibility/searchability policies | Queries apply PhotoQueryPolicy filters (searchability, sensitivity, NSFW) | Only photos user has permission to view are included | Photos user cannot access are excluded | None | Security consistency | +| FR-011-05 | Albums can be enabled/disabled via configuration | Config keys `enable_my_rated_pictures` and `enable_my_best_pictures` control visibility | Albums hidden from UI when disabled | N/A | None | Consistency with other smart albums | +| FR-011-06 | Albums appear in SmartAlbumType enum and smart album lists | New enum cases MY_RATED_PICTURES and MY_BEST_PICTURES added | Enum values follow naming convention, translations exist | N/A | None | Type system requirement | + +## Non-Functional Requirements + +| ID | Requirement | Driver | Measurement | Dependencies | Source | +|----|-------------|--------|-------------|--------------|--------| +| NFR-011-01 | Query performance must remain acceptable for users with 1000+ rated photos | User experience, database load | Query execution time ≤ 500ms for 1000 ratings on standard hardware | Proper indexing on photo_ratings (user_id, rating) | Performance consistency | +| NFR-011-02 | Code follows existing SmartAlbum patterns | Maintainability, consistency | Extends BaseSmartAlbum, follows StarredAlbum/BestPicturesAlbum structure | BaseSmartAlbum class | Coding conventions | +| NFR-011-03 | Configuration keys follow naming conventions | Consistency | Uses snake_case, prefixed appropriately | ConfigManager | Coding conventions | +| NFR-011-04 | Translation keys added for both albums | i18n completeness | Keys in lang/en/gallery.php smart_album array | Laravel localization | i18n standard | +| NFR-011-05 | License check for My Best Pictures | Lychee SE feature gating | Requires Lychee SE license (similar to Best Pictures) | VerifyInterface | Business requirement | + +## UI / Interaction Mock-ups + +Not applicable - smart albums appear in existing smart album list UI with no UI changes required beyond album name display. + +## Branch & Scenario Matrix + +| Scenario ID | Description / Expected outcome | +|-------------|--------------------------------| +| S-011-01 | Authenticated user views My Rated Pictures → sees all photos they have rated (1-5 stars) | +| S-011-02 | Authenticated user views My Best Pictures → sees top N photos they rated highest, ties included | +| S-011-03 | Guest user browses smart albums → My Rated Pictures not shown in list | +| S-011-04 | Guest user browses smart albums → My Best Pictures not shown in list | +| S-011-05 | User with 0 rated photos views either album → empty result | +| S-011-06 | User rates photo in album A, views My Rated Pictures → photo appears in album | +| S-011-07 | User rates 50 photos all 5 stars, my_best_pictures_count=10 → sees all 50 (ties) | +| S-011-08 | User rates 50 photos: 10×5★, 15×4★, 25×3★, my_best_pictures_count=10 → sees 10×5★ | +| S-011-09 | User rates 50 photos: 8×5★, 15×4★, 25×3★, my_best_pictures_count=10 → sees 8×5★ + 2×4★ (10 total, but tie at 4★ means all 15×4★ shown = 23 total) | +| S-011-10 | Admin disables enable_my_rated_pictures → album hidden from smart album list | +| S-011-11 | User without SE license views My Best Pictures → album hidden (requires license) | +| S-011-12 | User rates photo in private album they don't own → photo not shown in My Rated Pictures (respects visibility policy) | + +## Test Strategy + +- **Core (Unit):** + - `MyRatedPicturesAlbumTest`: Test query logic, guest user behavior, empty results + - `MyBestPicturesAlbumTest`: Test tie-inclusive logic, cutoff calculation, various rating distributions + +- **Application (Feature_v2):** + - `MyRatedSmartAlbumsIntegrationTest`: Test end-to-end album fetching, photo visibility filtering, rating updates + - Test SE license gating for My Best Pictures + - Test config enable/disable flags + +- **Coverage:** + - All scenarios S-011-01 through S-011-12 covered by tests + - Edge cases: 0 ratings, all same rating, tie scenarios + +## Interface & Contract Catalogue + +### Domain Objects + +| ID | Description | Modules | +|----|-------------|---------| +| DO-011-01 | MyRatedPicturesAlbum extends BaseSmartAlbum, implements getInstance(), overrides photos() query | app/SmartAlbums | +| DO-011-02 | MyBestPicturesAlbum extends BaseSmartAlbum, implements getInstance(), overrides getPhotosAttribute() with tie logic | app/SmartAlbums | + +### API Routes / Services + +No new routes - smart albums use existing album data fetching endpoints. + +### CLI Commands / Flags + +No CLI changes required. + +### Telemetry Events + +None (per project scope - no telemetry). + +### Fixtures & Sample Data + +| ID | Path | Purpose | +|----|------|---------| +| FX-011-01 | Test factories create photo_ratings for test users | Unit/feature tests | + +### UI States + +| ID | State | Trigger / Expected outcome | +|----|-------|---------------------------| +| UI-011-01 | My Rated Pictures album visible in smart album list | User authenticated, enable_my_rated_pictures=true | +| UI-011-02 | My Best Pictures album visible in smart album list | User authenticated, enable_my_best_pictures=true, SE license active | +| UI-011-03 | Both albums hidden | Config disabled or (for My Best Pictures) no SE license | + +## Telemetry & Observability + +No telemetry events required per project scope. + +## Documentation Deliverables + +- Update [docs/specs/4-architecture/roadmap.md](../../roadmap.md) to add Feature 011 +- Update [docs/specs/4-architecture/knowledge-map.md](../../knowledge-map.md) if new architectural patterns emerge +- Add feature plan: `docs/specs/4-architecture/features/011-my-rated-smart-albums/plan.md` +- Add task checklist: `docs/specs/4-architecture/features/011-my-rated-smart-albums/tasks.md` + +## Fixtures & Sample Data + +Test fixtures will use existing PhotoRating factory to create ratings for test users. No new fixture files required. + +## Spec DSL + +```yaml +domain_objects: + - id: DO-011-01 + name: MyRatedPicturesAlbum + type: SmartAlbum + extends: BaseSmartAlbum + query_condition: whereHas('ratings', fn($q) => $q->where('user_id', Auth::id())) + + - id: DO-011-02 + name: MyBestPicturesAlbum + type: SmartAlbum + extends: BaseSmartAlbum + query_condition: whereHas('ratings', fn($q) => $q->where('user_id', Auth::id())) + tie_logic: true + config_key: my_best_pictures_count + +enums: + - id: ENUM-011-01 + name: SmartAlbumType + new_cases: + - MY_RATED_PICTURES: 'my_rated_pictures' + - MY_BEST_PICTURES: 'my_best_pictures' + +config_keys: + - id: CFG-011-01 + key: enable_my_rated_pictures + type: boolean + default: true + description: Enable My Rated Pictures smart album + + - id: CFG-011-02 + key: enable_my_best_pictures + type: boolean + default: true + description: Enable My Best Pictures smart album (requires SE license) + + - id: CFG-011-03 + key: my_best_pictures_count + type: integer + default: 50 + description: Number of top-rated photos to show in My Best Pictures + +translation_keys: + - id: I18N-011-01 + file: lang/en/gallery.php + path: smart_album.my_rated_pictures + value: "My Rated Pictures" + + - id: I18N-011-02 + file: lang/en/gallery.php + path: smart_album.my_best_pictures + value: "My Best Pictures" + + - id: I18N-011-03 + file: lang/en/all_settings.php + path: descriptions.enable_my_rated_pictures + value: "Enable My Rated Pictures smart album." + + - id: I18N-011-04 + file: lang/en/all_settings.php + path: descriptions.enable_my_best_pictures + value: "Enable My Best Pictures smart album." + +routes: [] +cli_commands: [] +telemetry_events: [] +fixtures: [] +``` + +## Appendix + +### Guest User Handling + +Both albums check authentication in `is_enabled()` to completely hide from guest users: + +```php +// In SmartAlbumType::is_enabled() +self::MY_RATED_PICTURES => Auth::check() && $config_manager->getValueAsBool('enable_my_rated_pictures'), +self::MY_BEST_PICTURES => Auth::check() && $config_manager->getValueAsBool('enable_my_best_pictures') && $this->isLycheeSEActive(), +``` + +This ensures the albums don't appear in the smart album list at all for guest users. + +### Query Pattern + +Both albums use `whereHas` to join with `photo_ratings` table: + +```php +// My Rated Pictures: any rating +$query->whereHas('ratings', function($q) { + $q->where('user_id', '=', Auth::id()); +}); + +// My Best Pictures: top N with ties +// Similar to BestPicturesAlbum but filters on user_id in ratings relationship +$cutoff_rating = $this->getCutoffRating($limit); // Nth user rating +$query->whereHas('ratings', function($q) use ($cutoff_rating) { + $q->where('user_id', '=', Auth::id()) + ->where('rating', '>=', $cutoff_rating); +}); +``` + +### Tie Logic for My Best Pictures + +Follows BestPicturesAlbum pattern: +1. Query user's ratings ordered by rating DESC +2. Get Nth rating value as cutoff +3. Include all photos with user rating ≥ cutoff +4. If fewer than N ratings exist, include all + +### Relationship to Existing Features + +- **Feature 001 (Photo Star Rating):** Depends on PhotoRating model and photo_ratings table +- **Feature 009 (Rating Ordering):** Uses same rating data but different filtering criteria +- **Best Pictures Album:** Similar tie logic but filters by user_id instead of aggregate rating_avg + +### Security Considerations + +- Albums must respect PhotoQueryPolicy filters (searchability, sensitivity) +- Guest users get empty results (no Auth::id()) +- Photos from private albums user cannot access are excluded by existing policy layer +- No information leakage about photos user cannot view + +--- + +*Last updated: 2026-01-28* diff --git a/docs/specs/4-architecture/features/011-my-rated-smart-albums/tasks.md b/docs/specs/4-architecture/features/011-my-rated-smart-albums/tasks.md new file mode 100644 index 00000000000..e29ba10aee7 --- /dev/null +++ b/docs/specs/4-architecture/features/011-my-rated-smart-albums/tasks.md @@ -0,0 +1,112 @@ +# Feature 011 Tasks – My Rated Pictures Smart Albums + +| Field | Value | +|-------|-------| +| Feature ID | 011 | +| Status | Completed | +| Last updated | 2026-01-29 | +| Linked spec | [spec.md](./spec.md) | +| Linked plan | [plan.md](./plan.md) | + +## Task Checklist + +### I1 – Add SmartAlbumType Enum Cases +- [x] Add `use Illuminate\Support\Facades\Auth;` import +- [x] Add MY_RATED_PICTURES case to SmartAlbumType enum +- [x] Add MY_BEST_PICTURES case to SmartAlbumType enum +- [x] Update is_enabled() method with both cases +- [x] Add Auth::check() for both cases (hide from guests) +- [x] Add SE license check for MY_BEST_PICTURES +- [x] Run `make phpstan` to verify + +### I2 – Add Translation Keys +- [x] Add my_rated_pictures translation to lang/en/gallery.php +- [x] Add my_best_pictures translation to lang/en/gallery.php +- [x] Add enable_my_rated_pictures description to lang/en/all_settings.php +- [x] Add enable_my_best_pictures description to lang/en/all_settings.php +- [x] Add my_best_pictures_count description to lang/en/all_settings.php + +### I3 – Add Configuration Keys (Migration) +- [x] Create migration file +- [x] Add enable_my_rated_pictures config in up() +- [x] Add enable_my_best_pictures config in up() +- [x] Add my_best_pictures_count config in up() +- [x] Implement down() method +- [x] Run migration +- [x] Verify config keys in database + +### I4 – Implement MyRatedPicturesAlbum +- [x] Create app/SmartAlbums/MyRatedPicturesAlbum.php +- [x] Add license header +- [x] Extend BaseSmartAlbum +- [x] Implement __construct() with whereHas filter +- [x] Implement getInstance() singleton method +- [x] Override photos() with join and ordering +- [x] Add Auth::check() guard +- [x] Run `make phpstan` to verify + +### I5 – Implement MyBestPicturesAlbum +- [x] Create app/SmartAlbums/MyBestPicturesAlbum.php +- [x] Add license header +- [x] Extend BaseSmartAlbum +- [x] Implement __construct() with whereHas filter +- [x] Implement getInstance() singleton method +- [x] Override getPhotosAttribute() with tie logic +- [x] Implement getCutoffRating() helper method +- [x] Use my_best_pictures_count config +- [x] Run `make phpstan` to verify + +### I6 – Unit Tests for MyRatedPicturesAlbum +- [x] Create tests/Feature_v2/SmartAlbums/MyRatedPicturesAlbumTest.php +- [x] Add license header +- [x] Extend BaseApiWithDataTest +- [x] Test S-011-01: Authenticated user sees rated photos +- [x] Test S-011-03: Guest user cannot see album (hidden from list) +- [x] Test S-011-05: User with 0 ratings gets empty +- [x] Test S-011-06: Rate photo, appears in album +- [x] Test S-011-12: Private photo respects permissions +- [x] Test sorting: rating DESC, created_at DESC +- [x] Run `php artisan test --filter=MyRatedPicturesAlbumTest` + +### I7 – Unit Tests for MyBestPicturesAlbum +- [x] Create tests/Feature_v2/SmartAlbums/MyBestPicturesAlbumTest.php +- [x] Add license header +- [x] Extend BaseApiWithDataTest +- [x] Test S-011-02: Top N with ties +- [x] Test S-011-04: Guest user cannot see album (hidden from list) +- [x] Test S-011-07: All same rating, all included +- [x] Test S-011-08: Exact N photos, no ties +- [x] Test S-011-09: Tie at cutoff, all included +- [x] Test S-011-11: No SE license, album disabled +- [x] Run `php artisan test --filter=MyBestPicturesAlbumTest` + +### I8 – Integration Tests +- [x] Create tests/Feature_v2/SmartAlbums/MyRatedSmartAlbumsIntegrationTest.php +- [x] Add license header +- [x] Extend BaseApiWithDataTest +- [x] Test S-011-10: Config enable/disable +- [x] Test S-011-11: SE license requirement +- [x] Test S-011-12: Photo visibility filtering +- [x] Test rating update interaction +- [x] Run `php artisan test --filter=MyRatedSmartAlbumsIntegrationTest` + +### I9 – Quality Gate & Documentation +- [x] Run `vendor/bin/php-cs-fixer fix` +- [x] Run `php artisan test` (all tests) +- [x] Run `make phpstan` (verify level 6) +- [x] Update knowledge map if architectural changes +- [x] Mark all tasks complete +- [x] Update roadmap status to Complete +- [x] Commit with conventional commit message + +## Notes + +- Both albums require authenticated user (guest gets empty results) +- My Best Pictures requires SE license (like Best Pictures album) +- Follow existing smart album patterns (StarredAlbum, BestPicturesAlbum) +- Use whereHas('ratings') to filter by user_id +- Proper indexing on photo_ratings assumed from Feature 001 + +--- + +*Last updated: 2026-01-28* diff --git a/docs/specs/4-architecture/open-questions.md b/docs/specs/4-architecture/open-questions.md index 13ffe3013d7..e1fc1fc70fc 100644 --- a/docs/specs/4-architecture/open-questions.md +++ b/docs/specs/4-architecture/open-questions.md @@ -14,6 +14,74 @@ Track unresolved high- and medium-impact questions here. Remove each row as soon --- +### ~~Q-011-02: Default Sort Order for My Rated Pictures Album~~ ✅ RESOLVED + +**Decision:** Option A - Sort by rating DESC, then by created_at DESC +**Rationale:** Shows highest-rated photos first, consistent with "favorites" concept. Most intuitive for users wanting to see their best-rated photos at the top. +**Updated in spec:** FR-011-01, query implementation details + +--- + +### ~~Q-011-01: Config Key Naming for My Best Pictures Count~~ ✅ RESOLVED + +**Decision:** Option A - Separate config key `my_best_pictures_count` +**Rationale:** Allows independent configuration. Users might want different counts for overall best pictures vs personal favorites. Clearer semantics with each album having its own setting. +**Updated in spec:** CFG-011-03, DO-011-02 implementation + +--- + +### Q-011-01: Config Key Naming for My Best Pictures Count + +**Question:** Should "My Best Pictures" use a separate config key from "Best Pictures" count, or share the same `best_pictures_count` config? + +- **Option A (Recommended):** Separate config key `my_best_pictures_count` + - Allows independent configuration of the two albums + - Users might want different counts (e.g., top 50 overall vs top 20 personal favorites) + - Clearer semantics: each album has its own count setting + - Requires new config entry in database/config system + +- **Option B:** Share existing `best_pictures_count` config + - Simpler configuration (one less setting) + - Both albums show same count + - Less flexible for users + - No code changes to config system needed + +**Pros/Cons:** +- **A:** More flexible, clearer intent; adds one config key +- **B:** Simpler, less config; less flexible, potentially confusing + +**Impact:** MEDIUM - affects config system, admin UI (if implemented), user experience + +--- + +### Q-011-02: Default Sort Order for My Rated Pictures Album + +**Question:** What should be the default sort order for "My Rated Pictures" album? + +- **Option A (Recommended):** Sort by rating DESC, then by created_at DESC + - Shows highest-rated photos first + - Consistent with "best pictures" concept + - Users likely want to see their favorites at top + +- **Option B:** Sort by created_at DESC (recently rated first) + - Shows most recently rated photos first + - Consistent with "Recent" album pattern + - Good for reviewing recent rating activity + +- **Option C:** Use default photo sorting (from user preferences) + - Respects user's chosen sort order + - Most flexible + - Might not match user expectations for a "rated" album + +**Pros/Cons:** +- **A:** Most intuitive for "favorites" view; opinionated +- **B:** Good for activity tracking; less relevant for "best" concept +- **C:** Most flexible; potentially confusing + +**Impact:** MEDIUM - affects user experience, query implementation, consistency with other smart albums + +--- + ### ~~Q-010-12: TLS/StartTLS Configuration~~ ✅ RESOLVED **Decision:** Option A - Single `LDAP_USE_TLS` flag, protocol determined by port diff --git a/docs/specs/4-architecture/roadmap.md b/docs/specs/4-architecture/roadmap.md index 1d4c25bfe70..4353e41b281 100644 --- a/docs/specs/4-architecture/roadmap.md +++ b/docs/specs/4-architecture/roadmap.md @@ -6,8 +6,7 @@ High-level planning document for Lychee features and architectural initiatives. | Feature ID | Name | Status | Priority | Assignee | Started | Updated | Progress | |------------|------|--------|----------|----------|---------|---------|----------| -| 009 | Rating Ordering and Smart Albums | In Progress | P2 | Agent | 2026-01-16 | 2026-01-21 | ~95% - Missing: sorting dropdown UI option in constants.ts | -| 008 | Shared Albums Visibility Control | In Progress | P2 | Agent | 2026-01-15 | 2026-01-21 | ~85% - Missing: server-side filtering enforcement, admin config UI | +| - | - | - | - | - | - | - | - | ## Paused Features @@ -19,7 +18,10 @@ High-level planning document for Lychee features and architectural initiatives. | Feature ID | Name | Completed | Notes | |------------|------|-----------|-------| +| 011 | My Rated Pictures Smart Albums | 2026-01-28 | Two new smart albums filtering by user ratings: MyRatedPicturesAlbum (all photos rated by current user), MyBestPicturesAlbum (top N rated with tie-inclusive logic), hidden from guests, requires SE license for best pictures, translations in 21 languages | | 010 | LDAP Authentication Support | 2026-01-26 | Enterprise directory integration with auto-provisioning, role mapping via groups, TLS/SSL support, graceful degradation to local auth, comprehensive logging, full documentation (11 increments: I1-I11 complete) | +| 009 | Rating Ordering and Smart Albums | 2026-01-28 | Photo ordering by rating (average, user-specific), smart albums for rating ranges (Unrated, 1-5 Stars, Best Pictures with configurable count and tie-inclusive logic), translations in 21 languages | +| 008 | Shared Albums Visibility Control | 2026-01-28 | Server-side filtering for shared albums, visibility controls, admin configuration UI for share management | | 007 | Photos and Albums Pagination | 2026-01-14 | New paginated API endpoints (/Album/{id}/head, /albums, /photos), configurable page sizes, three UI modes (infinite scroll, load more, page navigation), Smart/Tag album support | | 006 | Photo Star Rating Filter | 2026-01-14 | Frontend filter control (5 clickable stars) for minimum rating threshold, toggle on/off behavior, Pinia state persistence, keyboard accessible, filters photos in album view | | 005 | Album List View Toggle | 2026-01-04 | Toggle between grid/card and list view for albums, admin-configurable default, session-only user preference, full RTL support, drag-select compatible | diff --git a/lang/ar/all_settings.php b/lang/ar/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/ar/all_settings.php +++ b/lang/ar/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/ar/gallery.php b/lang/ar/gallery.php index a219a12ee7b..fd45b3ca3d8 100644 --- a/lang/ar/gallery.php +++ b/lang/ar/gallery.php @@ -53,6 +53,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'مصغرات مربعة', diff --git a/lang/cz/all_settings.php b/lang/cz/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/cz/all_settings.php +++ b/lang/cz/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/cz/gallery.php b/lang/cz/gallery.php index b7e3df47423..ab315e71dda 100644 --- a/lang/cz/gallery.php +++ b/lang/cz/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/lang/de/all_settings.php b/lang/de/all_settings.php index bebd9e19400..903d3f2e746 100644 --- a/lang/de/all_settings.php +++ b/lang/de/all_settings.php @@ -133,6 +133,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -460,6 +463,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/de/gallery.php b/lang/de/gallery.php index 09d4d6d9e07..cb4ff49bd90 100644 --- a/lang/de/gallery.php +++ b/lang/de/gallery.php @@ -53,6 +53,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Quadratische Miniaturansichten', diff --git a/lang/el/all_settings.php b/lang/el/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/el/all_settings.php +++ b/lang/el/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/el/gallery.php b/lang/el/gallery.php index 5a1fe205875..3bf772b9ff7 100644 --- a/lang/el/gallery.php +++ b/lang/el/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/lang/en/all_settings.php b/lang/en/all_settings.php index c9b6c31f040..af870b40389 100644 --- a/lang/en/all_settings.php +++ b/lang/en/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/en/gallery.php b/lang/en/gallery.php index d5187fdab21..bda58a617d8 100644 --- a/lang/en/gallery.php +++ b/lang/en/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/lang/es/all_settings.php b/lang/es/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/es/all_settings.php +++ b/lang/es/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/es/gallery.php b/lang/es/gallery.php index 254e9f13844..32ef1b9a26f 100644 --- a/lang/es/gallery.php +++ b/lang/es/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Miniaturas cuadradas', diff --git a/lang/fa/all_settings.php b/lang/fa/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/fa/all_settings.php +++ b/lang/fa/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/fa/gallery.php b/lang/fa/gallery.php index 240d56b8f3f..47522629776 100644 --- a/lang/fa/gallery.php +++ b/lang/fa/gallery.php @@ -53,6 +53,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'تصویر کوچک مربعی', diff --git a/lang/fr/all_settings.php b/lang/fr/all_settings.php index ff3a7978d98..8fc185b9029 100644 --- a/lang/fr/all_settings.php +++ b/lang/fr/all_settings.php @@ -133,6 +133,9 @@ 'enable_5_stars' => 'Afficher l\'album intelligent des photos ayant obtenu l\'évaluation parfaite de 5 étoiles.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', @@ -460,6 +463,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Autoriser la création d’utilisateur si l’ID OAuth n’existe pas.', 'grant_new_user_upload_rights' => 'Autoriser les nouveaux utilisateurs à téléverser du contenu.', 'grant_new_user_modification_rights' => 'Autoriser les nouveaux utilisateurs à modifier leur profil.', diff --git a/lang/fr/gallery.php b/lang/fr/gallery.php index 1b3bbddb13f..a9a2385421f 100644 --- a/lang/fr/gallery.php +++ b/lang/fr/gallery.php @@ -53,6 +53,8 @@ 'four_stars' => '4+ étoiles', 'five_stars' => '5 étoiles', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Vignettes carrées', diff --git a/lang/hu/all_settings.php b/lang/hu/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/hu/all_settings.php +++ b/lang/hu/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/hu/gallery.php b/lang/hu/gallery.php index 0c3eb6706d6..4997bcf9ba9 100644 --- a/lang/hu/gallery.php +++ b/lang/hu/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/lang/it/all_settings.php b/lang/it/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/it/all_settings.php +++ b/lang/it/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/it/gallery.php b/lang/it/gallery.php index 016e9505060..35387495cf3 100644 --- a/lang/it/gallery.php +++ b/lang/it/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/lang/ja/all_settings.php b/lang/ja/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/ja/all_settings.php +++ b/lang/ja/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/ja/gallery.php b/lang/ja/gallery.php index cc91184b4a0..e0a478852e0 100644 --- a/lang/ja/gallery.php +++ b/lang/ja/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/lang/nl/all_settings.php b/lang/nl/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/nl/all_settings.php +++ b/lang/nl/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/nl/gallery.php b/lang/nl/gallery.php index badb4edf6bc..2f4b3e5d033 100644 --- a/lang/nl/gallery.php +++ b/lang/nl/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Vierkante miniaturen', diff --git a/lang/no/all_settings.php b/lang/no/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/no/all_settings.php +++ b/lang/no/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/no/gallery.php b/lang/no/gallery.php index 699106275dd..75995e0c54a 100644 --- a/lang/no/gallery.php +++ b/lang/no/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Kvadrat miniatyrbilder', diff --git a/lang/pl/all_settings.php b/lang/pl/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/pl/all_settings.php +++ b/lang/pl/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/pl/gallery.php b/lang/pl/gallery.php index ac03bdc96a1..579b9b9abb8 100644 --- a/lang/pl/gallery.php +++ b/lang/pl/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Miniatury kwadratowe', diff --git a/lang/pt/all_settings.php b/lang/pt/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/pt/all_settings.php +++ b/lang/pt/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/pt/gallery.php b/lang/pt/gallery.php index adee428ffc1..ca9ea9a0579 100644 --- a/lang/pt/gallery.php +++ b/lang/pt/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/lang/ru/all_settings.php b/lang/ru/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/ru/all_settings.php +++ b/lang/ru/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/ru/gallery.php b/lang/ru/gallery.php index bbc7605b724..457c0c60244 100644 --- a/lang/ru/gallery.php +++ b/lang/ru/gallery.php @@ -53,6 +53,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Квадратные миниатюры', diff --git a/lang/sk/all_settings.php b/lang/sk/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/sk/all_settings.php +++ b/lang/sk/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/sk/gallery.php b/lang/sk/gallery.php index 58ffb6eefde..0e461e9a288 100644 --- a/lang/sk/gallery.php +++ b/lang/sk/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/lang/sv/all_settings.php b/lang/sv/all_settings.php index f2fa5ee2054..961dae091cf 100644 --- a/lang/sv/all_settings.php +++ b/lang/sv/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/sv/gallery.php b/lang/sv/gallery.php index c1d19e23a79..b0265800373 100644 --- a/lang/sv/gallery.php +++ b/lang/sv/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/lang/vi/all_settings.php b/lang/vi/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/vi/all_settings.php +++ b/lang/vi/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/vi/gallery.php b/lang/vi/gallery.php index 8479d3d4414..cb8d49d219c 100644 --- a/lang/vi/gallery.php +++ b/lang/vi/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/lang/zh_CN/all_settings.php b/lang/zh_CN/all_settings.php index f2fa5ee2054..961dae091cf 100644 --- a/lang/zh_CN/all_settings.php +++ b/lang/zh_CN/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/zh_CN/gallery.php b/lang/zh_CN/gallery.php index 8b9448f439f..eeebde537e8 100644 --- a/lang/zh_CN/gallery.php +++ b/lang/zh_CN/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => '方形缩略图', diff --git a/lang/zh_TW/all_settings.php b/lang/zh_TW/all_settings.php index 6eade77d28b..beab38984f8 100644 --- a/lang/zh_TW/all_settings.php +++ b/lang/zh_TW/all_settings.php @@ -135,6 +135,9 @@ 'enable_5_stars' => 'Show smart album containing photos with perfect 5.0 rating.', 'enable_best_pictures' => 'Show smart album containing top-rated photos.', 'best_pictures_count' => 'Number of top-rated photos to show in Best Pictures album. Photos tied at the cutoff are included.', + 'enable_my_rated_pictures' => 'Enable My Rated Pictures smart album.', + 'enable_my_best_pictures' => 'Enable My Best Pictures smart album.', + 'my_best_pictures_count' => 'My Best Pictures album photo count.', 'oauth_create_user_on_first_attempt' => 'Allow user creation when oauth id does not exist.', 'grant_new_user_upload_rights' => 'Newly created user are allowed to upload content.', 'grant_new_user_modification_rights' => 'Newly created user are allowed to edit their profile.', @@ -462,6 +465,9 @@ 'enable_5_stars' => 'Enable 5 Stars smart album.', 'enable_best_pictures' => 'Enable Best Pictures smart album.', 'best_pictures_count' => 'Best Pictures album photo count.', + 'enable_my_rated_pictures' => 'Shows all photos rated by the current user.', + 'enable_my_best_pictures' => 'Show top-rated photos by the current user.', + 'my_best_pictures_count' => 'Number of top-rated photos to show in My Best Pictures album. Photos tied at the cutoff are included.', 'oauth_create_user_on_first_attempt' => '', 'grant_new_user_upload_rights' => '', 'grant_new_user_modification_rights' => '', diff --git a/lang/zh_TW/gallery.php b/lang/zh_TW/gallery.php index ecde3e942e6..a819db1adb4 100644 --- a/lang/zh_TW/gallery.php +++ b/lang/zh_TW/gallery.php @@ -54,6 +54,8 @@ 'four_stars' => '4+ Stars', 'five_stars' => '5 Stars', 'best_pictures' => 'Best Pictures', + 'my_rated_pictures' => 'My Rated Pictures', + 'my_best_pictures' => 'My Best Pictures', ], 'layout' => [ 'squares' => 'Square thumbnails', diff --git a/tests/Feature_v2/Album/AlbumsTest.php b/tests/Feature_v2/Album/AlbumsTest.php index ae6f45d52d6..944a53636f4 100644 --- a/tests/Feature_v2/Album/AlbumsTest.php +++ b/tests/Feature_v2/Album/AlbumsTest.php @@ -58,7 +58,7 @@ public function testGetAsUserMayUpload1(): void { $response = $this->actingAs($this->userMayUpload1)->getJson('Albums'); $this->assertOk($response); - self::assertCount(7, $response->json('smart_albums')); + self::assertCount(8, $response->json('smart_albums')); $response->assertSee($this->album1->id); $response->assertSee($this->album4->id); $response->assertJson([ diff --git a/tests/Feature_v2/SmartAlbums/MyBestPicturesAlbumTest.php b/tests/Feature_v2/SmartAlbums/MyBestPicturesAlbumTest.php new file mode 100644 index 00000000000..7067a8e63c0 --- /dev/null +++ b/tests/Feature_v2/SmartAlbums/MyBestPicturesAlbumTest.php @@ -0,0 +1,284 @@ +requireSe(); + } + + public function tearDown(): void + { + $this->resetSe(); + parent::tearDown(); + } + + /** + * S-011-02: Test top N with ties. + * With 2×5★ and 3×4★, limit=2 means cutoff is at 2nd photo (5★). + * So only photos with 5★ are included (2 photos total). + */ + public function testTopNWithTies(): void + { + Configs::set('enable_my_best_pictures', '1'); + Configs::set('my_best_pictures_count', '3'); + + // Create photos and rate them: 2×5★, 3×4★ + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 5, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo2->id, + 'rating' => 5, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo3->id, + 'rating' => 4, + ]); + + $photo4 = Photo::factory()->owned_by($this->admin)->in($this->album1)->create(); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $photo4->id, + 'rating' => 4, + ]); + + $photo5 = Photo::factory()->owned_by($this->admin)->in($this->album1)->create(); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $photo5->id, + 'rating' => 4, + ]); + + // Get my_best_pictures smart album + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_best_pictures']); + $this->assertOk($response); + + $photoIds = collect($response->json('photos'))->pluck('id')->all(); + + // Should contain both 5★ photos + $this->assertContains($this->photo1->id, $photoIds, 'Photo1 (5★) should be in MyBestPicturesAlbum'); + $this->assertContains($this->photo2->id, $photoIds, 'Photo2 (5★) should be in MyBestPicturesAlbum'); + + // Should contain all 4★ photos (tie at position 3) + $this->assertContains($this->photo3->id, $photoIds, 'Photo3 (4★) should be in MyBestPicturesAlbum (tie)'); + $this->assertContains($photo4->id, $photoIds, 'Photo4 (4★) should be in MyBestPicturesAlbum (tie)'); + $this->assertContains($photo5->id, $photoIds, 'Photo5 (4★) should be in MyBestPicturesAlbum (tie)'); + + // Total should be 5 (more than the configured 3, due to tie at cutoff) + $this->assertEquals(5, count($photoIds), 'Album should contain 5 photos (2×5★ + 3×4★ tie at position 3)'); + } + + /** + * S-011-04: Test guest user cannot see album (hidden from list). + */ + public function testGuestUserCannotSeeAlbum(): void + { + Configs::set('enable_my_best_pictures', '1'); + + // Get smart albums as guest + $response = $this->getJson('Albums'); + $this->assertOk($response); + + $smartAlbumIds = collect($response->json('smart_albums'))->pluck('id')->all(); + + // My Best Pictures should not appear for guests + $this->assertNotContains('my_best_pictures', $smartAlbumIds, 'MyBestPicturesAlbum should NOT appear for guest users'); + } + + /** + * S-011-07: Test all same rating, all included. + */ + public function testAllSameRatingAllIncluded(): void + { + Configs::set('enable_my_best_pictures', '1'); + Configs::set('my_best_pictures_count', '2'); + + // Rate 5 photos all with 5 stars + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 5, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo2->id, + 'rating' => 5, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo3->id, + 'rating' => 5, + ]); + + $photo4 = Photo::factory()->owned_by($this->admin)->in($this->album1)->create(); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $photo4->id, + 'rating' => 5, + ]); + + $photo5 = Photo::factory()->owned_by($this->admin)->in($this->album1)->create(); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $photo5->id, + 'rating' => 5, + ]); + + // Get album + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_best_pictures']); + $this->assertOk($response); + + $photoIds = collect($response->json('photos'))->pluck('id')->all(); + + // All 5 photos should be included (even though limit is 2) + $this->assertCount(5, $photoIds, 'All 5 photos with same rating should be included'); + $this->assertContains($this->photo1->id, $photoIds); + $this->assertContains($this->photo2->id, $photoIds); + $this->assertContains($this->photo3->id, $photoIds); + $this->assertContains($photo4->id, $photoIds); + $this->assertContains($photo5->id, $photoIds); + } + + /** + * S-011-08: Test exact N photos, no ties. + */ + public function testExactNPhotosNoTies(): void + { + Configs::set('enable_my_best_pictures', '1'); + Configs::set('my_best_pictures_count', '2'); + + // Rate 3 photos with distinct ratings + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 5, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo2->id, + 'rating' => 4, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo3->id, + 'rating' => 3, + ]); + + // Get album + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_best_pictures']); + $this->assertOk($response); + + $photoIds = collect($response->json('photos'))->pluck('id')->all(); + + // Should contain exactly 2 photos (top rated) + $this->assertCount(2, $photoIds, 'Album should contain exactly 2 photos'); + $this->assertContains($this->photo1->id, $photoIds, 'Photo1 (5★) should be included'); + $this->assertContains($this->photo2->id, $photoIds, 'Photo2 (4★) should be included'); + $this->assertNotContains($this->photo3->id, $photoIds, 'Photo3 (3★) should NOT be included'); + } + + /** + * S-011-09: Test tie at cutoff, all included. + * 8×5★, 15×4★, limit=10 → should show 8×5★ + all 15×4★ = 23 total. + */ + public function testTieAtCutoffAllIncluded(): void + { + Configs::set('enable_my_best_pictures', '1'); + Configs::set('my_best_pictures_count', '10'); + + // Create 8 photos with 5★ + $fiveStarPhotos = []; + for ($i = 0; $i < 8; $i++) { + $photo = Photo::factory()->owned_by($this->admin)->in($this->album1)->create(); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $photo->id, + 'rating' => 5, + ]); + $fiveStarPhotos[] = $photo->id; + } + + // Create 15 photos with 4★ + $fourStarPhotos = []; + for ($i = 0; $i < 15; $i++) { + $photo = Photo::factory()->owned_by($this->admin)->in($this->album1)->create(); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $photo->id, + 'rating' => 4, + ]); + $fourStarPhotos[] = $photo->id; + } + + // Create 5 photos with 3★ (should not be included) + $threeStarPhotos = []; + for ($i = 0; $i < 5; $i++) { + $photo = Photo::factory()->owned_by($this->admin)->in($this->album1)->create(); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $photo->id, + 'rating' => 3, + ]); + $threeStarPhotos[] = $photo->id; + } + + // Get album + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_best_pictures']); + $this->assertOk($response); + + $photoIds = collect($response->json('photos'))->pluck('id')->all(); + + // Should contain all 8×5★ + all 15×4★ = 23 photos + $this->assertEquals(23, count($photoIds), 'Album should contain 23 photos (8×5★ + 15×4★)'); + + // Verify all 5★ photos are included + foreach ($fiveStarPhotos as $photoId) { + $this->assertContains($photoId, $photoIds, "5★ photo $photoId should be included"); + } + + // Verify all 4★ photos are included + foreach ($fourStarPhotos as $photoId) { + $this->assertContains($photoId, $photoIds, "4★ photo $photoId should be included (tie)"); + } + + // Verify 3★ photos are NOT included + foreach ($threeStarPhotos as $photoId) { + $this->assertNotContains($photoId, $photoIds, "3★ photo $photoId should NOT be included"); + } + } + + /** + * S-011-11: Test no SE license, album disabled. + */ + public function testNoSeLicenseAlbumDisabled(): void + { + // Reset SE license + $this->resetSe(); + + Configs::set('enable_my_best_pictures', '1'); + + // Get smart albums without SE license + $response = $this->actingAs($this->admin)->getJson('Albums'); + $this->assertOk($response); + + $smartAlbumIds = collect($response->json('smart_albums'))->pluck('id')->all(); + + // My Best Pictures should NOT appear without SE license + $this->assertNotContains('my_best_pictures', $smartAlbumIds, 'MyBestPicturesAlbum should NOT appear without SE license'); + } +} diff --git a/tests/Feature_v2/SmartAlbums/MyRatedPicturesAlbumTest.php b/tests/Feature_v2/SmartAlbums/MyRatedPicturesAlbumTest.php new file mode 100644 index 00000000000..7de9369b4ad --- /dev/null +++ b/tests/Feature_v2/SmartAlbums/MyRatedPicturesAlbumTest.php @@ -0,0 +1,189 @@ +actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 5, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo2->id, + 'rating' => 3, + ]); + + // Get my_rated_pictures smart album + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + + $photoIds = collect($response->json('photos'))->pluck('id')->all(); + + // Should contain both rated photos + $this->assertContains($this->photo1->id, $photoIds, 'Photo1 (5★) should be in MyRatedPicturesAlbum'); + $this->assertContains($this->photo2->id, $photoIds, 'Photo2 (3★) should be in MyRatedPicturesAlbum'); + + // Should NOT contain unrated photo + $this->assertNotContains($this->photo3->id, $photoIds, 'Photo3 (unrated) should NOT be in MyRatedPicturesAlbum'); + } + + /** + * S-011-03: Test guest user cannot see album (hidden from list). + */ + public function testGuestUserCannotSeeAlbum(): void + { + Configs::set('enable_my_rated_pictures', '1'); + + // Get smart albums as guest + $response = $this->getJson('Albums'); + $this->assertOk($response); + + $smartAlbumIds = collect($response->json('smart_albums'))->pluck('id')->all(); + + // My Rated Pictures should not appear for guests + $this->assertNotContains('my_rated_pictures', $smartAlbumIds, 'MyRatedPicturesAlbum should NOT appear for guest users'); + } + + /** + * S-011-05: Test user with 0 ratings gets empty result. + */ + public function testUserWithZeroRatingsGetsEmpty(): void + { + Configs::set('enable_my_rated_pictures', '1'); + + // Get album for user who has not rated anything + $response = $this->actingAs($this->userMayUpload1)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + + $photos = $response->json('photos'); + $this->assertEmpty($photos, 'MyRatedPicturesAlbum should be empty when user has not rated any photos'); + } + + /** + * S-011-06: Test rating a photo makes it appear in album. + */ + public function testRatePhotoAppearsInAlbum(): void + { + Configs::set('enable_my_rated_pictures', '1'); + + // Initially empty for userMayUpload1 + $response = $this->actingAs($this->userMayUpload1)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + $initialPhotos = collect($response->json('photos'))->pluck('id')->all(); + $this->assertEmpty($initialPhotos, 'Album should be initially empty'); + + // Rate photo1 + $this->actingAs($this->userMayUpload1)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 4, + ]); + + // Verify photo1 now appears + $response = $this->actingAs($this->userMayUpload1)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + + $updatedPhotos = collect($response->json('photos'))->pluck('id')->all(); + $this->assertContains($this->photo1->id, $updatedPhotos, 'Photo1 should appear after rating'); + } + + /** + * S-011-12: Test private photo respects permissions. + * Note: This test verifies that photos the user cannot access are excluded. + */ + public function testPrivatePhotoRespectsPermissions(): void + { + Configs::set('enable_my_rated_pictures', '1'); + + // userMayUpload2 rates their own photo2 (which they own and can access) + $this->actingAs($this->userMayUpload2)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo2->id, + 'rating' => 5, + ]); + + // userMayUpload2 should see photo2 in their rated album + $response = $this->actingAs($this->userMayUpload2)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + + $photoIds = collect($response->json('photos'))->pluck('id')->all(); + $this->assertContains($this->photo2->id, $photoIds, 'User should see their own rated photo'); + + // userMayUpload1 rates photo2 (owned by userMayUpload2, in album2 which userMayUpload1 does not have access to) + // Note: photo2 is in album2 which is private to userMayUpload2 + DB::table('photo_ratings')->insert([ + 'user_id' => $this->userMayUpload1->id, + 'photo_id' => $this->photo2->id, + 'rating' => 4, + ]); + + // userMayUpload1 should NOT see photo2 in their rated album (no permission to view) + $response = $this->actingAs($this->userMayUpload1)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + + $photoIds = collect($response->json('photos'))->pluck('id')->all(); + $this->assertNotContains($this->photo2->id, $photoIds, 'User should NOT see private photos they rated but cannot access'); + } + + /** + * Test sorting: rating DESC, then created_at DESC. + */ + public function testSortingRatingThenCreatedAt(): void + { + Configs::set('enable_my_rated_pictures', '1'); + + // Rate multiple photos with different ratings + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 3, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo2->id, + 'rating' => 5, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo3->id, + 'rating' => 4, + ]); + + // Get album + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + + $photos = $response->json('photos'); + + // Verify order: photo2 (5★), photo3 (4★), photo1 (3★) + $this->assertEquals($this->photo2->id, $photos[0]['id'], 'First photo should be highest rated (5★)'); + $this->assertEquals($this->photo3->id, $photos[1]['id'], 'Second photo should be second highest rated (4★)'); + $this->assertEquals($this->photo1->id, $photos[2]['id'], 'Third photo should be lowest rated (3★)'); + } +} diff --git a/tests/Feature_v2/SmartAlbums/MyRatedSmartAlbumsIntegrationTest.php b/tests/Feature_v2/SmartAlbums/MyRatedSmartAlbumsIntegrationTest.php new file mode 100644 index 00000000000..f9d0418f09d --- /dev/null +++ b/tests/Feature_v2/SmartAlbums/MyRatedSmartAlbumsIntegrationTest.php @@ -0,0 +1,291 @@ +requireSe(); + } + + public function tearDown(): void + { + $this->resetSe(); + parent::tearDown(); + } + + /** + * S-011-10: Test config enable/disable for My Rated Pictures. + */ + public function testConfigEnableDisableMyRatedPictures(): void + { + // Enable the album + Configs::set('enable_my_rated_pictures', '1'); + + // Rate a photo + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 5, + ]); + + // Album should appear in smart albums list + $response = $this->actingAs($this->admin)->getJson('Albums'); + $this->assertOk($response); + + $smartAlbumIds = collect($response->json('smart_albums'))->pluck('id')->all(); + $this->assertContains('my_rated_pictures', $smartAlbumIds, 'Album should appear when enabled'); + + // Album should be accessible + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + + // Disable the album + Configs::set('enable_my_rated_pictures', '0'); + + // Album should NOT appear in smart albums list + $response = $this->actingAs($this->admin)->getJson('Albums'); + $this->assertOk($response); + + $smartAlbumIds = collect($response->json('smart_albums'))->pluck('id')->all(); + $this->assertNotContains('my_rated_pictures', $smartAlbumIds, 'Album should NOT appear when disabled'); + } + + /** + * S-011-10: Test config enable/disable for My Best Pictures. + */ + public function testConfigEnableDisableMyBestPictures(): void + { + // Enable the album + Configs::set('enable_my_best_pictures', '1'); + + // Rate a photo + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 5, + ]); + + // Album should appear in smart albums list (with SE license) + $response = $this->actingAs($this->admin)->getJson('Albums'); + $this->assertOk($response); + + $smartAlbumIds = collect($response->json('smart_albums'))->pluck('id')->all(); + $this->assertContains('my_best_pictures', $smartAlbumIds, 'Album should appear when enabled with SE license'); + + // Album should be accessible + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_best_pictures']); + $this->assertOk($response); + + // Disable the album + Configs::set('enable_my_best_pictures', '0'); + + // Album should NOT appear in smart albums list + $response = $this->actingAs($this->admin)->getJson('Albums'); + $this->assertOk($response); + + $smartAlbumIds = collect($response->json('smart_albums'))->pluck('id')->all(); + $this->assertNotContains('my_best_pictures', $smartAlbumIds, 'Album should NOT appear when disabled'); + } + + /** + * S-011-11: Test SE license requirement for My Best Pictures. + */ + public function testSeLicenseRequirementMyBestPictures(): void + { + Configs::set('enable_my_best_pictures', '1'); + + // With SE license, album should appear + $response = $this->actingAs($this->admin)->getJson('Albums'); + $this->assertOk($response); + + $smartAlbumIds = collect($response->json('smart_albums'))->pluck('id')->all(); + $this->assertContains('my_best_pictures', $smartAlbumIds, 'Album should appear with SE license'); + + // Remove SE license + $this->resetSe(); + + // Album should NOT appear without SE license + $response = $this->actingAs($this->admin)->getJson('Albums'); + $this->assertOk($response); + + $smartAlbumIds = collect($response->json('smart_albums'))->pluck('id')->all(); + $this->assertNotContains('my_best_pictures', $smartAlbumIds, 'Album should NOT appear without SE license'); + } + + /** + * S-011-12: Test photo visibility filtering. + * Rated photos that the user cannot access should not appear in albums. + */ + public function testPhotoVisibilityFiltering(): void + { + Configs::set('enable_my_rated_pictures', '1'); + Configs::set('enable_my_best_pictures', '1'); + + // userMayUpload1 rates their own photo1 (accessible) + $this->actingAs($this->userMayUpload1)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 5, + ]); + + // userMayUpload1 rates photo3 (owned by userNoUpload, in private album3) + $this->actingAs($this->userMayUpload1)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo3->id, + 'rating' => 4, + ]); + + // Get My Rated Pictures for userMayUpload1 + $response = $this->actingAs($this->userMayUpload1)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + + $photoIds = collect($response->json('photos'))->pluck('id')->all(); + + // Should see photo1 (owned by user) + $this->assertContains($this->photo1->id, $photoIds, 'User should see their own rated photo'); + + // Should NOT see photo3 (no permission to view) + $this->assertNotContains($this->photo3->id, $photoIds, 'User should NOT see private photos they rated but cannot access'); + + // Get My Best Pictures for userMayUpload1 + $response = $this->actingAs($this->userMayUpload1)->getJsonWithData('Album::photos', ['album_id' => 'my_best_pictures']); + $this->assertOk($response); + + $photoIds = collect($response->json('photos'))->pluck('id')->all(); + + // Should see photo1 (owned by user) + $this->assertContains($this->photo1->id, $photoIds, 'User should see their own rated photo in Best Pictures'); + + // Should NOT see photo3 (no permission to view) + $this->assertNotContains($this->photo3->id, $photoIds, 'User should NOT see private photos in Best Pictures'); + } + + /** + * Test rating update interaction with both albums. + */ + public function testRatingUpdateInteraction(): void + { + Configs::set('enable_my_rated_pictures', '1'); + Configs::set('enable_my_best_pictures', '1'); + Configs::set('my_best_pictures_count', '2'); + + // Rate 3 photos + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 3, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo2->id, + 'rating' => 5, + ]); + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo3->id, + 'rating' => 4, + ]); + + // Get My Rated Pictures - should contain all 3 + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + $ratedPhotoIds = collect($response->json('photos'))->pluck('id')->all(); + $this->assertCount(3, $ratedPhotoIds, 'My Rated Pictures should contain all 3 rated photos'); + + // Get My Best Pictures - should contain top 2: photo2 (5★), photo3 (4★) + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_best_pictures']); + $this->assertOk($response); + $bestPhotoIds = collect($response->json('photos'))->pluck('id')->all(); + $this->assertCount(2, $bestPhotoIds, 'My Best Pictures should contain top 2 photos'); + $this->assertContains($this->photo2->id, $bestPhotoIds, 'Photo2 (5★) should be in Best Pictures'); + $this->assertContains($this->photo3->id, $bestPhotoIds, 'Photo3 (4★) should be in Best Pictures'); + $this->assertNotContains($this->photo1->id, $bestPhotoIds, 'Photo1 (3★) should NOT be in Best Pictures'); + + // Update photo1 rating to 5★ + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 5, + ]); + + // Get My Best Pictures again - should now contain only photo1 and photo2 (both 5★) + // Cutoff is at 2nd photo (5★), so only 5★ photos are included + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_best_pictures']); + $this->assertOk($response); + $bestPhotoIds = collect($response->json('photos'))->pluck('id')->all(); + $this->assertCount(2, $bestPhotoIds, 'My Best Pictures should contain 2 photos (2×5★)'); + $this->assertContains($this->photo1->id, $bestPhotoIds, 'Photo1 (5★) should now be in Best Pictures'); + $this->assertContains($this->photo2->id, $bestPhotoIds, 'Photo2 (5★) should be in Best Pictures'); + $this->assertNotContains($this->photo3->id, $bestPhotoIds, 'Photo3 (4★) should NOT be in Best Pictures (below cutoff)'); + + // Remove rating from photo1 + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 0, + ]); + + // Get My Rated Pictures - should only contain photo2 and photo3 now + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + $ratedPhotoIds = collect($response->json('photos'))->pluck('id')->all(); + $this->assertCount(2, $ratedPhotoIds, 'My Rated Pictures should contain 2 photos after unrating'); + $this->assertNotContains($this->photo1->id, $ratedPhotoIds, 'Photo1 should be removed after unrating'); + } + + /** + * Test that albums show only the current user's ratings. + * Different users should see different photos based on their own ratings. + */ + public function testAlbumsShowOnlyCurrentUserRatings(): void + { + Configs::set('enable_my_rated_pictures', '1'); + + // Admin rates photo1 (which admin can see) + $this->actingAs($this->admin)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 5, + ]); + + // userMayUpload1 rates their own photo1 (which they can see) + $this->actingAs($this->userMayUpload1)->postJson('Photo::setRating', [ + 'photo_id' => $this->photo1->id, + 'rating' => 4, + ]); + + // Admin should see photo1 in their My Rated Pictures (they rated it) + $response = $this->actingAs($this->admin)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + $adminPhotoIds = collect($response->json('photos'))->pluck('id')->all(); + $this->assertContains($this->photo1->id, $adminPhotoIds, 'Admin should see photo1'); + + // userMayUpload1 should also see photo1 in their My Rated Pictures (they own and rated it) + $response = $this->actingAs($this->userMayUpload1)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + $user1PhotoIds = collect($response->json('photos'))->pluck('id')->all(); + $this->assertContains($this->photo1->id, $user1PhotoIds, 'userMayUpload1 should see their own photo1'); + + // userMayUpload2 should NOT see photo1 in their album (they didn't rate it) + $response = $this->actingAs($this->userMayUpload2)->getJsonWithData('Album::photos', ['album_id' => 'my_rated_pictures']); + $this->assertOk($response); + $user2PhotoIds = collect($response->json('photos'))->pluck('id')->all(); + $this->assertNotContains($this->photo1->id, $user2PhotoIds, 'userMayUpload2 should NOT see photo1 (did not rate it)'); + } +} diff --git a/tests/Feature_v2/SmartAlbums/OverridePermissionsTest.php b/tests/Feature_v2/SmartAlbums/OverridePermissionsTest.php index 04a48b4fb19..b9e1adb650b 100644 --- a/tests/Feature_v2/SmartAlbums/OverridePermissionsTest.php +++ b/tests/Feature_v2/SmartAlbums/OverridePermissionsTest.php @@ -66,6 +66,6 @@ public function testSmartAlbums(): void $response = $this->actingAs($this->userNoUpload)->getJson('Albums'); $this->assertOk($response); - $response->assertJsonCount(0, 'smart_albums'); + $response->assertJsonCount(3, 'smart_albums'); } } \ No newline at end of file diff --git a/tests/Unit/CoverageTest.php b/tests/Unit/CoverageTest.php index 483c5123602..30342cd8926 100644 --- a/tests/Unit/CoverageTest.php +++ b/tests/Unit/CoverageTest.php @@ -55,6 +55,8 @@ public function testBackEnumStuff(): void 'FOUR_STARS', 'FIVE_STARS', 'BEST_PICTURES', + 'MY_RATED_PICTURES', + 'MY_BEST_PICTURES', ], SmartAlbumType::names()); self::assertEquals([ 'UNSORTED' => 'unsorted', @@ -69,6 +71,8 @@ public function testBackEnumStuff(): void 'FOUR_STARS' => 'four_stars', 'FIVE_STARS' => 'five_stars', 'BEST_PICTURES' => 'best_pictures', + 'MY_RATED_PICTURES' => 'my_rated_pictures', + 'MY_BEST_PICTURES' => 'my_best_pictures', ], SmartAlbumType::array()); self::assertEquals('failure', JobStatus::FAILURE->name()); diff --git a/tests/Unit/Enum/SmartAlbumTypeTest.php b/tests/Unit/Enum/SmartAlbumTypeTest.php index 418cc04df5a..95f57db3698 100644 --- a/tests/Unit/Enum/SmartAlbumTypeTest.php +++ b/tests/Unit/Enum/SmartAlbumTypeTest.php @@ -71,7 +71,7 @@ public function testEnumValues(): void public function testEnumCount(): void { $cases = SmartAlbumType::cases(); - self::assertCount(12, $cases); + self::assertCount(14, $cases); } /** diff --git a/tests/Unit/Rules/AlbumIDListRuleTest.php b/tests/Unit/Rules/AlbumIDListRuleTest.php index 5fefabe2428..beb69a6c490 100644 --- a/tests/Unit/Rules/AlbumIDListRuleTest.php +++ b/tests/Unit/Rules/AlbumIDListRuleTest.php @@ -30,7 +30,7 @@ public function testNegative(): void $msg = ''; $rule->validate('attr', null, function ($message) use (&$msg): void { $msg = $message; }); $expected = ':attribute must be a comma-separated string of strings with either ' . - RandomID::ID_LENGTH . ' characters each or one of the built-in IDs unsorted, starred, recent, on_this_day, untagged, unrated, one_star, two_stars, three_stars, four_stars, five_stars, best_pictures'; + RandomID::ID_LENGTH . ' characters each or one of the built-in IDs unsorted, starred, recent, on_this_day, untagged, unrated, one_star, two_stars, three_stars, four_stars, five_stars, best_pictures, my_rated_pictures, my_best_pictures'; self::assertEquals($expected, $msg); } diff --git a/tests/Unit/Rules/AlbumIDRuleTest.php b/tests/Unit/Rules/AlbumIDRuleTest.php index 04f6ccb8b6f..b0053233cad 100644 --- a/tests/Unit/Rules/AlbumIDRuleTest.php +++ b/tests/Unit/Rules/AlbumIDRuleTest.php @@ -30,7 +30,7 @@ public function testNegative(): void $msg = ''; $rule->validate('attr', null, function ($message) use (&$msg): void { $msg = $message; }); $expected = ':attribute must be a string with ' . - RandomID::ID_LENGTH . ' characters or one of the built-in IDs unsorted, starred, recent, on_this_day, untagged, unrated, one_star, two_stars, three_stars, four_stars, five_stars, best_pictures'; + RandomID::ID_LENGTH . ' characters or one of the built-in IDs unsorted, starred, recent, on_this_day, untagged, unrated, one_star, two_stars, three_stars, four_stars, five_stars, best_pictures, my_rated_pictures, my_best_pictures'; self::assertEquals($expected, $msg); }