#29: Album permissions are now checked when retrieving photos linked with a label. Labels are displayed in the gallery with their own views.

This commit is contained in:
Andy Heathershaw 2017-09-10 11:24:44 +01:00
parent 88d660d92e
commit f46d9e1101
12 changed files with 270 additions and 3 deletions

View File

@ -9,6 +9,21 @@ use Illuminate\Support\Facades\Auth;
class DbHelper
{
public static function getAlbumIDsForCurrentUser()
{
$query = self::getAlbumsForCurrentUser_NonPaged();
$query->select('albums.id');
$ids = [];
foreach ($query->get() as $album)
{
$ids[] = $album->id;
}
return $ids;
}
public static function getAlbumsForCurrentUser($parentID = -1)
{
$query = self::getAlbumsForCurrentUser_NonPaged();

View File

@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers\Gallery;
use App\Facade\Theme;
use App\Facade\UserConfig;
use App\Helpers\DbHelper;
use App\Http\Controllers\Controller;
use App\Label;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\Request;
class LabelController extends Controller
{
public function index(Request $request, $labelAlias)
{
$label = Label::where('url_alias', $labelAlias)->first();
if (is_null($label))
{
App::abort(404);
}
$validViews = UserConfig::allowedAlbumViews();
$requestedView = strtolower($request->get('view'));
if (!in_array($requestedView, $validViews))
{
$requestedView = $validViews[0];
}
$allowedAlbumIDs = DbHelper::getAlbumIDsForCurrentUser();
if ($label->photos()->count() == 0)
{
$requestedView = 'empty';
$photos = [];
}
else if ($requestedView != 'slideshow')
{
$photos = $label->photos()
->where('album_id', $allowedAlbumIDs)
->orderBy(DB::raw('COALESCE(photos.taken_at, photos.created_at)'))
->paginate(UserConfig::get('items_per_page'));
}
else
{
// The slideshow view needs access to all photos, not paged
$photos = $label->photos()
->where('album_id', $allowedAlbumIDs)
->orderBy(DB::raw('COALESCE(photos.taken_at, photos.created_at)'))
->get();
}
if (count($photos) == 0)
{
$requestedView = 'empty';
}
return Theme::render(sprintf('gallery.label_%s', $requestedView), [
'allowed_views' => $validViews,
'current_view' => $requestedView,
'label' => $label,
'photos' => $photos
]);
}
}

View File

@ -2,6 +2,7 @@
namespace App;
use App\Helpers\MiscHelper;
use Illuminate\Database\Eloquent\Model;
class Label extends Model
@ -12,9 +13,14 @@ class Label extends Model
* @var array
*/
protected $fillable = [
'name'
'name', 'url_alias'
];
public function generateAlias()
{
$this->url_alias = preg_replace('/[^a-z0-9\-]/', '-', strtolower($this->name));
}
public function photos()
{
return $this->belongsToMany(Photo::class, 'photo_labels');

View File

@ -0,0 +1,13 @@
<?php
namespace App\ModelObservers;
use App\Label;
class LabelObserver
{
public function creating(Label $label)
{
$label->generateAlias();
}
}

View File

@ -8,7 +8,9 @@ use App\Helpers\ImageHelper;
use App\Helpers\MiscHelper;
use App\Helpers\ThemeHelper;
use App\Helpers\ValidationHelper;
use App\Label;
use App\ModelObservers\AlbumObserver;
use App\ModelObservers\LabelObserver;
use Illuminate\Database\QueryException;
use Illuminate\Mail\Mailer;
use Illuminate\Pagination\LengthAwarePaginator;
@ -47,6 +49,7 @@ class AppServiceProvider extends ServiceProvider
// Model observers
Album::observe(AlbumObserver::class);
Label::observe(LabelObserver::class);
// Configure our default pager
if (MiscHelper::isAppInstalled())

View File

@ -16,6 +16,7 @@ class CreatePhotoLabelsTable extends Migration
Schema::create('labels', function ($table) {
$table->increments('id');
$table->string('name');
$table->string('url_alias');
$table->timestamps();
});

View File

@ -10,6 +10,10 @@ return [
'back_to_album' => 'Back to :name',
'index_no_results_heading' => 'Start something amazing',
'index_no_results_text' => 'This gallery is currently empty. If you are the owner of this gallery, you can create new albums and upload photos using the :admin_link.',
'label_intro' => 'All photos tagged with the label &quot;:name&quot;.',
'label_no_results_text' => 'No photos are tagged with the label &quot;:name&quot;.',
'label_no_results_text_2' => 'If you are an admin of this gallery, you can upload and tag photos in the :admin_link.',
'labels' => 'Labels:',
'manage_album_link' => 'Manage',
'manage_album_link_2' => 'Manage Album',
'open_album_link' => 'Open Album',

View File

@ -0,0 +1,46 @@
@extends('themes.base.layout')
@section('title', $label->name)
@section('breadcrumb')
<li class="breadcrumb-item"><a href="{{ route('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
<li class="breadcrumb-item">{{ $label->name }}</li>
@endsection
@section('content')
<div class="container album-container">
<div class="row">
<div class="col">
<div class="pull-right">
@include(\App\Facade\Theme::viewName('partials.album_view_selector'))
</div>
<h1 class="page-title">{{ $label->name }}</h1>
<p>@lang('gallery.label_intro', ['name' => $label->name])</p>
<div class="clearfix"><!-- --></div>
<hr/>
</div>
</div>
<div class="row">
@foreach ($photos as $photo)
<div class="col-sm-6 col-md-4 photo mb-3">
<div class="card">
<img src="{{ $photo->thumbnailUrl('preview') }}" alt="" class="card-img-top"/>
<div class="card-body">
<h4 class="card-title"><a href="{{ $photo->url() }}">{{ $photo->name }}</a></h4>
</div>
<div class="card-footer">
<small class="text-muted"><i class="fa fa-fw fa-calendar"></i> {{ date(UserConfig::get('date_format'), strtotime(!is_null($photo->taken_at) ? $photo->taken_at : $photo->created_at)) }}</small>
</div>
</div>
</div>
@endforeach
<div class="row" style="margin-top: 15px;">
<div class="col text-center">
{{ $photos->links() }}
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,21 @@
@extends('themes.base.layout')
@section('title', $label->name)
@section('breadcrumb')
<li class="breadcrumb-item"><a href="{{ route('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
<li class="breadcrumb-item">{{ $label->name }}</li>
@endsection
@section('content')
<div class="container album-container">
<div class="row">
<div class="col text-center">
<h1>@lang('gallery.album_no_results_heading')</h1>
<p style="line-height: 1.6em;">@lang('gallery.label_no_results_text', ['name' => $label->name])</p>
<p style="margin-bottom: 30px; line-height: 1.6em;">@lang('gallery.label_no_results_text_2', ['admin_link' => sprintf('<a href="%s">%s</a>', route('admin'), trans('admin.title'))])</p>
<img src="{{ asset('themes/base/images/smartphone-photo.jpg') }}" class="img-fluid rounded" style="display: inline;" />
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,76 @@
@extends('themes.base.layout')
@section('title', $label->name)
@section('breadcrumb')
<li class="breadcrumb-item"><a href="{{ route('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
<li class="breadcrumb-item">{{ $label->name }}</li>
@endsection
@section('content')
<div class="container album-container album-slideshow-container" id="slideshow-container">
<div class="row">
<div class="col">
<div class="pull-right">
@include(\App\Facade\Theme::viewName('partials.album_view_selector'))
</div>
<h1 class="page-title">{{ $label->name }}</h1>
<p>@lang('gallery.label_intro', ['name' => $label->name])</p>
<div class="clearfix"><!-- --></div>
<hr/>
</div>
</div>
<div class="row">
<div class="col text-center">
<div class="pull-right">
<div class="btn btn-group">
<button class="btn btn-secondary" v-on:click="pauseSlideshow" v-bind:disabled="isPaused" v-if="isRunning"><i class="fa fa-fw fa-pause"></i></button>
<button class="btn btn-secondary" v-on:click="continueSlideshow" v-bind:disabled="!isPaused" v-if="isRunning"><i class="fa fa-fw fa-play"></i></button>
</div>
</div>
<div id="image-preview" v-if="current !== null">
<a v-bind:href="current.info_url" v-bind:title="current.description">
<img class="rounded thumbnail" v-bind:src="current.original_url" v-bind:alt="current.name" v-bind:title="current.description"/>
</a>
</div>
</div>
</div>
<div class="row" style="margin-top: 15px;">
<div class="col">
<div class="thumbnails">
@foreach ($photos as $photo)
<a href="{{ $photo->url() }}" v-on:click="changeCurrentImage({{ $photo->id }}); $event.preventDefault();">
<img class="ss-thumbnail rounded" src="{{ $photo->thumbnailUrl('slideshow-tiny') }}" alt="{{ $photo->name }}" title="{{ $photo->description }}" data-photo-id="{{ $photo->id }}" data-original-url="{{ $photo->thumbnailUrl('fullsize') }}" data-info-url="{{ $photo->url() }}" />
</a>
@endforeach
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script type="text/javascript">
// TODO make this timeout configurable
var viewModel = new SlideShowViewModel(5000);
$(document).ready(function() {
$('img.ss-thumbnail').each(function(index, element)
{
viewModel.data.images.push({
'id': $(element).data('photo-id'),
'name': $(element).attr('alt'),
'description': $(element).attr('title'),
'original_url': $(element).data('original-url'),
'info_url': $(element).data('info-url')
});
});
var app = new Vue(viewModel);
app.startSlideshow();
});
</script>
@endpush

View File

@ -29,10 +29,23 @@
@if ($is_original_allowed)
<a href="{{ $photo->thumbnailUrl() }}">
@endif
<img src="{{ $photo->thumbnailUrl('fullsize') }}" alt="" class="img-thumbnail"/>
<img src="{{ $photo->thumbnailUrl('fullsize') }}" alt="" class="img-thumbnail mb-4"/>
@if ($is_original_allowed)
</a>
@endif
@if ($photo->labels()->count() > 0)
<h4>@lang('gallery.labels')</h4>
<ul class="nav nav-pills mb-4">
@foreach ($photo->labels()->orderBy('name')->get() as $label)
<li class="nav-item">
<a class="nav-link" href="{{ route('viewLabel', ['labelAlias' => $label->url_alias]) }}">
<i class="fa fa-tag"></i> {{ $label->name }}
</a>
</li>
@endforeach
</ul>
@endif
</div>
<div class="col-sm-4">

View File

@ -88,4 +88,7 @@ Route::get('p/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@show')
->where('albumUrlAlias', '.*');
Route::get('i/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@download')
->name('downloadPhoto')
->where('albumUrlAlias', '.*');
->where('albumUrlAlias', '.*');
Route::get('label/{labelAlias}', 'Gallery\LabelController@index')
->name('viewLabel')
->where('labelAlias', '.*');