diff --git a/app/Album.php b/app/Album.php index e93c21f..e11e798 100644 --- a/app/Album.php +++ b/app/Album.php @@ -49,6 +49,28 @@ class Album extends Model public function getAlbumSource() { // TODO allow albums to specify different storage locations - e.g. Amazon S3, SFTP/FTP, OpenStack - return new LocalFilesystemSource(dirname(__DIR__) . '/storage/app'); + return new LocalFilesystemSource(dirname(__DIR__) . '/storage/app/albums'); + } + + public function photos() + { + return $this->hasMany(Photo::class); + } + + public function thumbnailUrl($thumbnailName) + { + $photo = Photo::where('album_id', $this->id)->first(); + + if (!is_null($photo)) + { + return $this->getAlbumSource()->getUrlToPhoto($this, $photo, $thumbnailName); + } + + return ''; + } + + public function url() + { + return sprintf('/%s', urlencode($this->url_alias)); } } \ No newline at end of file diff --git a/app/AlbumSources/IAlbumSource.php b/app/AlbumSources/IAlbumSource.php index 72b4df5..44a4825 100644 --- a/app/AlbumSources/IAlbumSource.php +++ b/app/AlbumSources/IAlbumSource.php @@ -8,15 +8,35 @@ use Symfony\Component\HttpFoundation\File\File; interface IAlbumSource { + function getOriginalsFolder(); + /** - * Gets the absolute path or URL to the given photo file. - * @param Album $album - * @param Photo $photo + * Gets the absolute path to the given photo file. + * @param Album $album Album containing the photo. + * @param Photo $photo Photo to get the path to. + * @param string $thumbnail Thumbnail to get the image to. * @return string */ - function getPathToPhoto(Album $album, Photo $photo); + function getPathToPhoto(Album $album, Photo $photo, $thumbnail); - function saveThumbnail(Album $album, Photo $photo, $thumbnailImageResource, $thumbnailInfo); + /** + * Gets the absolute URL to the given photo file. + * @param Album $album Album containing the photo. + * @param Photo $photo Photo to get the URL to. + * @param string $thumbnail Thumbnail to get the image to. + * @return string + */ + function getUrlToPhoto(Album $album, Photo $photo, $thumbnail); + + /** + * Saves a generated thumbnail to its permanent location. + * @param Album $album Album containing the photo. + * @param Photo $photo Photo the thumbnail relates to. + * @param string $thumbnailInfo Information about the thumbnail. + * @param string $tempFilename Filename containing the thumbnail. + * @return mixed + */ + function saveThumbnail(Album $album, Photo $photo, $thumbnailInfo, $tempFilename); /** * Saves an uploaded file to the container and returns the filename. diff --git a/app/AlbumSources/LocalFilesystemSource.php b/app/AlbumSources/LocalFilesystemSource.php index 7dcfd59..9984664 100644 --- a/app/AlbumSources/LocalFilesystemSource.php +++ b/app/AlbumSources/LocalFilesystemSource.php @@ -7,6 +7,10 @@ use App\Helpers\MiscHelper; use App\Photo; use Symfony\Component\HttpFoundation\File\File; +/** + * Driver for managing files on the local filesystem. + * @package App\AlbumSources + */ class LocalFilesystemSource implements IAlbumSource { private $parentFolder; @@ -16,18 +20,42 @@ class LocalFilesystemSource implements IAlbumSource $this->parentFolder = $parentFolder; } - public function getPathToPhoto(Album $album, Photo $photo) + public function getOriginalsFolder() { - return sprintf('%s/%s', $this->getPathToAlbum($album), $photo->file_name); + return '_originals'; } - public function saveThumbnail(Album $album, Photo $photo, $thumbnailImageResource, $thumbnailInfo) + public function getPathToPhoto(Album $album, Photo $photo, $thumbnail = null) { + if (is_null($thumbnail)) + { + $thumbnail = $this->getOriginalsFolder(); + } + + return sprintf('%s/%s/%s', $this->getPathToAlbum($album), $thumbnail, $photo->file_name); + } + + public function getUrlToPhoto(Album $album, Photo $photo, $thumbnail = null) + { + $photoUrl = sprintf('%s/%s', urlencode($album->url_alias), urlencode($photo->file_name)); + + if (!is_null($thumbnail)) + { + $photoUrl .= sprintf('?t=%s', urlencode($thumbnail)); + } + + return url($photoUrl); + } + + public function saveThumbnail(Album $album, Photo $photo, $thumbnailInfo, $tempFilename) + { + $fileInfo = new File($tempFilename); + $fileInfo->move(sprintf('%s/%s', $this->getPathToAlbum($album), $thumbnailInfo['name']), $photo->file_name); } public function saveUploadedPhoto(Album $album, File $uploadedFile) { - $tempFilename = sprintf('%s/photo_%s', $this->getPathToAlbum($album), MiscHelper::randomString(20)); + $tempFilename = sprintf('%s/%s/%s', $this->getPathToAlbum($album), $this->getOriginalsFolder(), MiscHelper::randomString(20)); $extension = $uploadedFile->guessExtension(); if (!is_null($extension)) @@ -41,6 +69,6 @@ class LocalFilesystemSource implements IAlbumSource private function getPathToAlbum(Album $album) { - return sprintf('%s/albums/%s', $this->parentFolder, $album->url_alias); + return sprintf('%s/%s', $this->parentFolder, $album->url_alias); } } \ No newline at end of file diff --git a/app/Console/Commands/ProcessUploadCommand.php b/app/Console/Commands/ProcessUploadCommand.php index b4b8053..8db4c2a 100644 --- a/app/Console/Commands/ProcessUploadCommand.php +++ b/app/Console/Commands/ProcessUploadCommand.php @@ -117,19 +117,16 @@ class ProcessUploadCommand extends Command $photo->camera_software = $this->metadataCameraSoftware($exifData); $photo->save(); - // Generate thumbnails + // Generate and save thumbnails $this->output->writeln('Generating thumbnails'); $themeInfo = $this->themeHelper->info(); $thumbnailsRequired = $themeInfo['thumbnails']; + /** @var mixed $thumbnail */ foreach ($thumbnailsRequired as $thumbnail) { - $this->imageHelper->generateThumbnail( - $originalPhotoResource, - $photo, - $albumSource, - $thumbnail - ); + $generatedThumbnailPath = $this->imageHelper->generateThumbnail($originalPhotoResource, $photo, $thumbnail); + $albumSource->saveThumbnail($album, $photo, $thumbnail, $generatedThumbnailPath); $this->output->writeln(sprintf('Thumbnail \'%s\' (%dx%d) created', $thumbnail['name'], $thumbnail['width'], $thumbnail['height'])); } diff --git a/app/Helpers/ImageHelper.php b/app/Helpers/ImageHelper.php index ada15a2..a860d5c 100644 --- a/app/Helpers/ImageHelper.php +++ b/app/Helpers/ImageHelper.php @@ -7,12 +7,7 @@ use App\Photo; class ImageHelper { - public function generateThumbnail( - $gdImageResource, - Photo $photo, - IAlbumSource $storage, - $thumbnailInfo - ) + public function generateThumbnail($gdImageResource, Photo $photo, $thumbnailInfo) { $thumbnailWidth = intval($thumbnailInfo['width']); $thumbnailHeight = intval($thumbnailInfo['height']); @@ -30,11 +25,16 @@ class ImageHelper $photo->width, $photo->height ); - $tempName = tempnam('/tmp', 'blue_twilight_thumbnail_'); - rename($tempName, $tempName . '.jpg'); + + // TODO make the /tmp folder configurable + $tempName = tempnam('/tmp', 'btw_thumb_'); + $tempNameWithExtension = ($tempName . '.jpg'); + rename($tempName, $tempNameWithExtension); // TODO make thumbnail quality configurable - imagejpeg($thumbnailImageResource, $tempName . '.jpg', 90); + imagejpeg($thumbnailImageResource, $tempNameWithExtension, 90); + + return $tempNameWithExtension; } public function openImage($imagePath, &$imageInfo) diff --git a/app/Helpers/MiscHelper.php b/app/Helpers/MiscHelper.php index 23857b1..37a4032 100644 --- a/app/Helpers/MiscHelper.php +++ b/app/Helpers/MiscHelper.php @@ -4,6 +4,12 @@ namespace App\Helpers; class MiscHelper { + public static function gravatarUrl($emailAddress, $size = 48, $default = 'identicon') + { + $hash = md5(strtolower(trim($emailAddress))); + return sprintf('https://www.gravatar.com/avatar/%s?s=%d&d=%s', $hash, $size, $default); + } + public static function randomString($length = 10) { $seed = 'abcdefghijklmnopqrstuvwxyz01234567890'; diff --git a/app/Http/Controllers/Gallery/DefaultController.php b/app/Http/Controllers/Gallery/DefaultController.php index 65e82c8..60bad4d 100644 --- a/app/Http/Controllers/Gallery/DefaultController.php +++ b/app/Http/Controllers/Gallery/DefaultController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Gallery; +use App\Album; use App\Facade\Theme; use App\Http\Controllers\Controller; @@ -9,6 +10,10 @@ class DefaultController extends Controller { public function index() { - return Theme::render('gallery.index'); + $albums = Album::all()->sortBy('name'); + + return Theme::render('gallery.index', [ + 'albums' => $albums + ]); } } \ No newline at end of file diff --git a/app/Http/Controllers/Gallery/PhotoController.php b/app/Http/Controllers/Gallery/PhotoController.php new file mode 100644 index 0000000..93cab1a --- /dev/null +++ b/app/Http/Controllers/Gallery/PhotoController.php @@ -0,0 +1,60 @@ +getAlbumSource(); + + $thumbnail = $request->get('t', $albumSource->getOriginalsFolder()); + $photo = PhotoController::loadPhotoByAlbumAndFilename($album, $photoFilename); + + return response()->file($albumSource->getPathToPhoto($album, $photo, $thumbnail)); + } + + /** + * @param $id + * @return Album + */ + public static function loadAlbumByAlias($alias) + { + $album = Album::where('url_alias', $alias)->first(); + if (is_null($album)) + { + App::abort(404); + return null; + } + + return $album; + } + + /** + * @param $id + * @return Photo + */ + public static function loadPhotoByAlbumAndFilename(Album $album, $filename) + { + $photo = Photo::where([ + ['album_id', $album->id], + ['file_name', $filename] + ])->first(); + + if (is_null($photo)) + { + App::abort(404); + return null; + } + + return $photo; + } +} \ No newline at end of file diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 99ca2bc..46893d0 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Album; use App\Facade\Theme; use App\Helpers\ImageHelper; use App\Helpers\ThemeHelper; @@ -27,13 +28,11 @@ class AppServiceProvider extends ServiceProvider return new ThemeHelper(); }); - try + // When running migrations or CLI tasks, don't need to add things to the view + if (php_sapi_name() != 'cli') { $this->addThemeInfoToView(); - } - catch (QueryException $ex) - { - // When running migrations, the configuration table may not exist - so ignore and continue + $this->addAlbumsToView(); } } @@ -47,6 +46,12 @@ class AppServiceProvider extends ServiceProvider // } + private function addAlbumsToView() + { + $albums = Album::all()->sortBy('name'); + View::share('albums', $albums); + } + private function addThemeInfoToView() { $themeInfo = Theme::info(); diff --git a/public/themes/base/css/app.css b/public/themes/base/css/app.css index cbf908f..e69de29 100644 --- a/public/themes/base/css/app.css +++ b/public/themes/base/css/app.css @@ -1,15 +0,0 @@ -@import url(https://fonts.googleapis.com/css?family=Raleway:300,400,600); - -* { - font-family: Raleway, sans-serif; -} - -.form-actions { - text-align: right; -} - -.tab-content { - border: solid 1px #ddd; - border-top: 0; - padding: 10px; -} \ No newline at end of file diff --git a/public/themes/bootstrap3/theme.css b/public/themes/bootstrap3/theme.css new file mode 100644 index 0000000..1562a1e --- /dev/null +++ b/public/themes/bootstrap3/theme.css @@ -0,0 +1,25 @@ +@import url(https://fonts.googleapis.com/css?family=Raleway:300,400,600); + +* { + font-family: Raleway, sans-serif; +} + +.album-index img { + max-width: 100%; +} + +.form-actions { + text-align: right; +} + +.navbar .avatar { + left: -28px; + position: absolute; + top: 9px; +} + +.tab-content { + border: solid 1px #ddd; + border-top: 0; + padding: 10px; +} \ No newline at end of file diff --git a/resources/views/themes/base/gallery/index.blade.php b/resources/views/themes/base/gallery/index.blade.php index ebbc9a9..25e3540 100644 --- a/resources/views/themes/base/gallery/index.blade.php +++ b/resources/views/themes/base/gallery/index.blade.php @@ -1,2 +1,25 @@ @extends('themes.base.layout') -@section('title', 'Welcome') \ No newline at end of file +@section('title', 'Welcome') + +@section('content') +
+ +
+{{ $album->description }}
+