#3: Permissions can now be set on what users can do with photos in an album. This required re-thinking the available permissions slightly. Photo owners can do anything.

This commit is contained in:
Andy Heathershaw 2017-04-16 09:00:57 +01:00
parent 1312808d75
commit 2d8ba9da16
18 changed files with 556 additions and 348 deletions

View File

@ -36,7 +36,7 @@ class AlbumController extends Controller
{
$this->authorizeAccessToAdminPanel('admin:manage-albums');
$album = $this->loadAlbum($id);
$album = $this->loadAlbum($id, 'upload-photos');
$photos = $album->photos()
->where('is_analysed', false)
->orderBy('created_at')
@ -83,7 +83,7 @@ class AlbumController extends Controller
{
$this->authorizeAccessToAdminPanel('admin:manage-albums');
$album = $this->loadAlbum($id);
$album = $this->loadAlbum($id, 'delete');
return Theme::render('admin.delete_album', ['album' => $album]);
}
@ -98,7 +98,7 @@ class AlbumController extends Controller
{
$this->authorizeAccessToAdminPanel('admin:manage-albums');
$album = $this->loadAlbum($id);
$album = $this->loadAlbum($id, 'delete');
// Delete all the photo files
/** @var Photo $photo */
@ -122,12 +122,17 @@ class AlbumController extends Controller
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
public function edit(Request $request, $id)
{
$this->authorizeAccessToAdminPanel('admin:manage-albums');
$album = $this->loadAlbum($id);
if (!$request->session()->has('_old_input'))
{
$request->session()->flash('_old_input', $album->toArray());
}
return Theme::render('admin.edit_album', ['album' => $album]);
}
@ -153,7 +158,7 @@ class AlbumController extends Controller
$this->authorizeAccessToAdminPanel('admin:manage-albums');
/** @var Album $album */
$album = $this->loadAlbum($id);
$album = $this->loadAlbum($id, 'change-permissions');
if ($request->get('action') == 'add_group' && $request->has('group_id'))
{
@ -209,7 +214,7 @@ class AlbumController extends Controller
$this->authorizeAccessToAdminPanel('admin:manage-albums');
/** @var Album $album */
$album = $this->loadAlbum($id);
$album = $this->loadAlbum($id, 'change-permissions');
if ($request->get('action') == 'add_user' && $request->has('user_id'))
{
@ -331,6 +336,11 @@ class AlbumController extends Controller
$activeTab = $request->get('tab');
if (!$request->session()->has('_old_input'))
{
$request->session()->flash('_old_input', $album->toArray());
}
return Theme::render('admin.show_album', [
'active_tab' => (strlen($activeTab) == 0) ? 'photos' : $activeTab,
'album' => $album,
@ -428,11 +438,8 @@ class AlbumController extends Controller
App::abort(404);
return null;
}
else if (!Auth::user()->can($permission, $album))
{
App::abort(403);
return null;
}
$this->authorize($permission, $album);
return $album;
}

View File

@ -95,13 +95,7 @@ class PhotoController extends Controller
{
$this->authorizeAccessToAdminPanel();
/** @var Photo $photo */
$photo = Photo::where('id', intval($id))->first();
if (is_null($photo))
{
App::abort(404);
return null;
}
$photo = $this->loadPhoto($id, 'delete');
$photoService = new PhotoService($photo);
$photoService->delete();
@ -116,12 +110,7 @@ class PhotoController extends Controller
settype($horizontal, 'boolean');
settype($vertical, 'boolean');
$photo = Photo::where('id', intval($photoId))->first();
if (is_null($photo))
{
App::abort(404);
return null;
}
$photo = $this->loadPhoto($photoId, 'manipulate');
$photoService = new PhotoService($photo);
$photoService->flip($horizontal, $vertical);
@ -131,11 +120,7 @@ class PhotoController extends Controller
{
$this->authorizeAccessToAdminPanel();
$photo = Photo::where('id', intval($photoId))->first();
if (is_null($photo))
{
App::abort(404);
}
$photo = $this->loadPhoto($photoId, 'manipulate');
$newAlbum = Album::where('id', intval($request->get('new_album_id')))->first();
if (is_null($newAlbum))
@ -162,13 +147,7 @@ class PhotoController extends Controller
{
$this->authorizeAccessToAdminPanel();
/** @var Photo $photo */
$photo = Photo::where('id', intval($photoId))->first();
if (is_null($photo))
{
App::abort(404);
return null;
}
$photo = $this->loadPhoto($photoId, 'change-metadata');
$result = ['is_successful' => false, 'message' => ''];
@ -192,12 +171,7 @@ class PhotoController extends Controller
{
$this->authorizeAccessToAdminPanel();
$photo = Photo::where('id', intval($photoId))->first();
if (is_null($photo))
{
App::abort(404);
return null;
}
$photo = $this->loadPhoto($photoId, 'manipulate');
if ($angle != 90 && $angle != 180 && $angle != 270)
{
@ -222,7 +196,7 @@ class PhotoController extends Controller
$photoFiles = $request->files->get('photo');
// Load the linked album
$album = $this->loadAlbum($request->get('album_id'));
$album = $this->loadAlbum($request->get('album_id'), 'upload-photos');
$isSuccessful = false;
// Create the folder to hold the analysis results if not already present
@ -406,16 +380,9 @@ class PhotoController extends Controller
{
$this->authorizeAccessToAdminPanel();
/** @var Album $album */
$album = Album::where('id', intval($albumId))->first();
$album = $this->loadAlbum($albumId);
if (is_null($album))
{
App::abort(404);
return null;
}
if ($request->has('bulk-action'))
if ($request->has('bulk-apply'))
{
$numberChanged = $this->applyBulkActions($request, $album);
}
@ -468,54 +435,87 @@ class PhotoController extends Controller
foreach ($photosToProcess as $photo)
{
$changed = false;
$photoService = new PhotoService($photo);
$doNotSave = false;
switch (strtolower($action))
{
case 'change_album':
$newAlbumId = intval($request->get('new-album-id'));
if ($newAlbumId == $photo->album_id)
if (Auth::user()->can('change-metadata', $photo))
{
// Photo already belongs to this album, don't move
continue;
}
$newAlbumId = intval($request->get('new-album-id'));
if ($newAlbumId == $photo->album_id)
{
// Photo already belongs to this album, don't move
continue;
}
$newAlbum = Album::where('id', $newAlbumId)->first();
if (is_null($newAlbum))
{
App::abort(404);
}
$newAlbum = Album::where('id', $newAlbumId)->first();
if (is_null($newAlbum))
{
App::abort(404);
}
$photoService->changeAlbum($newAlbum);
$photoService->changeAlbum($newAlbum);
$changed = true;
}
break;
case 'delete':
$photoService->delete();
$doNotSave = true;
if (Auth::user()->can('delete', $photo))
{
$photoService->delete();
$doNotSave = true;
$changed = true;
}
break;
case 'flip_both':
$photoService->flip(true, true);
if (Auth::user()->can('manipulate', $photo))
{
$photoService->flip(true, true);
$changed = true;
}
break;
case 'flip_horizontal':
$photoService->flip(true, false);
if (Auth::user()->can('manipulate', $photo))
{
$photoService->flip(true, false);
$changed = true;
}
break;
case 'flip_vertical':
$photoService->flip(false, true);
if (Auth::user()->can('manipulate', $photo))
{
$photoService->flip(false, true);
$changed = true;
}
break;
case 'refresh_thumbnails':
$photoService->regenerateThumbnails();
if (Auth::user()->can('change-metadata', $photo))
{
$photoService->regenerateThumbnails();
$changed = true;
}
break;
case 'rotate_left':
$photoService->rotate(90);
if (Auth::user()->can('manipulate', $photo))
{
$photoService->rotate(90);
$changed = true;
}
break;
case 'rotate_right':
$photoService->rotate(270);
if (Auth::user()->can('manipulate', $photo))
{
$photoService->rotate(270);
$changed = true;
}
break;
}
@ -524,7 +524,10 @@ class PhotoController extends Controller
$photo->save();
}
$numberChanged++;
if ($changed)
{
$numberChanged++;
}
}
return $numberChanged;
@ -534,7 +537,7 @@ class PhotoController extends Controller
* @param $id
* @return Album
*/
private function loadAlbum($id)
private function loadAlbum($id, $permission = 'edit')
{
$album = Album::where('id', intval($id))->first();
if (is_null($album))
@ -543,9 +546,33 @@ class PhotoController extends Controller
return null;
}
$this->authorize($permission, $album);
return $album;
}
/**
* @param $id
* @param string|null $permission
* @return Photo
*/
private function loadPhoto($id, $permission = null)
{
$photo = Photo::where('id', intval($id))->first();
if (is_null($photo))
{
App::abort(404);
return null;
}
if (!is_null($permission))
{
$this->authorize($permission, $photo);
}
return $photo;
}
private function updatePhotoDetails(Request $request, Album $album)
{
$numberChanged = 0;
@ -555,14 +582,13 @@ class PhotoController extends Controller
{
/** @var Photo $photo */
$photo = $album->photos()->where('id', intval($photoId))->first();
if (is_null($photo))
if (is_null($photo) || !Auth::user()->can('change-metadata', $photo))
{
continue;
}
$photo->fill($value);
$photo->save();
$numberChanged++;
}

View File

@ -31,6 +31,46 @@ class AlbumPolicy
}
}
public function changePermissions(User $user, Album $album)
{
// Only the album's owner (or an admin, matched by the before() rule) can change permissions
return $user->id == $album->user_id;
}
public function changePhotoMetadata(User $user, Album $album)
{
if ($user->id == $album->user_id)
{
// The album's owner and can do everything
return true;
}
// Get the edit permission
$permission = Permission::where([
'section' => 'album',
'description' => 'change-photo-metadata'
])->first();
return $this->userHasPermission($user, $album, $permission);
}
public function deletePhotos(User $user, Album $album)
{
if ($user->id == $album->user_id)
{
// The album's owner and can do everything
return true;
}
// Get the edit permission
$permission = Permission::where([
'section' => 'album',
'description' => 'delete-photos'
])->first();
return $this->userHasPermission($user, $album, $permission);
}
public function edit(User $user, Album $album)
{
if ($user->id == $album->user_id)
@ -48,6 +88,40 @@ class AlbumPolicy
return $this->userHasPermission($user, $album, $permission);
}
public function manipulatePhotos(User $user, Album $album)
{
if ($user->id == $album->user_id)
{
// The album's owner and can do everything
return true;
}
// Get the edit permission
$permission = Permission::where([
'section' => 'album',
'description' => 'manipulate-photos'
])->first();
return $this->userHasPermission($user, $album, $permission);
}
public function uploadPhotos(User $user, Album $album)
{
if ($user->id == $album->user_id)
{
// The album's owner and can do everything
return true;
}
// Get the edit permission
$permission = Permission::where([
'section' => 'album',
'description' => 'upload-photos'
])->first();
return $this->userHasPermission($user, $album, $permission);
}
public function view(User $user, Album $album)
{
if ($user->id == $album->user_id)

View File

@ -0,0 +1,64 @@
<?php
namespace App\Policies;
use App\Photo;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class PhotoPolicy
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* @return void
*/
public function __construct()
{
//
}
public function before($user, $ability)
{
if ($user->is_admin)
{
// Admins can do anything
return true;
}
}
public function changeMetadata(User $user, Photo $photo)
{
if ($user->id == $photo->user_id)
{
// The photo's owner can do everything
return true;
}
return $user->can('change-photo-metadata', $photo->album);
}
public function delete(User $user, Photo $photo)
{
if ($user->id == $photo->user_id)
{
// The photo's owner can do everything
return true;
}
return $user->can('delete-photos', $photo->album);
}
public function manipulate(User $user, Photo $photo)
{
if ($user->id == $photo->user_id)
{
// The photo's owner can do everything
return true;
}
return $user->can('manipulate-photos', $photo->album);
}
}

View File

@ -8,6 +8,7 @@ use App\Group;
use App\Permission;
use App\Photo;
use App\Policies\AlbumPolicy;
use App\Policies\PhotoPolicy;
use App\User;
use function GuzzleHttp\Psr7\mimetype_from_extension;
use Illuminate\Support\Facades\Gate;
@ -26,7 +27,8 @@ class AuthServiceProvider extends ServiceProvider
* @var array
*/
protected $policies = [
Album::class => AlbumPolicy::class
Album::class => AlbumPolicy::class,
Photo::class => PhotoPolicy::class
];
/**

View File

@ -111,7 +111,7 @@ class PermissionsSeeder extends Seeder
// album:edit-own-photos = controls if existing photos, owned by the current user, in the album can be edited
DatabaseSeeder::createOrUpdate('permissions', [
'section' => 'album',
'description' => 'edit-own-photos',
'description' => 'manipulate-photos',
'is_default' => true,
'sort_order' => 40
]);
@ -119,7 +119,7 @@ class PermissionsSeeder extends Seeder
// album:edit-other-photos = controls if existing photos, owned by other users, in the album can be edited
DatabaseSeeder::createOrUpdate('permissions', [
'section' => 'album',
'description' => 'edit-other-photos',
'description' => 'change-photo-metadata',
'is_default' => true,
'sort_order' => 50
]);
@ -127,17 +127,9 @@ class PermissionsSeeder extends Seeder
// album:delete-own-photos = controls if existing photos, owned by the current user, in the album can be deleted
DatabaseSeeder::createOrUpdate('permissions', [
'section' => 'album',
'description' => 'delete-own-photos',
'description' => 'delete-photos',
'is_default' => true,
'sort_order' => 60
]);
// album:delete-other-photos = controls if existing photos, owned by other users, in the album can be deleted
DatabaseSeeder::createOrUpdate('permissions', [
'section' => 'album',
'description' => 'delete-other-photos',
'is_default' => true,
'sort_order' => 70
]);
}
}

View File

@ -163,6 +163,7 @@ function EditPhotosViewModel(album_id, language, urls) {
return true;
}
var self = this;
var bulk_form = $(e.target).closest('form');
if (this.bulkModifyMethod === 'change_album') {
@ -176,6 +177,7 @@ function EditPhotosViewModel(album_id, language, urls) {
_bt_showLoadingModal();
});
e.preventDefault();
return false;
}
else if (this.bulkModifyMethod === 'delete') {
@ -194,12 +196,13 @@ function EditPhotosViewModel(album_id, language, urls) {
callback: function () {
self.isSubmitting = true;
$('button[name="bulk-apply"]', bulk_form).click();
//_bt_showLoadingModal();
_bt_showLoadingModal();
}
}
}
});
e.preventDefault();
return false;
}

View File

@ -11,6 +11,8 @@ return [
'album_no_photos_p1' => 'No photos in this album',
'album_no_photos_p2' => 'Click the "Upload photos" button below to get started.',
'album_no_photos_button' => 'Upload photos',
'album_permissions_album' => 'Album Permissions',
'album_permissions_photo' => 'Photo Permissions',
'album_photos_tab' => 'Photos',
'album_saved_successfully' => 'The ":name" album was updated successfully.',
'album_security_tab' => 'Permissions',

View File

@ -9,13 +9,12 @@ return [
'manage-users' => 'Manage users'
],
'album' => [
'change-photo-metadata' => 'Change metadata of photos in this album',
'delete' => 'Delete this album',
'delete-other-photos' => 'Delete photos owned by other users',
'delete-own-photos' => 'Delete user\'s own photos',
'delete-photos' => 'Delete photos from this album',
'edit' => 'Manage this album',
'edit-other-photos' => 'Edit photos owned by other users',
'edit-own-photos' => 'Edit user\'s own photos',
'list' => 'See this album in listings',
'manipulate-photos' => 'Manipulate photos in this album',
'upload-photos' => 'Upload photos into this album',
'view' => 'Access this album'
]

View File

@ -2,50 +2,53 @@
@section('title', 'Gallery Admin')
@section('breadcrumb')
<div class="breadcrumb">
<div class="container">
<ol class="breadcrumb">
<li><a href="{{ route('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
<li><a href="{{ route('admin') }}">@lang('navigation.breadcrumb.admin')</a></li>
<li><a href="{{ route('albums.index') }}">@lang('navigation.breadcrumb.albums')</a></li>
<li><a href="{{ route('albums.show', ['id' => $album->id]) }}">{{ $album->name }}</a></li>
<li class="active">@lang('navigation.breadcrumb.edit_album')</li>
</ol>
</div>
</div>
<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.edit_album')</li>
@endsection
@section('content')
<div class="container">
<div class="row">
<div class="col-xs-12">
<div class="col">
<h1>@lang('admin.edit_album', ['album_name' => $album->name])</h1>
<p>@lang('admin.edit_album_intro')</p>
<p>@lang('admin.edit_album_intro2', ['album_name' => $album->name])</p>
<hr/>
{!! Form::model($album, ['route' => ['albums.update', $album->id], 'method' => 'PUT']) !!}
<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
{!! Form::label('name', trans('forms.name_label'), ['class' => 'control-label']) !!}
{!! Form::text('name', old('name'), ['class' => 'form-control']) !!}
<form method="post" action="{{ route('albums.update', [$album->id]) }}">
{{ csrf_field() }}
{{ method_field('PUT') }}
@if ($errors->has('name'))
<span class="help-block">
<strong>{{ $errors->first('name') }}</strong>
</span>
@endif
</div>
<div class="form-group{{ $errors->has('name') ? ' has-danger' : '' }}">
<label for="album-name" class="control-label">@lang('forms.name_label')</label>
<input type="text" id="album-name" class="form-control" name="name" value="{{ old('name') }}" />
<div class="form-group">
{!! Form::label('description', trans('forms.description_label'), ['class' => 'control-label']) !!}
{!! Form::textarea('description', old('description'), ['class' => 'form-control']) !!}
</div>
@if ($errors->has('name'))
<div class="form-control-feedback">
<strong>{{ $errors->first('name') }}</strong>
</div>
@endif
</div>
<div class="form-actions">
<a href="{{ route('albums.show', ['id' => $album->id]) }}" class="btn btn-default">@lang('forms.cancel_action')</a>
<button type="submit" class="btn btn-success"><i class="fa fa-fw fa-check"></i> @lang('forms.save_action')</button>
</div>
{!! Form::close() !!}
<div class="form-group{{ $errors->has('description') ? ' has-danger' : '' }}">
<label for="album-description" class="control-label" name="name">@lang('forms.description_label')</label>
<textarea class="form-control" id="album-description" rows="5" name="description">{{ old('description') }}</textarea>
@if ($errors->has('description'))
<div class="form-control-feedback">
<strong>{{ $errors->first('description') }}</strong>
</div>
@endif
</div>
<div class="text-right">
<a href="{{ route('albums.show', ['id' => $album->id]) }}" class="btn btn-link">@lang('forms.cancel_action')</a>
<button type="submit" class="btn btn-success"><i class="fa fa-fw fa-check"></i> @lang('forms.save_action')</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -42,9 +42,14 @@
<p style="margin-bottom: 0;"><b>{{ $album->photos_count }}</b> {{ trans_choice('admin.stats_widget.photos', $album->photos_count) }}</p>
</td>
<td class="text-right">
@can('delete', $album)
<a href="{{ route('albums.delete', ['id' => $album->id]) }}" class="btn btn-danger">@lang('forms.delete_action')</a>
@endcan
<div class="btn-group">
@can('edit', $album)
<a href="{{ route('albums.edit', ['id' => $album->id]) }}" class="btn btn-secondary">@lang('forms.edit_action')</a>
@endcan
@can('delete', $album)
<a href="{{ route('albums.delete', ['id' => $album->id]) }}" class="btn btn-danger">@lang('forms.delete_action')</a>
@endcan
</div>
</td>
</tr>
@endforeach

View File

@ -21,177 +21,28 @@
<div>
<ul class="nav nav-tabs" role="tablist">
@include(Theme::viewName('partials.tab'), ['tab_name' => 'photos', 'tab_icon' => 'photo', 'tab_text' => trans('admin.album_photos_tab')])
@include(Theme::viewName('partials.tab'), ['tab_name' => 'upload', 'tab_icon' => 'upload', 'tab_text' => trans('admin.album_upload_tab')])
@include(Theme::viewName('partials.tab'), ['tab_name' => 'permissions', 'tab_icon' => 'lock', 'tab_text' => trans('admin.album_security_tab')])
@can('upload-photos', $album)
@include(Theme::viewName('partials.tab'), ['tab_name' => 'upload', 'tab_icon' => 'upload', 'tab_text' => trans('admin.album_upload_tab')])
@endcan
@can('change-permissions', $album)
@include(Theme::viewName('partials.tab'), ['tab_name' => 'permissions', 'tab_icon' => 'lock', 'tab_text' => trans('admin.album_security_tab')])
@endcan
@include(Theme::viewName('partials.tab'), ['tab_name' => 'settings', 'tab_icon' => 'cog', 'tab_text' => trans('admin.album_settings_tab')])
</ul>
<div class="tab-content">
{{-- Photos --}}
@include(Theme::viewName('partials.album_photos_tab'))
{{-- Upload --}}
@include(Theme::viewName('partials.album_upload_tab'))
{{-- Permissions --}}
<div role="tabpanel" class="tab-pane{{ $active_tab == 'permissions' ? ' active' : '' }}" id="permissions-tab">
<h4>@lang('admin.security_heading')</h4>
<p>@lang('admin.security_text')</p>
<hr/>
<h5 style="font-weight: bold;">@lang('admin.security_groups_heading')</h5>
<form action="{{ route('albums.set_group_permissions', ['id' => $album->id]) }}" method="post">
{{ csrf_field() }}
@if (count($existing_groups) > 0)
<div id="groups-accordion" role="tablist" aria-multiselectable="true">
@foreach ($existing_groups as $group)
@include(Theme::viewName('partials.album_permissions'), [
'key_id' => 'group_' . $group->id,
'object_id' => $group->id,
'title' => $group->name,
'callback' => [$album, 'doesGroupHavePermission'],
'callback_object' => $group,
'parent_id' => 'groups-accordion'
])
@endforeach
</div>
@endif
<div class="row mt-3">
<div class="col-md-4">
<select class="form-control" name="group_id" style="margin-bottom: 2px;"@if (count($add_new_groups) == 0) disabled="disabled"@endif>
@foreach ($add_new_groups as $group)
<option value="{{ $group->id }}">{{ $group->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-2">
<button type="submit" name="action" value="add_group" class="btn btn-primary">Assign Permissions</button>
</div>
<div class="col-md-6 text-right">
<button type="submit" name="action" value="update_group_permissions" class="btn btn-success">
<i class="fa fa-fw fa-check"></i> @lang('forms.save_action')
</button>
</div>
</div>
</form>
<hr/>
<h5 style="font-weight: bold;">@lang('admin.security_users_heading')</h5>
<form action="{{ route('albums.set_user_permissions', ['id' => $album->id]) }}" method="post">
{{ csrf_field() }}
<div id="users-accordion" role="tablist" aria-multiselectable="true">
{{-- Anonymous users --}}
@include(Theme::viewName('partials.album_permissions'), [
'key_id' => 'anonymous',
'object_id' => 'anonymous',
'title' => trans('admin.anonymous_users'),
'callback' => [$album, 'doesUserHavePermission'],
'callback_object' => null,
'parent_id' => 'users-accordion'
])
@foreach ($existing_users as $user)
@include(Theme::viewName('partials.album_permissions'), [
'key_id' => 'user_' . $user->id,
'object_id' => $user->id,
'title' => $user->name,
'callback' => [$album, 'doesUserHavePermission'],
'callback_object' => $user,
'parent_id' => 'users-accordion'
])
@endforeach
</div>
<div class="row mt-3">
<div class="col-md-4">
<input class="form-control" name="user_name" id="user-search-textbox" size="20" style="margin-bottom: 2px;" />
<input type="hidden" name="user_id" id="user-id-field" />
</div>
<div class="col-md-2">
<button type="submit" name="action" value="add_user" class="btn btn-primary">Assign Permissions</button>
</div>
<div class="col-md-6 text-right">
<button type="submit" name="action" value="update_user_permissions" class="btn btn-success">
<i class="fa fa-fw fa-check"></i> @lang('forms.save_action')
</button>
</div>
</div>
</form>
</div>
@can('upload-photos', $album)
{{-- Upload --}}
@include(Theme::viewName('partials.album_upload_tab'))
@endcan
@can('change-permissions', $album)
{{-- Permissions --}}
@include(Theme::viewName('partials.album_permissions_tab'))
@endcan
{{-- Settings --}}
<div role="tabpanel" class="tab-pane{{ $active_tab == 'settings' ? ' active' : '' }}" id="settings-tab">
<form action="{{ route('albums.update', [$album->id]) }}" method="POST">
{{ csrf_field() }}
{{ method_field('PUT') }}
<h4><i class="fa fa-fw fa-info"></i> @lang('admin.album_basic_info_heading')</h4>
<p>@lang('admin.album_basic_info_intro')</p>
<div class="form-group{{ $errors->has('name') ? ' has-danger' : '' }}" style="margin-top: 20px;">
<label class="form-control-label" for="album-name">@lang('forms.name_label')</label>
<input type="text" class="form-control" id="album-name" name="name" value="{{ old('name') }}">
@if ($errors->has('name'))
<div class="form-control-feedback">
<strong>{{ $errors->first('name') }}</strong>
</div>
@endif
</div>
<div class="form-group">
<label class="form-control-label" for="album-description">@lang('forms.description_label')</label>
<textarea class="form-control" id="album-description" name="description">{{ old('description') }}</textarea>
</div>
<hr/>
<h4><i class="fa fa-fw fa-paint-brush"></i> @lang('admin.album_appearance_heading')</h4>
<p>@lang('admin.album_appearance_intro')</p>
<div class="form-group" style="margin-top: 20px;">
<label class="control-label" for="album-view">@lang('forms.default_album_view_label')</label>
<select class="form-control" name="default_view">
@foreach ($allowed_views as $view)
<option value="{{ $view }}"{{ $view == old('default_view') ? ' selected="selected"' : '' }}>{{ $view }}</option>
@endforeach
</select>
</div>
<hr/>
<div class="row">
<div class="col-sm-6 push-sm-6">
<div class="card">
<div class="card-header">@lang('admin.save_changes_heading')</div>
<div class="card-block">
<p>@lang('admin.save_changes_intro')</p>
<div class="text-right">
<button type="submit" class="btn btn-success"><i class="fa fa-fw fa-floppy-o"></i> @lang('forms.save_action')</button>
</div>
</div>
</div>
</div>
<div class="col-sm-6 pull-sm-6">
<div class="card card-outline-danger">
<div class="card-header card-danger">@lang('admin.danger_zone_heading')</div>
<div class="card-block">
<p class="text-danger">@lang('admin.danger_zone_intro')</p>
<div>
<a href="{{ route('albums.delete', ['id' => $album->id]) }}" class="btn btn-danger">@lang('forms.delete_action')</a>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
@include(Theme::viewName('partials.album_settings_tab'))
</div>
</div>
</div>

View File

@ -9,20 +9,29 @@
<p style="margin-bottom: 20px;"><a class="select-all" href="#">Select All</a> &middot; <a class="select-none" href="">Select None</a></p>
<div class="row">
<div class="col-md-4">
<h5>Album Permissions</h5>
<div class="col-md-4 mb-3">
<h5>@lang('admin.album_permissions_album')</h5>
@include(Theme::viewName('partials.permission_checkbox'), ['permission' => Theme::getPermission($all_permissions, 'album', 'list')])
@include(Theme::viewName('partials.permission_checkbox'), ['permission' => Theme::getPermission($all_permissions, 'album', 'view')])
@if ($object_id != 'anonymous')
@include(Theme::viewName('partials.permission_checkbox'), ['permission' => Theme::getPermission($all_permissions, 'album', 'edit')])
@include(Theme::viewName('partials.permission_checkbox'), ['permission' => Theme::getPermission($all_permissions, 'album', 'delete')])
@endif
</div>
</div>
@foreach ($all_permissions as $permission)
@if ($object_id == 'anonymous' && $permission->section == 'album' && $permission->description != 'list' && $permission->description != 'view')
@continue
@if ($object_id != 'anonymous')
<div class="col-md-4 mb-3">
<h5>@lang('admin.album_permissions_photo')</h5>
@include(Theme::viewName('partials.permission_checkbox'), ['permission' => Theme::getPermission($all_permissions, 'album', 'upload-photos')])
@include(Theme::viewName('partials.permission_checkbox'), ['permission' => Theme::getPermission($all_permissions, 'album', 'manipulate-photos')])
@include(Theme::viewName('partials.permission_checkbox'), ['permission' => Theme::getPermission($all_permissions, 'album', 'change-photo-metadata')])
@include(Theme::viewName('partials.permission_checkbox'), ['permission' => Theme::getPermission($all_permissions, 'album', 'delete-photos')])
</div>
@endif
@endforeach
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,89 @@
<div role="tabpanel" class="tab-pane{{ $active_tab == 'permissions' ? ' active' : '' }}" id="permissions-tab">
<h4>@lang('admin.security_heading')</h4>
<p>@lang('admin.security_text')</p>
<hr/>
<h5 style="font-weight: bold;">@lang('admin.security_groups_heading')</h5>
<form action="{{ route('albums.set_group_permissions', ['id' => $album->id]) }}" method="post">
{{ csrf_field() }}
@if (count($existing_groups) > 0)
<div id="groups-accordion" role="tablist" aria-multiselectable="true">
@foreach ($existing_groups as $group)
@include(Theme::viewName('partials.album_permissions'), [
'key_id' => 'group_' . $group->id,
'object_id' => $group->id,
'title' => $group->name,
'callback' => [$album, 'doesGroupHavePermission'],
'callback_object' => $group,
'parent_id' => 'groups-accordion'
])
@endforeach
</div>
@endif
<div class="row mt-3">
<div class="col-md-4">
<select class="form-control" name="group_id" style="margin-bottom: 2px;"@if (count($add_new_groups) == 0) disabled="disabled"@endif>
@foreach ($add_new_groups as $group)
<option value="{{ $group->id }}">{{ $group->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-2">
<button type="submit" name="action" value="add_group" class="btn btn-primary">Assign Permissions</button>
</div>
<div class="col-md-6 text-right">
<button type="submit" name="action" value="update_group_permissions" class="btn btn-success">
<i class="fa fa-fw fa-check"></i> @lang('forms.save_action')
</button>
</div>
</div>
</form>
<hr/>
<h5 style="font-weight: bold;">@lang('admin.security_users_heading')</h5>
<form action="{{ route('albums.set_user_permissions', ['id' => $album->id]) }}" method="post">
{{ csrf_field() }}
<div id="users-accordion" role="tablist" aria-multiselectable="true">
{{-- Anonymous users --}}
@include(Theme::viewName('partials.album_permissions'), [
'key_id' => 'anonymous',
'object_id' => 'anonymous',
'title' => trans('admin.anonymous_users'),
'callback' => [$album, 'doesUserHavePermission'],
'callback_object' => null,
'parent_id' => 'users-accordion'
])
@foreach ($existing_users as $user)
@include(Theme::viewName('partials.album_permissions'), [
'key_id' => 'user_' . $user->id,
'object_id' => $user->id,
'title' => $user->name,
'callback' => [$album, 'doesUserHavePermission'],
'callback_object' => $user,
'parent_id' => 'users-accordion'
])
@endforeach
</div>
<div class="row mt-3">
<div class="col-md-4">
<input class="form-control" name="user_name" id="user-search-textbox" size="20" style="margin-bottom: 2px;" />
<input type="hidden" name="user_id" id="user-id-field" />
</div>
<div class="col-md-2">
<button type="submit" name="action" value="add_user" class="btn btn-primary">Assign Permissions</button>
</div>
<div class="col-md-6 text-right">
<button type="submit" name="action" value="update_user_permissions" class="btn btn-success">
<i class="fa fa-fw fa-check"></i> @lang('forms.save_action')
</button>
</div>
</div>
</form>
</div>

View File

@ -14,29 +14,31 @@
@include (Theme::viewName('partials.single_photo_admin'))
@endforeach
<div class="pull-left" style="margin-bottom: 15px;">
<p style="margin-bottom: 15px;">
<button v-on:click="selectAll" type="button" class="btn btn-secondary">@lang('admin.select_all_action')</button>
<button v-on:click="selectNone" type="button" class="btn btn-secondary">@lang('admin.select_none_action')</button>
</p>
<input v-model="selectAllInAlbum" type="hidden" name="select-all-album"/>
<div class="row">
<div class="col-md-6 mb-3">
<p style="margin-bottom: 15px;">
<button v-on:click="selectAll" type="button" class="btn btn-secondary">@lang('admin.select_all_action')</button>
<button v-on:click="selectNone" type="button" class="btn btn-secondary">@lang('admin.select_none_action')</button>
</p>
<input v-model="selectAllInAlbum" type="hidden" name="select-all-album"/>
<div v-if="selectAllInAlbum" class="alert alert-warning">
@lang('admin.select_all_album_active')
<div v-if="selectAllInAlbum" class="alert alert-warning">
@lang('admin.select_all_album_active')
</div>
<p><label class="control-label" for="bulk-action">@lang('forms.bulk_edit_photos_label')</label></p>
<input type="hidden" name="new-album-id" value="{{ $album->id }}"/>
<select name="bulk-action" v-model="bulkModifyMethod">
<option>@lang('forms.bulk_edit_photos_placeholder')</option>
@foreach ($bulk_actions as $key => $value)
<option value="{{ $key }}"{{ $key == old('bulk-action') ? ' selected="selected"' : '' }}>{{ $value }}</option>
@endforeach
</select>
<button type="submit" class="btn btn-sm btn-primary" name="bulk-apply" value="clicked" v-on:click="bulkModifySelected">@lang('forms.apply_action')</button>
</div>
<div class="col-md-6 text-right">
<button type="submit" class="btn btn-success">@lang('forms.save_action')</button>
</div>
<p><label class="control-label" for="bulk-action">@lang('forms.bulk_edit_photos_label')</label></p>
<input type="hidden" name="new-album-id" value="{{ $album->id }}"/>
<select name="bulk-action" data-bind="value: bulkModifyMethod, enable: photoIDs().length > 0">
<option>@lang('forms.bulk_edit_photos_placeholder')</option>
@foreach ($bulk_actions as $key => $value)
<option value="{{ $key }}"{{ $key == old('bulk-action') ? ' selected="selected"' : '' }}>{{ $value }}</option>
@endforeach
</select>
<button type="submit" class="btn btn-sm btn-primary" name="bulk-apply" value="clicked" data-bind="click: bulkModifySelected, enable: photoIDs().length > 0">@lang('forms.apply_action')</button>
</div>
<div class="text-right">
<button type="submit" class="btn btn-success">@lang('forms.save_action')</button>
</div>
</form>

View File

@ -0,0 +1,69 @@
<div role="tabpanel" class="tab-pane{{ $active_tab == 'settings' ? ' active' : '' }}" id="settings-tab">
<form action="{{ route('albums.update', [$album->id]) }}" method="POST">
{{ csrf_field() }}
{{ method_field('PUT') }}
<h4><i class="fa fa-fw fa-info"></i> @lang('admin.album_basic_info_heading')</h4>
<p>@lang('admin.album_basic_info_intro')</p>
<div class="form-group{{ $errors->has('name') ? ' has-danger' : '' }}" style="margin-top: 20px;">
<label class="form-control-label" for="album-name">@lang('forms.name_label')</label>
<input type="text" class="form-control" id="album-name" name="name" value="{{ old('name') }}">
@if ($errors->has('name'))
<div class="form-control-feedback">
<strong>{{ $errors->first('name') }}</strong>
</div>
@endif
</div>
<div class="form-group">
<label class="form-control-label" for="album-description">@lang('forms.description_label')</label>
<textarea class="form-control" id="album-description" name="description" rows="5">{{ old('description') }}</textarea>
</div>
<hr/>
<h4><i class="fa fa-fw fa-paint-brush"></i> @lang('admin.album_appearance_heading')</h4>
<p>@lang('admin.album_appearance_intro')</p>
<div class="form-group" style="margin-top: 20px;">
<label class="control-label" for="album-view">@lang('forms.default_album_view_label')</label>
<select class="form-control" name="default_view">
@foreach ($allowed_views as $view)
<option value="{{ $view }}"{{ $view == old('default_view') ? ' selected="selected"' : '' }}>{{ $view }}</option>
@endforeach
</select>
</div>
<hr/>
<div class="row">
<div class="col-md-6 push-md-6 mb-3">
<div class="card">
<div class="card-header">@lang('admin.save_changes_heading')</div>
<div class="card-block">
<p>@lang('admin.save_changes_intro')</p>
<div class="text-right">
<button type="submit" class="btn btn-success"><i class="fa fa-fw fa-floppy-o"></i> @lang('forms.save_action')</button>
</div>
</div>
</div>
</div>
@can('delete', $album)
<div class="col-md-6 pull-md-6 mb-3">
<div class="card card-outline-danger">
<div class="card-header card-danger">@lang('admin.danger_zone_heading')</div>
<div class="card-block">
<p class="text-danger">@lang('admin.danger_zone_intro')</p>
<div>
<a href="{{ route('albums.delete', ['id' => $album->id]) }}" class="btn btn-danger">@lang('forms.delete_action')</a>
</div>
</div>
</div>
</div>
@endcan
</div>
</form>
</div>

View File

@ -40,7 +40,7 @@
{{ Auth::user()->name }}
{{--<img class="avatar" src="{{ Theme::gravatarUrl(Auth::user()->email, 42) }}" alt="{{ Auth::user()->name }}" title="{{ Auth::user()->name }}" />--}}
</a>
<div class="dropdown-menu">
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="{{ route('auth.changePassword') }}">@lang('navigation.navbar.change_password')</a>
<a class="dropdown-item" href="{{ url('/logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">@lang('navigation.navbar.logout')</a>
<form id="logout-form" action="{{ url('/logout') }}" method="POST" style="display: none;">

View File

@ -9,44 +9,55 @@
</a><br/>
{{-- Photo editing tasks - these are hooked up using Javascript in admin/show_album --}}
<div class="text-center mt-1">
<div class="btn-group btn-group-sm" role="group">
@php($canChangeMetadata = Auth::user()->can('change-metadata', $photo))
@php($canManipulate = Auth::user()->can('manipulate', $photo))
@php($canDelete = Auth::user()->can('delete', $photo))
@if ($canManipulate || $canChangeMetadata || $canDelete)
<div class="text-center mt-1">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-secondary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-fw fa-pencil"></i> <span class="caret"></span>
</button>
<div class="dropdown-menu">
<a href="#" class="dropdown-item rotate-photo-left" v-on:click="rotateLeft"><i class="fa fa-fw fa-rotate-left"></i> @lang('admin.photo_actions.rotate_left')</a>
<a href="#" class="dropdown-item rotate-photo-right" v-on:click="rotateRight"><i class="fa fa-fw fa-rotate-right"></i> @lang('admin.photo_actions.rotate_right')</a>
<div class="dropdown-divider"></div>
<a href="#" class="dropdown-item flip-photo-horizontal" v-on:click="flipHorizontal"><i class="fa fa-fw fa-arrows-h"></i> @lang('admin.photo_actions.flip_horizontal')</a>
<a href="#" class="dropdown-item flip-photo-vertical" v-on:click="flipVertical"><i class="fa fa-fw fa-arrows-v"></i> @lang('admin.photo_actions.flip_vertical')</a>
<a href="#" class="dropdown-item flip-photo-both" v-on:click="flipBoth"><i class="fa fa-fw fa-retweet"></i> @lang('admin.photo_actions.flip_both')</a>
@if ($canManipulate)
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-secondary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-fw fa-pencil"></i> <span class="caret"></span>
</button>
<div class="dropdown-menu">
<a href="#" class="dropdown-item rotate-photo-left" v-on:click="rotateLeft"><i class="fa fa-fw fa-rotate-left"></i> @lang('admin.photo_actions.rotate_left')</a>
<a href="#" class="dropdown-item rotate-photo-right" v-on:click="rotateRight"><i class="fa fa-fw fa-rotate-right"></i> @lang('admin.photo_actions.rotate_right')</a>
<div class="dropdown-divider"></div>
<a href="#" class="dropdown-item flip-photo-horizontal" v-on:click="flipHorizontal"><i class="fa fa-fw fa-arrows-h"></i> @lang('admin.photo_actions.flip_horizontal')</a>
<a href="#" class="dropdown-item flip-photo-vertical" v-on:click="flipVertical"><i class="fa fa-fw fa-arrows-v"></i> @lang('admin.photo_actions.flip_vertical')</a>
<a href="#" class="dropdown-item flip-photo-both" v-on:click="flipBoth"><i class="fa fa-fw fa-retweet"></i> @lang('admin.photo_actions.flip_both')</a>
</div>
</div>
@endif
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-fw fa-cog"></i> <span class="caret"></span>
</button>
<div class="dropdown-menu">
@if ($canChangeMetadata)
<a href="#" class="dropdown-item change-album" v-on:click="changeAlbum"><i class="fa fa-fw fa-share"></i> @lang('admin.photo_actions.change_album')</a>
<a href="#" class="dropdown-item regenerate-thumbnails" v-on:click="regenerateThumbnails"><i class="fa fa-fw fa-picture-o"></i> @lang('admin.photo_actions.refresh_thumbnails')</a>
@endif
@if ($canDelete)
<a href="#" class="dropdown-item delete-photo" v-on:click="deletePhoto"><i class="fa fa-fw fa-trash text-danger"></i> <span class="text-danger">@lang('admin.photo_actions.delete')</span></a>
@endif
</div>
</div>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-fw fa-cog"></i> <span class="caret"></span>
</button>
<div class="dropdown-menu">
<a href="#" class="dropdown-item change-album" v-on:click="changeAlbum"><i class="fa fa-fw fa-share"></i> @lang('admin.photo_actions.change_album')</a>
<a href="#" class="dropdown-item regenerate-thumbnails" v-on:click="regenerateThumbnails"><i class="fa fa-fw fa-picture-o"></i> @lang('admin.photo_actions.refresh_thumbnails')</a>
<a href="#" class="dropdown-item delete-photo" v-on:click="deletePhoto"><i class="fa fa-fw fa-trash text-danger"></i> <span class="text-danger">@lang('admin.photo_actions.delete')</span></a>
</div>
<div class="mt-2">
<input type="checkbox" id="select-photo-{{ $photo->id }}" name="select-photo[]" value="{{ $photo->id }}" v-bind:checked="isPhotoSelected({{ $photo->id }})" /> <label for="select-photo-{{ $photo->id }}">@lang('forms.select')</label>
</div>
</div>
<div class="mt-2">
<input type="checkbox" id="select-photo-{{ $photo->id }}" name="select-photo[]" value="{{ $photo->id }}" v-bind:checked="isPhotoSelected({{ $photo->id }})" /> <label for="select-photo-{{ $photo->id }}">@lang('forms.select')</label>
</div>
</div>
@endif
</div>
<div class="col-xs-12 col-sm-10">
@php($validation_field_name = ('photo.' . $photo->id . '.name'))
<div class="form-group{{ $errors->has($validation_field_name) ? ' has-error' : '' }}">
<label class="control-label" name="{{ $field_prefix }}[name]">@lang('forms.name_label')</label>
<input class="form-control" type="text" name="{{ $field_prefix }}[name]" value="{{ old('name', $photo->name) }}"/>
<input class="form-control" type="text" name="{{ $field_prefix }}[name]" value="{{ old($field_prefix . '[name]', $photo->name) }}" @cannot('change-metadata', $photo) disabled="disabled"@endcannot/>
@if ($errors->has($validation_field_name))
<span class="help-block">
@ -57,7 +68,7 @@
<div class="form-group">
<label class="control-label" name="{{ $field_prefix }}[description]">@lang('forms.description_label')</label>
<textarea name="{{ $field_prefix }}[description]" class="form-control" rows="4">{{ old('name', $photo->description) }}</textarea>
<textarea name="{{ $field_prefix }}[description]" class="form-control" rows="4" @cannot('change-metadata', $photo) disabled="disabled"@endcannot>{{ old($field_prefix . '[description]', $photo->description) }}</textarea>
</div>
</div>
</div>