diff --git a/app/Helpers/DbHelper.php b/app/Helpers/DbHelper.php index bf09bf6..ff61fd4 100644 --- a/app/Helpers/DbHelper.php +++ b/app/Helpers/DbHelper.php @@ -30,7 +30,7 @@ class DbHelper return self::$allowedAlbumIDs; } - + public static function getAlbumsForCurrentUser($parentID = -1) { $query = self::getAlbumsForCurrentUser_NonPaged(); @@ -43,7 +43,7 @@ class DbHelper return $query->paginate(UserConfig::get('items_per_page')); } - public static function getAlbumsForCurrentUser_NonPaged($permission = 'list') + public static function getAlbumsForCurrentUser_NonPaged() { $albumsQuery = Album::query(); $user = Auth::user(); @@ -60,7 +60,7 @@ class DbHelper ->join('permissions', 'permissions.id', '=', 'album_anonymous_permissions.permission_id') ->where([ ['permissions.section', 'album'], - ['permissions.description', $permission] + ['permissions.description', 'list'] ]); } else @@ -78,12 +78,12 @@ class DbHelper ->where('albums.user_id', $user->id) ->orWhere([ ['group_permissions.section', 'album'], - ['group_permissions.description', $permission], + ['group_permissions.description', 'list'], ['user_groups.user_id', $user->id] ]) ->orWhere([ ['user_permissions.section', 'album'], - ['user_permissions.description', $permission], + ['user_permissions.description', 'list'], ['album_user_permissions.user_id', $user->id] ]); } diff --git a/app/Http/Controllers/Admin/AlbumController.php b/app/Http/Controllers/Admin/AlbumController.php index 0b40039..a73d154 100644 --- a/app/Http/Controllers/Admin/AlbumController.php +++ b/app/Http/Controllers/Admin/AlbumController.php @@ -8,6 +8,7 @@ use App\Facade\Theme; use App\Facade\UserConfig; use App\Group; use App\Helpers\DbHelper; +use App\Helpers\FileHelper; use App\Helpers\MiscHelper; use App\Http\Controllers\Controller; use App\Http\Requests; @@ -180,6 +181,54 @@ class AlbumController extends Controller ]); } + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function metadata(Request $request, $id) + { + $this->authorizeAccessToAdminPanel('admin:manage-albums'); + + /** @var Album $album */ + $album = $this->loadAlbum($id); + + return Theme::render('admin.album_metadata', ['album' => $album, 'current_metadata' => PhotoService::METADATA_VERSION]); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function metadataPost(Request $request, $id) + { + $this->authorizeAccessToAdminPanel('admin:manage-albums'); + + /** @var Album $album */ + $album = $this->loadAlbum($id); + + $photosNeededToUpdate = $album->photos()->where('metadata_version', '<', PhotoService::METADATA_VERSION)->get(); + $queueToken = MiscHelper::randomString(); + + // First download the original of each photo that needs updating and mark it as needing analysis + foreach ($photosNeededToUpdate as $photo) + { + /** @var Photo $photo */ + $photo->is_analysed = false; + + $photoService = new PhotoService($photo); + $photoService->downloadOriginalToFolder(FileHelper::getQueuePath($queueToken)); + + $photo->save(); + } + + // Now redirect to the analysis page + return response()->redirectToRoute('albums.analyse', ['id' => $id, 'queue_token' => $queueToken]); + } + public function setGroupPermissions(Request $request, $id) { $this->authorizeAccessToAdminPanel('admin:manage-albums'); diff --git a/app/Http/Controllers/Admin/DefaultController.php b/app/Http/Controllers/Admin/DefaultController.php index fb561ca..59ca712 100644 --- a/app/Http/Controllers/Admin/DefaultController.php +++ b/app/Http/Controllers/Admin/DefaultController.php @@ -15,6 +15,7 @@ use App\Http\Requests\SaveSettingsRequest; use App\Label; use App\Mail\TestMailConfig; use App\Photo; +use App\Services\PhotoService; use App\Storage; use App\User; use Illuminate\Http\Request; @@ -32,6 +33,38 @@ class DefaultController extends Controller View::share('is_admin', true); } + public function metadataUpgrade() + { + $albums = DbHelper::getAlbumsForCurrentUser(); + $albumIDs = DbHelper::getAlbumIDsForCurrentUser(); + + $photoMetadata = DB::table('photos') + ->whereIn('album_id', $albumIDs) + ->select([ + 'album_id', + DB::raw('MIN(metadata_version) AS min_metadata_version') + ]) + ->groupBy('album_id') + ->get(); + + foreach ($photoMetadata as $metadata) + { + /** @var Album $album */ + foreach ($albums as $album) + { + if ($album->id == $metadata->album_id) + { + $album->min_metadata_version = $metadata->min_metadata_version; + } + } + } + + return Theme::render('admin.metadata_upgrade', [ + 'albums' => $albums, + 'current_metadata_version' => PhotoService::METADATA_VERSION + ]); + } + public function index() { $this->authorizeAccessToAdminPanel(); @@ -42,12 +75,15 @@ class DefaultController extends Controller $labelCount = Label::all()->count(); $userCount = User::where('is_activated', true)->count(); + $metadataUpgradeNeeded = Photo::min('metadata_version') < PhotoService::METADATA_VERSION; + return Theme::render('admin.index', [ 'album_count' => $albumCount, 'app_version' => config('app.version'), 'group_count' => $groupCount, 'label_count' => $labelCount, 'memory_limit' => ini_get('memory_limit'), + 'metadata_upgrade_needed' => $metadataUpgradeNeeded, 'photo_count' => $photoCount, 'php_version' => phpversion(), 'os_version' => exec('lsb_release -ds 2>/dev/null || cat /etc/*release 2>/dev/null | head -n1 || uname -om'), diff --git a/app/Http/Controllers/Gallery/PhotoController.php b/app/Http/Controllers/Gallery/PhotoController.php index 57c38ff..5379c0d 100644 --- a/app/Http/Controllers/Gallery/PhotoController.php +++ b/app/Http/Controllers/Gallery/PhotoController.php @@ -135,6 +135,27 @@ class PhotoController extends Controller ]); } + public function showExifData(Request $request, $albumUrlAlias, $photoFilename) + { + $album = DbHelper::getAlbumByPath($albumUrlAlias); + if (is_null($album)) + { + App::abort(404); + return null; + } + + $this->authorizeForUser($this->getUser(), 'view', $album); + + $photo = PhotoController::loadPhotoByAlbumAndFilename($album, $photoFilename); + $this->authorizeForUser($this->getUser(), 'changeMetadata', $photo); + + return Theme::render('gallery.photo_exif', [ + 'album' => $album, + 'exif_data' => print_r(unserialize($photo->raw_exif_data), true), + 'photo' => $photo + ]); + } + /** * @param $id * @return Photo diff --git a/app/Photo.php b/app/Photo.php index d473600..520ab24 100644 --- a/app/Photo.php +++ b/app/Photo.php @@ -31,6 +31,7 @@ class Photo extends Model 'width', 'height', 'is_analysed', + 'raw_exif_data', 'created_at', 'updated_at' ]; @@ -48,6 +49,14 @@ class Photo extends Model return $this->belongsTo(Album::class); } + public function exifUrl() + { + return route('viewExifData', [ + 'albumUrlAlias' => $this->album->url_path, + 'photoFilename' => $this->storage_file_name + ]); + } + public function labelIDs() { $labelIDs = []; diff --git a/app/Services/PhotoService.php b/app/Services/PhotoService.php index 2747ae6..8064fbd 100644 --- a/app/Services/PhotoService.php +++ b/app/Services/PhotoService.php @@ -12,7 +12,7 @@ use Symfony\Component\HttpFoundation\File\File; class PhotoService { - const METADATA_VERSION = 1; + const METADATA_VERSION = 2; /** * @var Album @@ -70,6 +70,7 @@ class PhotoService // Read the Exif data $exifData = @exif_read_data($photoFile); $isExifDataFound = ($exifData !== false && is_array($exifData)); + $this->photo->raw_exif_data = $isExifDataFound ? serialize($exifData) : ''; $angleToRotate = 0; // If Exif data contains an Orientation, ensure we rotate the original image as such @@ -179,6 +180,24 @@ class PhotoService $this->photo->delete(); } + public function downloadOriginalToFolder($folderPath) + { + $photoPath = join(DIRECTORY_SEPARATOR, [$folderPath, $this->photo->storage_file_name]); + $photoHandle = fopen($photoPath, 'w'); + + $stream = $this->albumSource->fetchPhotoContent($this->photo); + $stream->rewind(); + while (!$stream->feof()) + { + fwrite($photoHandle, $stream->read(4096)); + } + fflush($photoHandle); + fclose($photoHandle); + $stream->close(); + + return $photoPath; + } + public function flip($horizontal, $vertical) { // First export the original photo from the storage provider diff --git a/resources/assets/selectize/css/selectize.bootstrap2.css b/resources/assets/selectize/css/selectize.bootstrap2.css old mode 100755 new mode 100644 diff --git a/resources/assets/selectize/css/selectize.bootstrap3.css b/resources/assets/selectize/css/selectize.bootstrap3.css old mode 100755 new mode 100644 diff --git a/resources/assets/selectize/css/selectize.css b/resources/assets/selectize/css/selectize.css old mode 100755 new mode 100644 diff --git a/resources/assets/selectize/css/selectize.default.css b/resources/assets/selectize/css/selectize.default.css old mode 100755 new mode 100644 diff --git a/resources/assets/selectize/css/selectize.legacy.css b/resources/assets/selectize/css/selectize.legacy.css old mode 100755 new mode 100644 diff --git a/resources/assets/selectize/js/selectize.js b/resources/assets/selectize/js/selectize.js old mode 100755 new mode 100644 diff --git a/resources/assets/selectize/js/selectize.min.js b/resources/assets/selectize/js/selectize.min.js old mode 100755 new mode 100644 diff --git a/resources/assets/selectize/js/standalone/selectize.js b/resources/assets/selectize/js/standalone/selectize.js old mode 100755 new mode 100644 diff --git a/resources/assets/selectize/js/standalone/selectize.min.js b/resources/assets/selectize/js/standalone/selectize.min.js old mode 100755 new mode 100644 diff --git a/resources/lang/en/admin.php b/resources/lang/en/admin.php index 607396e..3738fef 100644 --- a/resources/lang/en/admin.php +++ b/resources/lang/en/admin.php @@ -123,6 +123,19 @@ return [ 'manage_widget' => [ 'panel_header' => 'Manage' ], + 'metadata_upgrade' => [ + 'can_be_upgraded' => 'Metadata can be updated (version :version_from --> :version_to)', + 'confirm' => 'Are you sure you want to update the metadata of the :name album?', + 'intro' => 'Blue Twilight analyses your photos to capture metadata such as the camera and location used to capture the photo. Sometimes we capture more metadata than we have done previously, which requires your original photos to be re-analysed.', + 'intro_2' => 'This page shows you which of your albums contain photos that need to be re-analysed to get the latest metadata information.', + 'intro_3' => 'Click the "Update Metadata" button to re-analyse an album.', + 'is_up_to_date' => 'Metadata is up-to-date', + 'title' => 'Update Photo Metadata', + 'upgrade_button' => 'Update Metadata', + 'warning' => 'This will cause the original photo images to be downloaded from your storage. If this is a cloud storage provider (e.g. Amazon S3, Rackspace) you may incur charges for this action.' + ], + 'metadata_upgrade_link' => 'More information', + 'metadata_upgrade_needed' => 'To enable new features that use metadata stored within your photos, Blue Twilight needs to re-analyse one or more of your albums.', 'move_failed_same_album' => 'The photo ":name" already belongs to the ":album" album and was not moved.', 'move_successful_message' => 'The photo ":name" was moved successfully to the ":album" album.', 'no_albums_text' => 'You have no photo albums yet. Click the button below to create one.', diff --git a/resources/lang/en/gallery.php b/resources/lang/en/gallery.php index c8d7c38..f210a5f 100644 --- a/resources/lang/en/gallery.php +++ b/resources/lang/en/gallery.php @@ -24,6 +24,7 @@ return [ 'photos' => 'photo|photos', 'previous_button' => '« Previous Photo', 'show_more_labels' => '... and :count other|... and :count others', + 'show_raw_exif_data' => 'Show all EXIF data', 'statistics' => [ 'album_by_photos' => 'Top 10 largest albums - number of photos', 'album_by_size' => 'Top 10 largest albums - photo size (MB)', diff --git a/resources/lang/en/navigation.php b/resources/lang/en/navigation.php index e338620..cebb7e8 100644 --- a/resources/lang/en/navigation.php +++ b/resources/lang/en/navigation.php @@ -17,8 +17,9 @@ return [ 'edit_storage' => 'Edit storage location', 'edit_user' => 'Edit user', 'groups' => 'Groups', - 'labels' => 'Labels', 'home' => 'Gallery', + 'labels' => 'Labels', + 'metadata_upgrade' => 'Update Photo Metadata', 'settings' => 'Settings', 'storage' => 'Storage', 'users' => 'Users' diff --git a/resources/views/themes/base/admin/album_metadata.blade.php b/resources/views/themes/base/admin/album_metadata.blade.php new file mode 100644 index 0000000..d99a6c3 --- /dev/null +++ b/resources/views/themes/base/admin/album_metadata.blade.php @@ -0,0 +1,34 @@ +@extends(Theme::viewName('layout')) +@section('title', trans('admin.metadata_upgrade.title')) + +@section('breadcrumb') + + + + + +@endsection + +@section('content') +
+
+
+
+
@yield('title')
+
+

@lang('admin.metadata_upgrade.confirm', ['name' => $album->name])

+

@lang('admin.metadata_upgrade.warning')

+ +
+
+ {{ csrf_field() }} + @lang('forms.cancel_action') + +
+
+ +
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/themes/base/admin/index.blade.php b/resources/views/themes/base/admin/index.blade.php index e4e7dc4..4110d60 100644 --- a/resources/views/themes/base/admin/index.blade.php +++ b/resources/views/themes/base/admin/index.blade.php @@ -8,6 +8,17 @@ @section('content')
+ @if ($metadata_upgrade_needed) +
+
+
+ @lang('admin.metadata_upgrade_needed')
+ @lang('admin.metadata_upgrade_link') +
+
+
+ @endif +
@include (Theme::viewName('partials.admin_actions_widget')) diff --git a/resources/views/themes/base/admin/metadata_upgrade.blade.php b/resources/views/themes/base/admin/metadata_upgrade.blade.php new file mode 100644 index 0000000..6142b27 --- /dev/null +++ b/resources/views/themes/base/admin/metadata_upgrade.blade.php @@ -0,0 +1,81 @@ +@extends(Theme::viewName('layout')) +@section('title', 'Gallery Admin') + +@section('breadcrumb') + + + +@endsection + +@section('content') +
+
+
+

@lang('admin.metadata_upgrade.title')

+
+ @lang('admin.metadata_upgrade.intro') +
+ +

@lang('admin.metadata_upgrade.intro_2')

+

@lang('admin.metadata_upgrade.intro_3')

+ + @if (count($albums) == 0) +
+

@lang('admin.no_albums_title')

+

@lang('admin.no_albums_text')

+

+ @lang('admin.create_album') +

+
+ @else + + + @foreach ($albums as $album) + @include (Theme::viewName('partials.metadata_single_album_admin')) + @endforeach + +
+ +
+ {{ $albums->links() }} +
+ @endif +
+
+
+@endsection + +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/themes/base/gallery/photo.blade.php b/resources/views/themes/base/gallery/photo.blade.php index c5bfbe6..2b9a047 100644 --- a/resources/views/themes/base/gallery/photo.blade.php +++ b/resources/views/themes/base/gallery/photo.blade.php @@ -105,6 +105,12 @@
+ + @if (!empty($photo->raw_exif_data)) + @can('changeMetadata', $photo) +

@lang('gallery.show_raw_exif_data')

+ @endcan + @endif
diff --git a/resources/views/themes/base/gallery/photo_exif.blade.php b/resources/views/themes/base/gallery/photo_exif.blade.php new file mode 100644 index 0000000..0274ab6 --- /dev/null +++ b/resources/views/themes/base/gallery/photo_exif.blade.php @@ -0,0 +1,31 @@ +@extends(Theme::viewName('layout')) +@section('title', $photo->name) + +@section('breadcrumb') + + @foreach ($album->albumParentTree() as $parentAlbum) + + @endforeach + +@endsection + +@section('content') +
+
+
+

{{ $photo->name }}

+ @if (strlen($photo->description) > 0) +

{{ $photo->description }}

+ @endif +
+
+ +
+
+

+ +
{{ $exif_data }}
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/themes/base/partials/metadata_single_album_admin.blade.php b/resources/views/themes/base/partials/metadata_single_album_admin.blade.php new file mode 100644 index 0000000..b26e3d0 --- /dev/null +++ b/resources/views/themes/base/partials/metadata_single_album_admin.blade.php @@ -0,0 +1,30 @@ + + + + + + @can('edit', $album) + {{ $album->name }} + @endcan + @cannot('edit', $album) + {{ $album->name }} + @endcannot +
+

{{ $album->description }}

+

+ {{ $album->photos_count }} {{ trans_choice('admin.stats_widget.photos', $album->photos_count) }} · + @if ($album->min_metadata_version < $current_metadata_version) + @lang('admin.metadata_upgrade.can_be_upgraded', ['version_from' => $album->min_metadata_version, 'version_to' => $current_metadata_version]) + @else + @lang('admin.metadata_upgrade.is_up_to_date') + @endif +

+ + +
+ @if ($album->min_metadata_version < $current_metadata_version) + @lang('admin.metadata_upgrade.upgrade_button') + @endcan +
+ + \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index dfd66c1..93aab88 100644 --- a/routes/web.php +++ b/routes/web.php @@ -16,6 +16,7 @@ Auth::routes(); // Administration Route::group(['prefix' => 'admin'], function () { Route::get('/', 'Admin\DefaultController@index')->name('admin'); + Route::get('/photo-metadata', 'Admin\DefaultController@metadataUpgrade')->name('admin.metadataUpgrade'); Route::post('quick-upload', 'Admin\DefaultController@quickUpload')->name('admin.quickUpload'); Route::post('settings/save', 'Admin\DefaultController@saveSettings')->name('admin.saveSettings'); Route::post('settings/test-email', 'Admin\DefaultController@testMailSettings')->name('admin.testMailSettings'); @@ -25,6 +26,8 @@ Route::group(['prefix' => 'admin'], function () { // Album management Route::get('albums/{id}/analyse/{queue_token}', 'Admin\AlbumController@analyse')->name('albums.analyse'); Route::get('albums/{id}/delete', 'Admin\AlbumController@delete')->name('albums.delete'); + Route::get('/albums/{id}/metadata', 'Admin\AlbumController@metadata')->name('albums.metadata'); + Route::post('/albums/{id}/metadata', 'Admin\AlbumController@metadataPost')->name('albums.metadataPost'); Route::post('albums/{id}/set-group-permissions', 'Admin\AlbumController@setGroupPermissions')->name('albums.set_group_permissions'); Route::post('albums/{id}/set-user-permissions', 'Admin\AlbumController@setUserPermissions')->name('albums.set_user_permissions'); Route::delete('albums/{id}/delete-redirect/{redirectId}', 'Admin\AlbumController@deleteRedirect')->name('albums.delete_redirect'); @@ -87,6 +90,9 @@ Route::get('/statistics/uploaded-12m', 'Gallery\StatisticsController@photosUploa Route::get('a/{albumUrlAlias}', 'Gallery\AlbumController@index') ->name('viewAlbum') ->where('albumUrlAlias', '.*'); +Route::get('exif/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@showExifData') + ->name('viewExifData') + ->where('albumUrlAlias', '.*'); Route::get('p/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@show') ->name('viewPhoto') ->where('albumUrlAlias', '.*');