diff --git a/app/Actions/Photo/Pipes/Shared/HydrateMetadata.php b/app/Actions/Photo/Pipes/Shared/HydrateMetadata.php
index e469147a991..1baeac006e4 100644
--- a/app/Actions/Photo/Pipes/Shared/HydrateMetadata.php
+++ b/app/Actions/Photo/Pipes/Shared/HydrateMetadata.php
@@ -66,6 +66,7 @@ public function handle(DuplicateDTO|StandaloneDTO $state, \Closure $next): Dupli
}
if ($state->photo->taken_at === null) {
$state->photo->taken_at = $state->exifInfo->taken_at;
+ $state->photo->initial_taken_at = $state->exifInfo->taken_at;
}
if ($state->photo->latitude === null) {
$state->photo->latitude = $state->exifInfo->latitude;
diff --git a/app/Contracts/Http/Requests/HasTakenAt.php b/app/Contracts/Http/Requests/HasTakenAt.php
new file mode 100644
index 00000000000..e5751dc9260
--- /dev/null
+++ b/app/Contracts/Http/Requests/HasTakenAt.php
@@ -0,0 +1,19 @@
+created_at = $request->uploadDate();
$photo->tags = $request->tags();
$photo->license = $request->license()->value;
+
+ // if the request takenAt is null, then we set the initial value back.
+ $photo->taken_at = $request->takenAt() ?? $photo->initial_taken_at;
+
$photo->save();
return PhotoResource::fromModel($photo);
diff --git a/app/Http/Requests/Photo/EditPhotoRequest.php b/app/Http/Requests/Photo/EditPhotoRequest.php
index 3a95390aa05..5748243bcbf 100644
--- a/app/Http/Requests/Photo/EditPhotoRequest.php
+++ b/app/Http/Requests/Photo/EditPhotoRequest.php
@@ -12,6 +12,7 @@
use App\Contracts\Http\Requests\HasLicense;
use App\Contracts\Http\Requests\HasPhoto;
use App\Contracts\Http\Requests\HasTags;
+use App\Contracts\Http\Requests\HasTakenAt;
use App\Contracts\Http\Requests\HasTitle;
use App\Contracts\Http\Requests\HasUploadDate;
use App\Contracts\Http\Requests\RequestAttribute;
@@ -21,6 +22,7 @@
use App\Http\Requests\Traits\HasLicenseTrait;
use App\Http\Requests\Traits\HasPhotoTrait;
use App\Http\Requests\Traits\HasTagsTrait;
+use App\Http\Requests\Traits\HasTakenAtDateTrait;
use App\Http\Requests\Traits\HasTitleTrait;
use App\Http\Requests\Traits\HasUploadDateTrait;
use App\Models\Photo;
@@ -32,7 +34,7 @@
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rules\Enum;
-class EditPhotoRequest extends BaseApiRequest implements HasPhoto, HasTags, HasUploadDate, HasDescription, HasLicense, HasTitle
+class EditPhotoRequest extends BaseApiRequest implements HasPhoto, HasTags, HasUploadDate, HasDescription, HasLicense, HasTitle, HasTakenAt
{
use HasPhotoTrait;
use HasTitleTrait;
@@ -40,6 +42,7 @@ class EditPhotoRequest extends BaseApiRequest implements HasPhoto, HasTags, HasU
use HasTagsTrait;
use HasUploadDateTrait;
use HasLicenseTrait;
+ use HasTakenAtDateTrait;
/**
* {@inheritDoc}
@@ -62,6 +65,7 @@ public function rules(): array
RequestAttribute::TAGS_ATTRIBUTE . '.*' => ['required', 'string', 'min:1'],
RequestAttribute::LICENSE_ATTRIBUTE => ['required', new Enum(LicenseType::class)],
RequestAttribute::UPLOAD_DATE_ATTRIBUTE => ['required', 'date'],
+ RequestAttribute::TAKEN_DATE_ATTRIBUTE => ['nullable', 'date'],
];
}
@@ -82,5 +86,10 @@ protected function processValidatedValues(array $values, array $files): void
$this->tags = $values[RequestAttribute::TAGS_ATTRIBUTE];
$this->upload_date = Carbon::parse($values[RequestAttribute::UPLOAD_DATE_ATTRIBUTE]);
$this->license = LicenseType::tryFrom($values[RequestAttribute::LICENSE_ATTRIBUTE]);
+
+ // We only set this one if it is not null
+ if (isset($values[RequestAttribute::TAKEN_DATE_ATTRIBUTE])) {
+ $this->taken_at = Carbon::parse($values[RequestAttribute::TAKEN_DATE_ATTRIBUTE]);
+ }
}
}
diff --git a/app/Http/Requests/Traits/HasTakenAtDateTrait.php b/app/Http/Requests/Traits/HasTakenAtDateTrait.php
new file mode 100644
index 00000000000..d4da4524db3
--- /dev/null
+++ b/app/Http/Requests/Traits/HasTakenAtDateTrait.php
@@ -0,0 +1,24 @@
+taken_at;
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Resources/Models/Utils/PreComputedPhotoData.php b/app/Http/Resources/Models/Utils/PreComputedPhotoData.php
index 78a8086048f..14fdfddb3c4 100644
--- a/app/Http/Resources/Models/Utils/PreComputedPhotoData.php
+++ b/app/Http/Resources/Models/Utils/PreComputedPhotoData.php
@@ -21,6 +21,7 @@ class PreComputedPhotoData extends Data
public bool $is_camera_date;
public bool $has_exif;
public bool $has_location;
+ public bool $is_taken_at_modified;
public function __construct(Photo $photo)
{
@@ -30,6 +31,10 @@ public function __construct(Photo $photo)
$this->is_camera_date = $photo->taken_at !== null;
$this->has_exif = $this->genExifHash($photo) !== '';
$this->has_location = $this->has_location($photo);
+ // if taken_at is null, it is for sure not modified.
+ // if taken_at is not null, then it is modified if initial_taken_at is null or if taken_at is different from initial_taken_at.
+ // dd($photo->taken_at, $photo->initial_taken_at, $photo->taken_at->notEqualTo($photo->initial_taken_at));
+ $this->is_taken_at_modified = $photo->taken_at !== null && ($photo->initial_taken_at === null || $photo->taken_at->notEqualTo($photo->initial_taken_at));
}
private function has_location(Photo $photo): bool
diff --git a/app/Legacy/Actions/Photo/Strategies/AbstractAddStrategy.php b/app/Legacy/Actions/Photo/Strategies/AbstractAddStrategy.php
index eff088adb83..ee16034c567 100644
--- a/app/Legacy/Actions/Photo/Strategies/AbstractAddStrategy.php
+++ b/app/Legacy/Actions/Photo/Strategies/AbstractAddStrategy.php
@@ -82,6 +82,7 @@ protected function hydrateMetadata(): void
}
if ($this->photo->taken_at === null) {
$this->photo->taken_at = $this->parameters->exifInfo->taken_at;
+ $this->photo->initial_taken_at = $this->parameters->exifInfo->taken_at;
}
if ($this->photo->latitude === null) {
$this->photo->latitude = $this->parameters->exifInfo->latitude;
diff --git a/app/Models/Photo.php b/app/Models/Photo.php
index 2e4bb9aeec9..100419d6f1d 100644
--- a/app/Models/Photo.php
+++ b/app/Models/Photo.php
@@ -61,6 +61,8 @@
* @property string|null $location
* @property Carbon|null $taken_at
* @property string|null $taken_at_orig_tz
+ * @property Carbon|null $initial_taken_at
+ * @property string|null $initial_taken_at_orig_tz
* @property bool $is_starred
* @property string|null $live_photo_short_path
* @property string|null $live_photo_url
@@ -153,7 +155,9 @@ class Photo extends Model
'created_at' => 'datetime',
'updated_at' => 'datetime',
'taken_at' => DateTimeWithTimezoneCast::class,
+ 'initial_taken_at' => DateTimeWithTimezoneCast::class,
'live_photo_url' => MustNotSetCast::class . ':live_photo_short_path',
+ 'taken_at_mod' => 'datetime',
'owner_id' => 'integer',
'is_starred' => 'boolean',
'tags' => ArrayCast::class,
diff --git a/database/factories/PhotoFactory.php b/database/factories/PhotoFactory.php
index 71be09427ef..689efef5758 100644
--- a/database/factories/PhotoFactory.php
+++ b/database/factories/PhotoFactory.php
@@ -37,6 +37,8 @@ class PhotoFactory extends Factory
*/
public function definition(): array
{
+ $now = now();
+
return [
'title' => 'CR_' . fake()->numerify('####'),
'description' => null,
@@ -50,8 +52,10 @@ public function definition(): array
'lens' => 'EF200mm f/2L IS',
'shutter' => '1/320 s',
'focal' => '200mm',
- 'taken_at' => now(),
+ 'initial_taken_at' => $now,
'taken_at_orig_tz' => null,
+ 'taken_at' => $now,
+ 'initial_taken_at_orig_tz' => null,
'is_starred' => false,
'album_id' => null,
'checksum' => sha1(rand()),
diff --git a/database/migrations/2025_01_24_200235_add_initial_taken_at.php b/database/migrations/2025_01_24_200235_add_initial_taken_at.php
new file mode 100644
index 00000000000..0e13d288dfd
--- /dev/null
+++ b/database/migrations/2025_01_24_200235_add_initial_taken_at.php
@@ -0,0 +1,43 @@
+dateTime('initial_taken_at', 0)->nullable(true)->default(null)->after('taken_at_orig_tz')->comment('backup of the original taken_at value');
+ $table->string('initial_taken_at_orig_tz', 31)->nullable(true)->default(null)->after('initial_taken_at')->comment('backup of the timezone at which the photo has originally been taken');
+ });
+
+ // Set initial values
+ DB::table('photos')->update([
+ 'initial_taken_at' => DB::raw('taken_at'),
+ 'initial_taken_at_orig_tz' => DB::raw('taken_at_orig_tz'),
+ ]);
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('photos', function (Blueprint $table) {
+ $table->dropColumn('initial_taken_at_orig_tz');
+ });
+ Schema::table('photos', function (Blueprint $table) {
+ $table->dropColumn('initial_taken_at');
+ });
+ }
+};
diff --git a/lang/cz/gallery.php b/lang/cz/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/cz/gallery.php
+++ b/lang/cz/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/de/gallery.php b/lang/de/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/de/gallery.php
+++ b/lang/de/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/el/gallery.php b/lang/el/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/el/gallery.php
+++ b/lang/el/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/en/gallery.php b/lang/en/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/en/gallery.php
+++ b/lang/en/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/es/gallery.php b/lang/es/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/es/gallery.php
+++ b/lang/es/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/fr/gallery.php b/lang/fr/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/fr/gallery.php
+++ b/lang/fr/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/hu/gallery.php b/lang/hu/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/hu/gallery.php
+++ b/lang/hu/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/it/gallery.php b/lang/it/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/it/gallery.php
+++ b/lang/it/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/ja/gallery.php b/lang/ja/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/ja/gallery.php
+++ b/lang/ja/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/nl/gallery.php b/lang/nl/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/nl/gallery.php
+++ b/lang/nl/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/no/gallery.php b/lang/no/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/no/gallery.php
+++ b/lang/no/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/pl/gallery.php b/lang/pl/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/pl/gallery.php
+++ b/lang/pl/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/pt/gallery.php b/lang/pt/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/pt/gallery.php
+++ b/lang/pt/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/ru/gallery.php b/lang/ru/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/ru/gallery.php
+++ b/lang/ru/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/sk/gallery.php b/lang/sk/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/sk/gallery.php
+++ b/lang/sk/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/sv/gallery.php b/lang/sv/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/sv/gallery.php
+++ b/lang/sv/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/vi/gallery.php b/lang/vi/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/vi/gallery.php
+++ b/lang/vi/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/zh_CN/gallery.php b/lang/zh_CN/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/zh_CN/gallery.php
+++ b/lang/zh_CN/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/lang/zh_TW/gallery.php b/lang/zh_TW/gallery.php
index eb8008827e0..a56564245f2 100644
--- a/lang/zh_TW/gallery.php
+++ b/lang/zh_TW/gallery.php
@@ -177,6 +177,8 @@
'no_tags' => 'No Tags',
'set_tags' => 'Set Tags',
'set_created_at' => 'Set Upload Date',
+ 'set_taken_at' => 'Set Taken Date',
+ 'set_taken_at_info' => 'When set, a star %s will be displayed to indicate that this date is not the original EXIF date.
Untick the checkbox and save to reset to the original date.',
],
],
diff --git a/resources/js/components/drawers/PhotoDetails.vue b/resources/js/components/drawers/PhotoDetails.vue
index 1b4f1135127..283e40b45da 100644
--- a/resources/js/components/drawers/PhotoDetails.vue
+++ b/resources/js/components/drawers/PhotoDetails.vue
@@ -2,9 +2,9 @@