Skip to content

Mass rename on album view#4121

Merged
ildyria merged 5 commits intomasterfrom
mass-rename-after-upload
Feb 26, 2026
Merged

Mass rename on album view#4121
ildyria merged 5 commits intomasterfrom
mass-rename-after-upload

Conversation

@ildyria
Copy link
Member

@ildyria ildyria commented Feb 26, 2026

Fixes #4051

Summary by CodeRabbit

  • New Features

    • Preview renamer endpoint to see which photo/album titles would change; per-run rule filtering supported.
    • Two-step Apply Renamer dialog (rule selection → preview → apply) and Watermark confirmation dialog.
    • Context-menu and album hero actions to open renamer/watermark flows; frontend preview/rename service methods.
  • Documentation

    • Feature plan, spec, tasks, roadmap and open-questions updated for Apply Renamer & Watermark.
  • Tests

    • New test suite covering preview API auth, validation, scopes and rule filtering.
  • Localization

    • Dialog and gallery translations added across many locales.

@ildyria ildyria requested a review from a team as a code owner February 26, 2026 13:47
@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

📝 Walkthrough

Walkthrough

Adds a renamer preview API and wiring plus optional per-run rule filtering; extends renamer constructors to accept rule IDs; adds frontend dialogs, context-menu integrations, watermark confirmation, translations, docs, and tests for preview and apply flows.

Changes

