#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:
parent
88d660d92e
commit
f46d9e1101
@ -9,6 +9,21 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
|
|
||||||
class DbHelper
|
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)
|
public static function getAlbumsForCurrentUser($parentID = -1)
|
||||||
{
|
{
|
||||||
$query = self::getAlbumsForCurrentUser_NonPaged();
|
$query = self::getAlbumsForCurrentUser_NonPaged();
|
||||||
|
66
app/Http/Controllers/Gallery/LabelController.php
Normal file
66
app/Http/Controllers/Gallery/LabelController.php
Normal 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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use App\Helpers\MiscHelper;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class Label extends Model
|
class Label extends Model
|
||||||
@ -12,9 +13,14 @@ class Label extends Model
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name'
|
'name', 'url_alias'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function generateAlias()
|
||||||
|
{
|
||||||
|
$this->url_alias = preg_replace('/[^a-z0-9\-]/', '-', strtolower($this->name));
|
||||||
|
}
|
||||||
|
|
||||||
public function photos()
|
public function photos()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Photo::class, 'photo_labels');
|
return $this->belongsToMany(Photo::class, 'photo_labels');
|
||||||
|
13
app/ModelObservers/LabelObserver.php
Normal file
13
app/ModelObservers/LabelObserver.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\ModelObservers;
|
||||||
|
|
||||||
|
use App\Label;
|
||||||
|
|
||||||
|
class LabelObserver
|
||||||
|
{
|
||||||
|
public function creating(Label $label)
|
||||||
|
{
|
||||||
|
$label->generateAlias();
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,9 @@ use App\Helpers\ImageHelper;
|
|||||||
use App\Helpers\MiscHelper;
|
use App\Helpers\MiscHelper;
|
||||||
use App\Helpers\ThemeHelper;
|
use App\Helpers\ThemeHelper;
|
||||||
use App\Helpers\ValidationHelper;
|
use App\Helpers\ValidationHelper;
|
||||||
|
use App\Label;
|
||||||
use App\ModelObservers\AlbumObserver;
|
use App\ModelObservers\AlbumObserver;
|
||||||
|
use App\ModelObservers\LabelObserver;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Mail\Mailer;
|
use Illuminate\Mail\Mailer;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
@ -47,6 +49,7 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
// Model observers
|
// Model observers
|
||||||
Album::observe(AlbumObserver::class);
|
Album::observe(AlbumObserver::class);
|
||||||
|
Label::observe(LabelObserver::class);
|
||||||
|
|
||||||
// Configure our default pager
|
// Configure our default pager
|
||||||
if (MiscHelper::isAppInstalled())
|
if (MiscHelper::isAppInstalled())
|
||||||
|
@ -16,6 +16,7 @@ class CreatePhotoLabelsTable extends Migration
|
|||||||
Schema::create('labels', function ($table) {
|
Schema::create('labels', function ($table) {
|
||||||
$table->increments('id');
|
$table->increments('id');
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
|
$table->string('url_alias');
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,6 +10,10 @@ return [
|
|||||||
'back_to_album' => 'Back to :name',
|
'back_to_album' => 'Back to :name',
|
||||||
'index_no_results_heading' => 'Start something amazing',
|
'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.',
|
'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 ":name".',
|
||||||
|
'label_no_results_text' => 'No photos are tagged with the label ":name".',
|
||||||
|
'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' => 'Manage',
|
||||||
'manage_album_link_2' => 'Manage Album',
|
'manage_album_link_2' => 'Manage Album',
|
||||||
'open_album_link' => 'Open Album',
|
'open_album_link' => 'Open Album',
|
||||||
|
46
resources/views/themes/base/gallery/label_default.blade.php
Normal file
46
resources/views/themes/base/gallery/label_default.blade.php
Normal 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
|
21
resources/views/themes/base/gallery/label_empty.blade.php
Normal file
21
resources/views/themes/base/gallery/label_empty.blade.php
Normal 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
|
@ -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
|
@ -29,10 +29,23 @@
|
|||||||
@if ($is_original_allowed)
|
@if ($is_original_allowed)
|
||||||
<a href="{{ $photo->thumbnailUrl() }}">
|
<a href="{{ $photo->thumbnailUrl() }}">
|
||||||
@endif
|
@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)
|
@if ($is_original_allowed)
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@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>
|
||||||
|
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
|
@ -89,3 +89,6 @@ Route::get('p/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@show')
|
|||||||
Route::get('i/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@download')
|
Route::get('i/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@download')
|
||||||
->name('downloadPhoto')
|
->name('downloadPhoto')
|
||||||
->where('albumUrlAlias', '.*');
|
->where('albumUrlAlias', '.*');
|
||||||
|
Route::get('label/{labelAlias}', 'Gallery\LabelController@index')
|
||||||
|
->name('viewLabel')
|
||||||
|
->where('labelAlias', '.*');
|
Loading…
Reference in New Issue
Block a user