#4: Added a framework for manipulating data during system updates. Full URL path to an album is now saved in the database. Fall-back routes are now mapped by the first segment - a = album, p = photo, i = image
This commit is contained in:
parent
e0773ba236
commit
a26f9c1c1f
@ -4,6 +4,7 @@ namespace App;
|
|||||||
|
|
||||||
use App\AlbumSources\IAlbumSource;
|
use App\AlbumSources\IAlbumSource;
|
||||||
use App\AlbumSources\LocalFilesystemSource;
|
use App\AlbumSources\LocalFilesystemSource;
|
||||||
|
use App\Helpers\MiscHelper;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
@ -19,7 +20,7 @@ class Album extends Model
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name', 'description', 'url_alias', 'is_private', 'user_id', 'storage_id', 'default_view', 'parent_album_id'
|
'name', 'description', 'url_alias', 'is_private', 'user_id', 'storage_id', 'default_view', 'parent_album_id', 'url_path'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,6 +31,26 @@ class Album extends Model
|
|||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of parent items
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function albumParentTree()
|
||||||
|
{
|
||||||
|
$albums = [];
|
||||||
|
$current = $this;
|
||||||
|
|
||||||
|
while (!is_null($current))
|
||||||
|
{
|
||||||
|
$albums[] = $current;
|
||||||
|
|
||||||
|
$current = $current->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
$albums = array_reverse($albums);
|
||||||
|
return $albums;
|
||||||
|
}
|
||||||
|
|
||||||
public function anonymousPermissions()
|
public function anonymousPermissions()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Permission::class, 'album_anonymous_permissions');
|
return $this->belongsToMany(Permission::class, 'album_anonymous_permissions');
|
||||||
@ -66,7 +87,23 @@ class Album extends Model
|
|||||||
|
|
||||||
public function generateAlias()
|
public function generateAlias()
|
||||||
{
|
{
|
||||||
$this->url_alias = ucfirst(preg_replace('/[^a-z0-9\-]/', '-', strtolower($this->name)));
|
$this->url_alias = MiscHelper::capitaliseWord(preg_replace('/[^a-z0-9\-]/', '-', strtolower($this->name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateUrlPath()
|
||||||
|
{
|
||||||
|
$parts = [];
|
||||||
|
$current = $this;
|
||||||
|
|
||||||
|
while (!is_null($current))
|
||||||
|
{
|
||||||
|
$parts[] = $current->url_alias;
|
||||||
|
|
||||||
|
$current = $current->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = array_reverse($parts);
|
||||||
|
$this->url_path = join('/', $parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,19 +183,12 @@ class Album extends Model
|
|||||||
|
|
||||||
public function url()
|
public function url()
|
||||||
{
|
{
|
||||||
$parts = [];
|
if (is_null($this->url_path))
|
||||||
$current = $this;
|
|
||||||
|
|
||||||
while (!is_null($current))
|
|
||||||
{
|
{
|
||||||
$parts[] = $current->url_alias;
|
$this->generateUrlPath();
|
||||||
|
|
||||||
$current = $current->parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$parts = array_reverse($parts);
|
return route('viewAlbum', $this->url_path);
|
||||||
|
|
||||||
return route('viewAlbum', join('/', $parts));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userPermissions()
|
public function userPermissions()
|
||||||
|
@ -62,7 +62,7 @@ class LocalFilesystemSource extends AlbumSourceBase implements IAlbumSource
|
|||||||
public function getUrlToPhoto(Photo $photo, $thumbnail = null)
|
public function getUrlToPhoto(Photo $photo, $thumbnail = null)
|
||||||
{
|
{
|
||||||
$photoUrl = route('downloadPhoto', [
|
$photoUrl = route('downloadPhoto', [
|
||||||
'albumUrlAlias' => $this->album->url_alias,
|
'albumUrlAlias' => $this->album->url_path,
|
||||||
'photoFilename' => $photo->storage_file_name
|
'photoFilename' => $photo->storage_file_name
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
8
app/DataMigration.php
Normal file
8
app/DataMigration.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
abstract class DataMigration
|
||||||
|
{
|
||||||
|
abstract function run($currentVersion);
|
||||||
|
}
|
@ -72,8 +72,8 @@ class DbHelper
|
|||||||
->withCount('photos');
|
->withCount('photos');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getAlbumByAlias($urlAlias)
|
public static function getAlbumByPath($urlPath)
|
||||||
{
|
{
|
||||||
return Album::where('url_alias', $urlAlias)->first();
|
return Album::where('url_path', $urlPath)->first();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,21 @@ namespace App\Helpers;
|
|||||||
|
|
||||||
class MiscHelper
|
class MiscHelper
|
||||||
{
|
{
|
||||||
|
public static function capitaliseWord($word)
|
||||||
|
{
|
||||||
|
$word = strtolower($word);
|
||||||
|
|
||||||
|
$wordLetters = [];
|
||||||
|
preg_match_all('/\b[a-z]/i', $word, $wordLetters, PREG_OFFSET_CAPTURE);
|
||||||
|
|
||||||
|
foreach ($wordLetters[0] as $wordLetter)
|
||||||
|
{
|
||||||
|
$word = substr_replace($word, strtoupper($wordLetter[0]), $wordLetter[1], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $word;
|
||||||
|
}
|
||||||
|
|
||||||
public static function convertToBytes($val)
|
public static function convertToBytes($val)
|
||||||
{
|
{
|
||||||
if(empty($val))return 0;
|
if(empty($val))return 0;
|
||||||
|
@ -18,7 +18,7 @@ class AlbumController extends Controller
|
|||||||
{
|
{
|
||||||
public function index(Request $request, $albumUrlAlias)
|
public function index(Request $request, $albumUrlAlias)
|
||||||
{
|
{
|
||||||
$album = DbHelper::getAlbumByAlias($albumUrlAlias);
|
$album = DbHelper::getAlbumByPath($albumUrlAlias);
|
||||||
if (is_null($album))
|
if (is_null($album))
|
||||||
{
|
{
|
||||||
App::abort(404);
|
App::abort(404);
|
||||||
|
@ -16,7 +16,7 @@ class DefaultController extends Controller
|
|||||||
{
|
{
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$albums = DbHelper::getAlbumsForCurrentUser();
|
$albums = DbHelper::getAlbumsForCurrentUser(0);
|
||||||
$resetStatus = $request->session()->get('status');
|
$resetStatus = $request->session()->get('status');
|
||||||
|
|
||||||
// Record the visit to the index (no album or photo to record a hit against though)
|
// Record the visit to the index (no album or photo to record a hit against though)
|
||||||
|
@ -22,7 +22,7 @@ class PhotoController extends Controller
|
|||||||
{
|
{
|
||||||
public function download(Request $request, $albumUrlAlias, $photoFilename)
|
public function download(Request $request, $albumUrlAlias, $photoFilename)
|
||||||
{
|
{
|
||||||
$album = DbHelper::getAlbumByAlias($albumUrlAlias);
|
$album = DbHelper::getAlbumByPath($albumUrlAlias);
|
||||||
if (is_null($album))
|
if (is_null($album))
|
||||||
{
|
{
|
||||||
App::abort(404);
|
App::abort(404);
|
||||||
@ -89,7 +89,7 @@ class PhotoController extends Controller
|
|||||||
|
|
||||||
public function show(Request $request, $albumUrlAlias, $photoFilename)
|
public function show(Request $request, $albumUrlAlias, $photoFilename)
|
||||||
{
|
{
|
||||||
$album = DbHelper::getAlbumByAlias($albumUrlAlias);
|
$album = DbHelper::getAlbumByPath($albumUrlAlias);
|
||||||
if (is_null($album))
|
if (is_null($album))
|
||||||
{
|
{
|
||||||
App::abort(404);
|
App::abort(404);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
use App\Configuration;
|
use App\Configuration;
|
||||||
|
use App\DataMigration;
|
||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Helpers\MiscHelper;
|
use App\Helpers\MiscHelper;
|
||||||
use Closure;
|
use Closure;
|
||||||
@ -96,6 +97,14 @@ class AppInstallation
|
|||||||
|
|
||||||
Artisan::call('cache:clear');
|
Artisan::call('cache:clear');
|
||||||
Artisan::call('migrate', ['--force' => true]);
|
Artisan::call('migrate', ['--force' => true]);
|
||||||
|
|
||||||
|
$className = sprintf('DataMigrationV%s', str_replace(['.', '-'], '_', $appVersionNumber));
|
||||||
|
if (class_exists($className))
|
||||||
|
{
|
||||||
|
/** @var DataMigration $upgradeClass */
|
||||||
|
$upgradeClass = new $className;
|
||||||
|
$upgradeClass->run($versionNumber);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$versionNumber->value = $appVersionNumber;
|
$versionNumber->value = $appVersionNumber;
|
||||||
|
22
app/ModelObservers/AlbumObserver.php
Normal file
22
app/ModelObservers/AlbumObserver.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\ModelObservers;
|
||||||
|
|
||||||
|
use App\Album;
|
||||||
|
|
||||||
|
class AlbumObserver
|
||||||
|
{
|
||||||
|
public function creating(Album $album)
|
||||||
|
{
|
||||||
|
// Re-generate the alias and path
|
||||||
|
$album->generateAlias();
|
||||||
|
$album->generateUrlPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updating(Album $album)
|
||||||
|
{
|
||||||
|
// Re-generate the alias and path
|
||||||
|
$album->generateAlias();
|
||||||
|
$album->generateUrlPath();
|
||||||
|
}
|
||||||
|
}
|
@ -65,7 +65,7 @@ class Photo extends Model
|
|||||||
public function url()
|
public function url()
|
||||||
{
|
{
|
||||||
return route('viewPhoto', [
|
return route('viewPhoto', [
|
||||||
'albumUrlAlias' => $this->album->url_alias,
|
'albumUrlAlias' => $this->album->url_path,
|
||||||
'photoFilename' => $this->storage_file_name
|
'photoFilename' => $this->storage_file_name
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Album;
|
||||||
use App\Helpers\ConfigHelper;
|
use App\Helpers\ConfigHelper;
|
||||||
use App\Helpers\ImageHelper;
|
use App\Helpers\ImageHelper;
|
||||||
use App\Helpers\ThemeHelper;
|
use App\Helpers\ThemeHelper;
|
||||||
use App\Helpers\ValidationHelper;
|
use App\Helpers\ValidationHelper;
|
||||||
|
use App\ModelObservers\AlbumObserver;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Mail\Mailer;
|
use Illuminate\Mail\Mailer;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
@ -38,6 +40,9 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
Validator::extend('is_dir', (ValidationHelper::class . '@directoryExists'));
|
Validator::extend('is_dir', (ValidationHelper::class . '@directoryExists'));
|
||||||
Validator::extend('dir_empty', (ValidationHelper::class . '@isDirectoryEmpty'));
|
Validator::extend('dir_empty', (ValidationHelper::class . '@isDirectoryEmpty'));
|
||||||
Validator::extend('is_writeable', (ValidationHelper::class . '@isPathWriteable'));
|
Validator::extend('is_writeable', (ValidationHelper::class . '@isPathWriteable'));
|
||||||
|
|
||||||
|
// Model observers
|
||||||
|
Album::observe(AlbumObserver::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
27
database/data_migrations/DataMigrationV2_0_0_alpha_1.php
Normal file
27
database/data_migrations/DataMigrationV2_0_0_alpha_1.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Album;
|
||||||
|
use App\DataMigration;
|
||||||
|
|
||||||
|
class DataMigrationV2_0_0_alpha_1 extends DataMigration
|
||||||
|
{
|
||||||
|
public function run($currentVersion)
|
||||||
|
{
|
||||||
|
$this->storeAlbumUrlPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures all albums have a full URL path stored.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function storeAlbumUrlPaths()
|
||||||
|
{
|
||||||
|
$albumsWithNoPath = Album::where('url_path', null)->get();
|
||||||
|
|
||||||
|
/** @var Album $album */
|
||||||
|
foreach ($albumsWithNoPath as $album)
|
||||||
|
{
|
||||||
|
$album->touch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,7 @@ class AddParentAlbumColumn extends Migration
|
|||||||
Schema::table('albums', function (Blueprint $table)
|
Schema::table('albums', function (Blueprint $table)
|
||||||
{
|
{
|
||||||
$table->unsignedInteger('parent_album_id')->nullable();
|
$table->unsignedInteger('parent_album_id')->nullable();
|
||||||
|
$table->string('url_path')->nullable();
|
||||||
|
|
||||||
$table->foreign('parent_album_id')
|
$table->foreign('parent_album_id')
|
||||||
->references('id')->on('albums')
|
->references('id')->on('albums')
|
||||||
@ -34,6 +35,7 @@ class AddParentAlbumColumn extends Migration
|
|||||||
{
|
{
|
||||||
$table->dropForeign('albums_parent_album_id_foreign');
|
$table->dropForeign('albums_parent_album_id_foreign');
|
||||||
$table->dropColumn('parent_album_id');
|
$table->dropColumn('parent_album_id');
|
||||||
|
$table->dropColumn('url_path');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<ol class="breadcrumb">
|
<ol class="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('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
|
||||||
<li class="breadcrumb-item active">{{ $album->name }}</li>
|
@include(Theme::viewName('partials.album_breadcrumb'))
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
@extends('themes.base.layout')
|
@extends('themes.base.layout')
|
||||||
@section('title', $album->name)
|
@section('title', $album->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 active">{{ $album->name }}</li>
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="{{ route('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
|
||||||
|
@include(Theme::viewName('partials.album_breadcrumb'))
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container album-container">
|
<div class="container album-container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8 offset-md-2 text-center">
|
<div class="col-md-8 offset-md-2 text-center">
|
||||||
|
@ -2,14 +2,8 @@
|
|||||||
@section('title', $album->name)
|
@section('title', $album->name)
|
||||||
|
|
||||||
@section('breadcrumb')
|
@section('breadcrumb')
|
||||||
<div class="breadcrumb">
|
<li class="breadcrumb-item"><a href="{{ route('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
|
||||||
<div class="container">
|
@include(Theme::viewName('partials.album_breadcrumb'))
|
||||||
<ol class="breadcrumb">
|
|
||||||
<li><a href="{{ route('home') }}">Gallery</a></li>
|
|
||||||
<li class="active">{{ $album->name }}</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
@foreach ($album->albumParentTree() as $parentAlbum)
|
||||||
|
@if ($parentAlbum->id == $album->id)
|
||||||
|
<li class="breadcrumb-item active">{{ $album->name }}</li>
|
||||||
|
@else
|
||||||
|
<li class="breadcrumb-item"><a href="{{ $parentAlbum->url() }}">{{ $parentAlbum->name }}</a></li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
@ -66,6 +66,12 @@ Route::get('/', 'Gallery\DefaultController@index')->name('home');
|
|||||||
Route::get('/activate/{token}', 'Auth\ActivateController@activate')->name('auth.activate');
|
Route::get('/activate/{token}', 'Auth\ActivateController@activate')->name('auth.activate');
|
||||||
Route::get('/password/change', 'Auth\ChangePasswordController@showChangePasswordForm')->name('auth.changePassword');
|
Route::get('/password/change', 'Auth\ChangePasswordController@showChangePasswordForm')->name('auth.changePassword');
|
||||||
Route::post('/password/change', 'Auth\ChangePasswordController@processChangePassword')->name('auth.processChangePassword');
|
Route::post('/password/change', 'Auth\ChangePasswordController@processChangePassword')->name('auth.processChangePassword');
|
||||||
Route::get('{albumUrlAlias}', 'Gallery\AlbumController@index')->name('viewAlbum');
|
Route::get('a/{albumUrlAlias}', 'Gallery\AlbumController@index')
|
||||||
Route::get('{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@show')->name('viewPhoto');
|
->name('viewAlbum')
|
||||||
Route::get('photo/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@download')->name('downloadPhoto');
|
->where('albumUrlAlias', '.*');
|
||||||
|
Route::get('p/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@show')
|
||||||
|
->name('viewPhoto')
|
||||||
|
->where('albumUrlAlias', '.*');
|
||||||
|
Route::get('i/{albumUrlAlias}/{photoFilename}', 'Gallery\PhotoController@download')
|
||||||
|
->name('downloadPhoto')
|
||||||
|
->where('albumUrlAlias', '.*');
|
Loading…
Reference in New Issue
Block a user