Cohort / File(s) Summary
Controller & Requests
app/Http/Controllers/RenamerController.php, app/Http/Requests/Renamer/PreviewRenameRequest.php, app/Http/Requests/Renamer/RenameRequest.php
Added preview() endpoint and PreviewRenameRequest; RenameRequest now carries rule_ids; controller routes rule_ids to preview/rename actions.
Renamer Engine
app/Metadata/Renamer/Renamer.php, app/Metadata/Renamer/PhotoRenamer.php, app/Metadata/Renamer/AlbumRenamer.php
Constructors accept optional rule_ids; base Renamer filters its rules collection when rule_ids provided.
Preview Actions & Resource
app/Actions/Renamer/PreviewPhotos.php, app/Actions/Renamer/PreviewAlbums.php, app/Http/Resources/Models/RenamerPreviewResource.php
New preview action classes computing diffs in chunks and returning RenamerPreviewResource items (id, original, new).
Rename Actions
app/Actions/Renamer/RenamePhotos.php, app/Actions/Renamer/RenameAlbums.php
New batch rename action classes that apply renames in chunked batch updates and accept optional rule_ids.
API Route & Tests
routes/api_v2.php, tests/Feature_v2/RenamerRules/RenamerPreviewTest.php
Registered POST /Renamer::preview; added extensive feature tests covering auth, authorization, validation, scopes, explicit IDs, and rule filtering.
Frontend Components & Services
resources/js/components/forms/album/ApplyRenamerDialog.vue, resources/js/components/forms/album/WatermarkConfirmDialog.vue, resources/js/services/renamer-service.ts
Added two-step ApplyRenamer dialog and WatermarkConfirmDialog; RenamerService gains preview and rename methods and related types.
UI Integration & Modals
resources/js/components/gallery/albumModule/AlbumHero.vue, resources/js/components/gallery/albumModule/AlbumPanel.vue, resources/js/composables/modalsTriggers/galleryModals.ts, resources/js/stores/ModalsState.ts, resources/js/utils/Helpers.ts
Album hero exposes renamer button and emits new modal toggles; gallery modals state adds visibility flags and toggles; empty callback stubs added.
Context Menus & Panels Wiring
resources/js/composables/contextMenus/contextMenu.ts, resources/js/components/gallery/tagModule/TagPanel.vue, resources/js/views/gallery-panels/*.vue
Added toggleApplyRenamer to callback interfaces, context-menu entries (guarded by feature flag and edit rights), and propagated modal toggles across panels.
Translations & Documentation
lang/*/dialogs.php, lang/*/gallery.php (22 locales), docs/specs/.../plan.md, spec.md, tasks.md, open-questions.md, roadmap.md
Added apply_renamer and watermark_confirm translation blocks across locales; added feature plan, spec, tasks, resolved questions, and roadmap entry for Feature 017.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Poem

🐇 I hopped through code with tiny paws,

Previewed titles, followed all the laws.
Rules picked, changes shown in view,
Then stamped a watermark—soft and true.
A rabbit cheers: backend and UI too!

🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.

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


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

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/specs/4-architecture/open-questions.md (1)

1766-1766: ⚠️ Potential issue | 🟡 Minor

Update the "Last updated" date.

The footer shows *Last updated: 2026-01-02* but this file was modified today with Q-017-01 and Q-017-02 entries. As per coding guidelines, the date should reflect the actual update date.

📝 Proposed fix
-*Last updated: 2026-01-02*
+*Last updated: 2026-02-26*
resources/js/composables/contextMenus/contextMenu.ts (1)

348-375: ⚠️ Potential issue | 🟡 Minor

Missing "Apply Renamer Rules" option for multi-album selection.

The albumsMenu() function doesn't include the toggleApplyRenamer action. Per FR-017-08 in the spec, when one or more albums are selected, the "Apply Renamer Rules" option should appear. Single album selection (line 306-311) has it, but multi-album is missing.

🔧 Proposed fix to add apply_renamer_all to albumsMenu
 function albumsMenu() {
+	const leftMenuStore = useLeftMenuStateStore();
 	return [
 		{
 			label: "gallery.menus.merge_all",
 			icon: "pi pi-arrow-down-left-and-arrow-up-right-to-center",
 			callback: albumCallbacks.toggleMerge,
 			access: selectors.selectedAlbums?.value.reduce(canMove, true),
 		},
+		{
+			label: "gallery.menus.apply_renamer_all",
+			icon: "pi pi-pencil",
+			callback: albumCallbacks.toggleApplyRenamer,
+			access: selectors.selectedAlbums?.value.reduce(
+				(acc, album) => acc && album.rights.can_edit,
+				true
+			) && (leftMenuStore.initData?.modules.is_mod_renamer_enabled ?? false),
+		},
 		{
 			label: "gallery.menus.move_all",
🧹 Nitpick comments (7)
lang/ja/gallery.php (1)

121-122: New translation values are in English, not Japanese.

The newly added keys (apply_renamer, watermark, apply_renamer_all) have English values in the Japanese locale file. While the file does have a mix of translated and untranslated strings, consider providing Japanese translations for consistency with other translated entries in this file.

Suggested translations (please verify with a native speaker):

  • 'apply_renamer' => 'リネーマールールを適用', (Apply Renamer Rules)
  • 'watermark' => 'ウォーターマーク', (Watermark)
  • 'apply_renamer' => 'リネーマーを適用', (Apply Renamer - in menus)
  • 'apply_renamer_all' => '選択項目にリネーマーを適用', (Apply Renamer to Selected)

Also applies to: 260-261

lang/zh_CN/gallery.php (2)

121-122: Missing Chinese translations for new keys.

These entries are in English but should be translated to Chinese for the zh_CN locale. Suggested translations:

-            'apply_renamer' => 'Apply Renamer Rules',
-            'watermark' => 'Watermark',
+            'apply_renamer' => '应用重命名规则',
+            'watermark' => '水印',

260-261: Missing Chinese translations for menu entries.

These menu items should also be translated for consistency with the surrounding Chinese entries:

-        'apply_renamer' => 'Apply Renamer',
-        'apply_renamer_all' => 'Apply Renamer to Selected',
+        'apply_renamer' => '应用重命名',
+        'apply_renamer_all' => '应用重命名到所选',
lang/zh_TW/gallery.php (2)

121-122: Missing Traditional Chinese translations.

These new entries contain English text instead of Traditional Chinese translations. Other strings in this locale file are properly translated (e.g., '釘選', '列表檢視', '公開但隱藏'). Consider translating these for consistency:

  • 'apply_renamer' => '套用重新命名規則',
  • 'watermark' => '浮水印',

260-261: Missing Traditional Chinese translations for menu items.

Same issue as above — these menu entries use English text. Consider:

  • 'apply_renamer' => '套用重新命名',
  • 'apply_renamer_all' => '套用重新命名至所選項目',
lang/hu/dialogs.php (1)

264-288: Localize the new Hungarian dialog strings (currently English).

Lines 265-287 introduce new hu locale entries, but the values are English. This works functionally, but it regresses locale consistency in Hungarian UI surfaces. Please localize these new keys (or explicitly document English fallback policy for this locale).

tests/Feature_v2/RenamerRules/RenamerPreviewTest.php (1)

123-147: Consider cleanup of dynamically created rules.

The testPreviewNoChanges and testPreviewRuleIdsFiltering tests create additional RenamerRule records within the test method. While the DatabaseTransactions trait should roll back these changes, it's worth verifying that the RequiresEmptyRenamerRules trait properly handles cleanup.

If cleanup becomes an issue, consider storing the rule ID and explicitly deleting it in the test, or relying on the trait's teardown. Current implementation should work given the transaction rollback.


ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75bd868 and e821200.

📒 Files selected for processing (70)
  • app/Http/Controllers/RenamerController.php
  • app/Http/Requests/Renamer/PreviewRenameRequest.php
  • app/Http/Requests/Renamer/RenameRequest.php
  • app/Metadata/Renamer/AlbumRenamer.php
  • app/Metadata/Renamer/PhotoRenamer.php
  • app/Metadata/Renamer/Renamer.php
  • docs/specs/4-architecture/features/017-apply-renamer-rules/plan.md
  • docs/specs/4-architecture/features/017-apply-renamer-rules/spec.md
  • docs/specs/4-architecture/features/017-apply-renamer-rules/tasks.md
  • docs/specs/4-architecture/open-questions.md
  • docs/specs/4-architecture/roadmap.md
  • lang/ar/dialogs.php
  • lang/ar/gallery.php
  • lang/bg/dialogs.php
  • lang/bg/gallery.php
  • lang/cz/dialogs.php
  • lang/cz/gallery.php
  • lang/de/dialogs.php
  • lang/de/gallery.php
  • lang/el/dialogs.php
  • lang/el/gallery.php
  • lang/en/dialogs.php
  • lang/en/gallery.php
  • lang/es/dialogs.php
  • lang/es/gallery.php
  • lang/fa/dialogs.php
  • lang/fa/gallery.php
  • lang/fr/dialogs.php
  • lang/fr/gallery.php
  • lang/hu/dialogs.php
  • lang/hu/gallery.php
  • lang/it/dialogs.php
  • lang/it/gallery.php
  • lang/ja/dialogs.php
  • lang/ja/gallery.php
  • lang/nl/dialogs.php
  • lang/nl/gallery.php
  • lang/no/dialogs.php
  • lang/no/gallery.php
  • lang/pl/dialogs.php
  • lang/pl/gallery.php
  • lang/pt/dialogs.php
  • lang/pt/gallery.php
  • lang/ru/dialogs.php
  • lang/ru/gallery.php
  • lang/sk/dialogs.php
  • lang/sk/gallery.php
  • lang/sv/dialogs.php
  • lang/sv/gallery.php
  • lang/vi/dialogs.php
  • lang/vi/gallery.php
  • lang/zh_CN/dialogs.php
  • lang/zh_CN/gallery.php
  • lang/zh_TW/dialogs.php
  • lang/zh_TW/gallery.php
  • resources/js/components/forms/album/ApplyRenamerDialog.vue
  • resources/js/components/forms/album/WatermarkConfirmDialog.vue
  • resources/js/components/gallery/albumModule/AlbumHero.vue
  • resources/js/components/gallery/albumModule/AlbumPanel.vue
  • resources/js/components/gallery/tagModule/TagPanel.vue
  • resources/js/composables/contextMenus/contextMenu.ts
  • resources/js/composables/modalsTriggers/galleryModals.ts
  • resources/js/services/renamer-service.ts
  • resources/js/stores/ModalsState.ts
  • resources/js/utils/Helpers.ts
  • resources/js/views/gallery-panels/Albums.vue
  • resources/js/views/gallery-panels/Search.vue
  • resources/js/views/gallery-panels/Timeline.vue
  • routes/api_v2.php
  • tests/Feature_v2/RenamerRules/RenamerPreviewTest.php

@codecov
Copy link

codecov bot commented Feb 26, 2026

Codecov Report

❌ Patch coverage is 94.81481% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.22%. Comparing base (75bd868) to head (f17f9ba).
⚠️ Report is 1 commits behind head on master.

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
resources/js/components/forms/album/ApplyRenamerDialog.vue (1)

163-213: ⚠️ Potential issue | 🟡 Minor

Handle API failures and always clear loading state.

loadRules(), loadPreview(), and applyRules() still have no failure path; this can leave loading UI stuck and gives no error feedback.

🛠️ Suggested fix pattern
 function loadRules() {
 	is_loading_rules.value = true;
 	RenamerService.list().then((response) => {
 		rules.value = response.data;
 		selected_rule_ids.value = rules.value.map((r) => r.id);
-		is_loading_rules.value = false;
+	}).catch(() => {
+		toast.add({ severity: "error", summary: trans("toasts.error"), life: 5000 });
+	}).finally(() => {
+		is_loading_rules.value = false;
 	});
 }

 function loadPreview() {
 	is_loading_preview.value = true;
 	step.value = "preview";
 	RenamerService.preview({ ... }).then((response) => {
 		preview_items.value = response.data;
-		is_loading_preview.value = false;
+	}).catch(() => {
+		toast.add({ severity: "error", summary: trans("toasts.error"), life: 5000 });
+	}).finally(() => {
+		is_loading_preview.value = false;
 	});
 }

 function applyRules() {
 	...
 	RenamerService.rename(data).then(() => {
 		...
+	}).catch(() => {
+		toast.add({ severity: "error", summary: trans("toasts.error"), life: 5000 });
 	});
 }
🧹 Nitpick comments (1)
app/Actions/Renamer/PreviewPhotos.php (1)

49-55: Avoid materializing all descendant album IDs in memory.

For large trees, pluck('id') here can become expensive. Prefer constraining the whereHas('albums', ...) subquery directly by _lft/_rgt.

♻️ Suggested refactor
-				$descendant_ids = Album::query()
-					->where('_lft', '>=', $parent->_lft)
-					->where('_rgt', '<=', $parent->_rgt)
-					->pluck('id');
-				$query->whereHas('albums', function ($q) use ($descendant_ids): void {
-					$q->whereIn('albums.id', $descendant_ids);
+				$query->whereHas('albums', function ($q) use ($parent): void {
+					$q->where('albums._lft', '>=', $parent->_lft)
+						->where('albums._rgt', '<=', $parent->_rgt);
 				});

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e821200 and 904b020.

📒 Files selected for processing (7)
  • app/Actions/Renamer/PreviewAlbums.php
  • app/Actions/Renamer/PreviewPhotos.php
  • app/Http/Controllers/RenamerController.php
  • app/Http/Resources/Models/RenamerPreviewResource.php
  • resources/js/components/forms/album/ApplyRenamerDialog.vue
  • resources/js/services/renamer-service.ts
  • tests/Feature_v2/RenamerRules/RenamerPreviewTest.php

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/Actions/Renamer/RenameAlbums.php (1)

45-45: Fix the inline comment wording for albums.

Line 45 mentions “photo titles”, but this block updates album titles.

✏️ Suggested edit
-				// Make a batch update to update all photo titles at once
+				// Make a batch update to update all album titles at once

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 904b020 and f17f9ba.

📒 Files selected for processing (3)
  • app/Actions/Renamer/RenameAlbums.php
  • app/Actions/Renamer/RenamePhotos.php
  • app/Http/Controllers/RenamerController.php

@ildyria ildyria merged commit 71053b2 into master Feb 26, 2026
43 checks passed
@ildyria ildyria deleted the mass-rename-after-upload branch February 26, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Apply renaming on a selected set of pictures

1 participant