Browse Source

First batch of changes for #38 to allow photo metadata updates

tags/v2.1.0-beta.1
Andy Heathershaw 2 years ago
parent
commit
0b64728d0a
25 changed files with 355 additions and 7 deletions
  1. +5
    -5
      app/Helpers/DbHelper.php
  2. +49
    -0
      app/Http/Controllers/Admin/AlbumController.php
  3. +36
    -0
      app/Http/Controllers/Admin/DefaultController.php
  4. +21
    -0
      app/Http/Controllers/Gallery/PhotoController.php
  5. +9
    -0
      app/Photo.php
  6. +20
    -1
      app/Services/PhotoService.php
  7. +0
    -0
      resources/assets/selectize/css/selectize.bootstrap2.css
  8. +0
    -0
      resources/assets/selectize/css/selectize.bootstrap3.css
  9. +0
    -0
      resources/assets/selectize/css/selectize.css
  10. +0
    -0
      resources/assets/selectize/css/selectize.default.css
  11. +0
    -0
      resources/assets/selectize/css/selectize.legacy.css
  12. +0
    -0
      resources/assets/selectize/js/selectize.js
  13. +0
    -0
      resources/assets/selectize/js/selectize.min.js
  14. +0
    -0
      resources/assets/selectize/js/standalone/selectize.js
  15. +0
    -0
      resources/assets/selectize/js/standalone/selectize.min.js
  16. +13
    -0
      resources/lang/en/admin.php
  17. +1
    -0
      resources/lang/en/gallery.php
  18. +2
    -1
      resources/lang/en/navigation.php
  19. +34
    -0
      resources/views/themes/base/admin/album_metadata.blade.php
  20. +11
    -0
      resources/views/themes/base/admin/index.blade.php
  21. +81
    -0
      resources/views/themes/base/admin/metadata_upgrade.blade.php
  22. +6
    -0
      resources/views/themes/base/gallery/photo.blade.php
  23. +31
    -0
      resources/views/themes/base/gallery/photo_exif.blade.php
  24. +30
    -0
      resources/views/themes/base/partials/metadata_single_album_admin.blade.php
  25. +6
    -0
      routes/web.php

+ 5
- 5
app/Helpers/DbHelper.php View File

@@ -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]
]);
}

+ 49
- 0
app/Http/Controllers/Admin/AlbumController.php View File

@@ -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');

+ 36
- 0
app/Http/Controllers/Admin/DefaultController.php View File

@@ -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'),

+ 21
- 0
app/Http/Controllers/Gallery/PhotoController.php View File

@@ -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

+ 9
- 0
app/Photo.php View File

@@ -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 = [];

+ 20
- 1
app/Services/PhotoService.php View File

@@ -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

+ 0
- 0
resources/assets/selectize/css/selectize.bootstrap2.css View File


+ 0
- 0
resources/assets/selectize/css/selectize.bootstrap3.css View File


+ 0
- 0
resources/assets/selectize/css/selectize.css View File


+ 0
- 0
resources/assets/selectize/css/selectize.default.css View File


+ 0
- 0
resources/assets/selectize/css/selectize.legacy.css View File


+ 0
- 0
resources/assets/selectize/js/selectize.js View File


+ 0
- 0
resources/assets/selectize/js/selectize.min.js View File


+ 0
- 0
resources/assets/selectize/js/standalone/selectize.js View File


+ 0
- 0
resources/assets/selectize/js/standalone/selectize.min.js View File


+ 13
- 0
resources/lang/en/admin.php View File

@@ -123,6 +123,19 @@ return [
'manage_widget' => [
'panel_header' => 'Manage'
],
'metadata_upgrade' => [
'can_be_upgraded' => 'Metadata can be updated (version :version_from --&gt; :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.',

+ 1
- 0
resources/lang/en/gallery.php View File

@@ -24,6 +24,7 @@ return [
'photos' => 'photo|photos',
'previous_button' => '&laquo; 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)',

+ 2
- 1
resources/lang/en/navigation.php View File

@@ -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'

+ 34
- 0
resources/views/themes/base/admin/album_metadata.blade.php View File

@@ -0,0 +1,34 @@
@extends(Theme::viewName('layout'))
@section('title', trans('admin.metadata_upgrade.title'))

@section('breadcrumb')
<li class="breadcrumb-item"><a href="{{ route('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
<li class="breadcrumb-item"><a href="{{ route('admin') }}">@lang('navigation.breadcrumb.admin')</a></li>
<li class="breadcrumb-item"><a href="{{ route('albums.index') }}">@lang('navigation.breadcrumb.albums')</a></li>
<li class="breadcrumb-item"><a href="{{ route('albums.show', ['id' => $album->id]) }}">{{ $album->name }}</a></li>
<li class="breadcrumb-item active">@lang('navigation.breadcrumb.metadata_upgrade')</li>
@endsection

@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 ml-md-auto mr-md-auto">
<div class="card bg-primary">
<div class="card-header text-white">@yield('title')</div>
<div class="card-body bg-light">
<p>@lang('admin.metadata_upgrade.confirm', ['name' => $album->name])</p>
<p class="text-danger"><b>@lang('admin.metadata_upgrade.warning')</b></p>

<div class="text-right">
<form action="{{ route('albums.metadataPost', [$album->id]) }}" method="POST">
{{ csrf_field() }}
<a href="{{ route('admin.metadataUpgrade', ['id' => $album->id]) }}" class="btn btn-link">@lang('forms.cancel_action')</a>
<button type="submit" class="btn btn-primary"><i class="fa fa-fw fa-check"></i> @lang('forms.continue_action')</button>
</form>
</div>
</pdiv>
</div>
</div>
</div>
</div>
@endsection

+ 11
- 0
resources/views/themes/base/admin/index.blade.php View File

@@ -8,6 +8,17 @@

@section('content')
<div class="container">
@if ($metadata_upgrade_needed)
<div class="row">
<div class="col">
<div class="alert alert-warning">
@lang('admin.metadata_upgrade_needed')<br/>
<a href="{{ route('admin.metadataUpgrade') }}">@lang('admin.metadata_upgrade_link')</a>
</div>
</div>
</div>
@endif

<div class="row">
<div class="col-md-4 order-1 order-md-2">
@include (Theme::viewName('partials.admin_actions_widget'))

+ 81
- 0
resources/views/themes/base/admin/metadata_upgrade.blade.php View File

@@ -0,0 +1,81 @@
@extends(Theme::viewName('layout'))
@section('title', 'Gallery Admin')

@section('breadcrumb')
<li class="breadcrumb-item"><a href="{{ route('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
<li class="breadcrumb-item"><a href="{{ route('admin') }}">@lang('navigation.breadcrumb.admin')</a></li>
<li class="breadcrumb-item active">@lang('navigation.breadcrumb.metadata_upgrade')</li>
@endsection

@section('content')
<div class="container">
<div class="row">
<div class="col">
<h1>@lang('admin.metadata_upgrade.title')</h1>
<div class="alert alert-info mb-4">
<i class="fa fa-fw fa-info"></i> @lang('admin.metadata_upgrade.intro')
</div>

<p>@lang('admin.metadata_upgrade.intro_2')</p>
<p class="mb-4">@lang('admin.metadata_upgrade.intro_3')</p>

@if (count($albums) == 0)
<div class="text-center">
<h4 class="text-danger"><b>@lang('admin.no_albums_title')</b></h4>
<p>@lang('admin.no_albums_text')</p>
<p style="margin-top: 40px;">
<a href="{{ route('albums.create') }}" class="btn btn-lg btn-success"><i class="fa fa-fw fa-plus"></i> @lang('admin.create_album')</a>
</p>
</div>
@else
<table class="table table-hover table-striped">
<tbody>
@foreach ($albums as $album)
@include (Theme::viewName('partials.metadata_single_album_admin'))
@endforeach
</tbody>
</table>

<div class="text-center">
{{ $albums->links() }}
</div>
@endif
</div>
</div>
</div>
@endsection

@push('scripts')
<script type="text/javascript">
$(document).ready(function() {
$('.album-expand-handle').click(function() {
var parent = $(this).closest('tr');

var handle = $('.album-expand-handle', parent);
var albumID = parent.data('album-id');
$('tr[data-parent-album-id=' + albumID + ']').toggle();

if (handle.hasClass('fa-plus'))
{
handle.addClass('fa-minus');
handle.removeClass('fa-plus');
}
else
{
// Toggle all children
$('tr[data-parent-album-id=' + albumID + ']').each(function(index, element)
{
var childHandle = $('.album-expand-handle', element);
if (childHandle.hasClass('fa-minus'))
{
childHandle.click();
}
});

handle.addClass('fa-plus');
handle.removeClass('fa-minus');
}
})
})
</script>
@endpush

+ 6
- 0
resources/views/themes/base/gallery/photo.blade.php View File

@@ -105,6 +105,12 @@
</table>
</div>
</div>

@if (!empty($photo->raw_exif_data))
@can('changeMetadata', $photo)
<p><a href="{{ $photo->exifUrl() }}" class="btn btn-primary mt-3"><i class="fa fa-eye fa-fw"></i> @lang('gallery.show_raw_exif_data')</a></p>
@endcan
@endif
</div>
</div>


+ 31
- 0
resources/views/themes/base/gallery/photo_exif.blade.php View File

@@ -0,0 +1,31 @@
@extends(Theme::viewName('layout'))
@section('title', $photo->name)

@section('breadcrumb')
<li class="breadcrumb-item"><a href="{{ route('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
@foreach ($album->albumParentTree() as $parentAlbum)
<li class="breadcrumb-item"><a href="{{ $parentAlbum->url() }}">{{ $parentAlbum->name }}</a></li>
@endforeach
<li class="breadcrumb-item active">{{ $photo->name }}</li>
@endsection

@section('content')
<div class="container">
<div class="row">
<div class="col">
<h1>{{ $photo->name }}</h1>
@if (strlen($photo->description) > 0)
<p>{{ $photo->description }}</p>
@endif
</div>
</div>

<div class="row">
<div class="col content-body">
<p class="text-center"><img src="{{ $photo->thumbnailUrl('fullsize') }}" alt="" class="img-thumbnail mb-4"/></p>

<pre>{{ $exif_data }}</pre>
</div>
</div>
</div>
@endsection

+ 30
- 0
resources/views/themes/base/partials/metadata_single_album_admin.blade.php View File

@@ -0,0 +1,30 @@
<tr data-album-id="{{ $album->id }}">
<td style="width: 20px;">
</td>
<td>
<span style="font-size: 1.3em;">
@can('edit', $album)
<a href="{{ route('albums.show', ['id' => $album->id]) }}">{{ $album->name }}</a>
@endcan
@cannot('edit', $album)
{{ $album->name }} <i class="fa fa-fw fa-lock"></i>
@endcannot
</span><br/>
<p>{{ $album->description }}</p>
<p style="margin-bottom: 0;">
<b>{{ $album->photos_count }}</b> {{ trans_choice('admin.stats_widget.photos', $album->photos_count) }} &middot;
@if ($album->min_metadata_version < $current_metadata_version)
<span class="text-danger">@lang('admin.metadata_upgrade.can_be_upgraded', ['version_from' => $album->min_metadata_version, 'version_to' => $current_metadata_version])</span>
@else
<span class="text-success">@lang('admin.metadata_upgrade.is_up_to_date')</span>
@endif
</p>
</td>
<td class="text-right">
<div class="btn-group">
@if ($album->min_metadata_version < $current_metadata_version)
<a href="{{ route('albums.metadata', ['id' => $album->id]) }}" class="btn btn-primary">@lang('admin.metadata_upgrade.upgrade_button')</a>
@endcan
</div>
</td>
</tr>

+ 6
- 0
routes/web.php View File

@@ -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', '.*');

Loading…
Cancel
Save