diff --git a/app/AlbumSources/IAlbumSource.php b/app/AlbumSources/IAlbumSource.php index dc3c5de..4e98635 100644 --- a/app/AlbumSources/IAlbumSource.php +++ b/app/AlbumSources/IAlbumSource.php @@ -5,6 +5,7 @@ namespace App\AlbumSources; use App\Album; use App\Photo; use App\Storage; +use Guzzle\Http\EntityBody; use Symfony\Component\HttpFoundation\File\File; interface IAlbumSource @@ -23,20 +24,20 @@ interface IAlbumSource */ function deleteThumbnail(Photo $photo, $thumbnail = null); + /** + * Fetches the contents of a thumbnail for a photo. + * @param Photo $photo Photo to fetch the thumbnail for. + * @param string $thumbnail Thumbnail to fetch (or null to fetch the original.) + * @return EntityBody + */ + function fetchPhotoContent(Photo $photo, $thumbnail = null); + /** * Gets the name of this album source. * @return string */ function getName(); - /** - * Gets the absolute path to the given photo file. - * @param Photo $photo Photo to get the path to. - * @param string $thumbnail Thumbnail to get the image to. - * @return string - */ - function getPathToPhoto(Photo $photo, $thumbnail = null); - /** * Gets the absolute URL to the given photo file. * @param Photo $photo Photo to get the URL to. @@ -45,30 +46,14 @@ interface IAlbumSource */ function getUrlToPhoto(Photo $photo, $thumbnail = null); - /** - * Saves the original photo to its permanent location. - * @param Photo $photo Photo the original relates to. - * @param $tempFilename Filename containing the original image. - * @return mixed - */ - function saveOriginal(Photo $photo, $tempFilename); - /** * Saves a generated thumbnail to its permanent location. - * @param Photo $photo Photo the thumbnail relates to. - * @param string $thumbnailInfo Information about the thumbnail. - * @param string $tempFilename Filename containing the thumbnail. + * @param Photo $photo Photo the image relates to. + * @param string $tempFilename Filename containing the image. + * @param string $thumbnail Name of the thumbnail (or null for the original.) * @return mixed */ - function saveThumbnail(Photo $photo, $thumbnailInfo, $tempFilename); - - /** - * Saves an uploaded file to the container and returns the filename. - * @param File $uploadedFile The photo uploaded - * @param string $overrideFilename Specific file name to use, or null to randomly generate one. - * @return File - */ - function saveUploadedPhoto(File $uploadedFile, $overrideFilename = null); + function saveThumbnail(Photo $photo, $tempFilename, $thumbnail = null); /** * @param Album $album diff --git a/app/AlbumSources/LocalFilesystemSource.php b/app/AlbumSources/LocalFilesystemSource.php index 180b9ee..b2df40f 100644 --- a/app/AlbumSources/LocalFilesystemSource.php +++ b/app/AlbumSources/LocalFilesystemSource.php @@ -6,6 +6,7 @@ use App\Album; use App\Helpers\MiscHelper; use App\Photo; use App\Services\PhotoService; +use Guzzle\Http\EntityBody; use Symfony\Component\HttpFoundation\File\File; /** @@ -24,12 +25,33 @@ class LocalFilesystemSource extends AlbumSourceBase implements IAlbumSource public function deleteThumbnail(Photo $photo, $thumbnail = null) { - return @unlink($this->getPathToPhoto($photo, $thumbnail)); + return @unlink( + join(DIRECTORY_SEPARATOR, [ + $this->getPathToAlbum(), + is_null($thumbnail) ? $this->getOriginalsFolder() : $thumbnail, + $photo->storage_file_name + ]) + ); } - public function getOriginalsFolder() + /** + * Fetches the contents of a thumbnail for a photo. + * @param Photo $photo Photo to fetch the thumbnail for. + * @param string $thumbnail Thumbnail to fetch (or null to fetch the original.) + * @return EntityBody + */ + public function fetchPhotoContent(Photo $photo, $thumbnail = null) { - return '_originals'; + $fh = fopen( + join(DIRECTORY_SEPARATOR, [ + $this->getPathToAlbum(), + is_null($thumbnail) ? $this->getOriginalsFolder() : $thumbnail, + $photo->storage_file_name + ]), + 'r+' + ); + + return EntityBody::factory($fh); } public function getName() @@ -37,16 +59,6 @@ class LocalFilesystemSource extends AlbumSourceBase implements IAlbumSource return 'global.album_sources.filesystem'; } - public function getPathToPhoto(Photo $photo, $thumbnail = null) - { - if (is_null($thumbnail)) - { - $thumbnail = $this->getOriginalsFolder(); - } - - return sprintf('%s/%s/%s', $this->getPathToAlbum(), $thumbnail, $photo->storage_file_name); - } - public function getUrlToPhoto(Photo $photo, $thumbnail = null) { $photoUrl = route('downloadPhoto', [ @@ -62,38 +74,21 @@ class LocalFilesystemSource extends AlbumSourceBase implements IAlbumSource return $photoUrl; } - public function saveOriginal(Photo $photo, $tempFilename) - { - $this->saveThumbnail($photo, $tempFilename, $this->getOriginalsFolder()); - } - - public function saveThumbnail(Photo $photo, $tempFilename, $thumbnail) + public function saveThumbnail(Photo $photo, $tempFilename, $thumbnail = null) { $fileInfo = new File($tempFilename); - $fileInfo->move(sprintf('%s/%s', $this->getPathToAlbum(), $thumbnail), $photo->storage_file_name); + $fileInfo->move( + join(DIRECTORY_SEPARATOR, [ + $this->getPathToAlbum(), + is_null($thumbnail) ? $this->getOriginalsFolder() : $thumbnail + ]), + $photo->storage_file_name + ); } - public function saveUploadedPhoto(File $uploadedFile, $overrideFilename = null) + private function getOriginalsFolder() { - $tempFilename = sprintf( - '%s/%s/%s', - $this->getPathToAlbum(), - $this->getOriginalsFolder(), - is_null($overrideFilename) ? MiscHelper::randomString(20) : basename($overrideFilename) - ); - - // Only add an extension if an override filename was not given, assume this is present - if (is_null($overrideFilename)) - { - $extension = $uploadedFile->guessExtension(); - if (!is_null($extension)) - { - $tempFilename .= '.' . $extension; - } - } - - $uploadedFile->move(dirname($tempFilename), basename($tempFilename)); - return new File($tempFilename); + return '_originals'; } private function getPathToAlbum() diff --git a/app/AlbumSources/OpenStackSource.php b/app/AlbumSources/OpenStackSource.php index db338fe..4e564e7 100644 --- a/app/AlbumSources/OpenStackSource.php +++ b/app/AlbumSources/OpenStackSource.php @@ -2,6 +2,10 @@ namespace App\AlbumSources; use App\Photo; +use Guzzle\Http\EntityBody; +use Guzzle\Http\Exception\ClientErrorResponseException; +use Illuminate\Support\Facades\Log; +use OpenCloud\ObjectStore\Exception\ObjectNotFoundException; use OpenCloud\OpenStack; use Symfony\Component\HttpFoundation\File\File; @@ -17,7 +21,8 @@ class OpenStackSource extends AlbumSourceBase implements IAlbumSource */ public function deleteAlbumContents() { - // TODO: Implement deleteAlbumContents() method. + // Because all photos live in a single container, there is nothing "global" to delete for the entire album + // The delete routine will have already removed all photos } /** @@ -28,7 +33,30 @@ class OpenStackSource extends AlbumSourceBase implements IAlbumSource */ public function deleteThumbnail(Photo $photo, $thumbnail = null) { - // TODO: Implement deleteThumbnail() method. + $photoPath = $this->getPathToPhoto($photo, $thumbnail); + + try + { + $this->getContainer()->deleteObject($photoPath); + } + catch (ClientErrorResponseException $ex) + { + // Don't worry if the file no longer exists + Log::warning('Failed deleting image from OpenStack.', ['error' => $ex->getMessage(), 'path' => $photoPath]); + } + } + + /** + * Fetches the contents of a thumbnail for a photo. + * @param Photo $photo Photo to fetch the thumbnail for. + * @param string $thumbnail Thumbnail to fetch (or null to fetch the original.) + * @return EntityBody + */ + public function fetchPhotoContent(Photo $photo, $thumbnail = null) + { + $object = $this->getContainer()->getObject($this->getPathToPhoto($photo, $thumbnail)); + + return $object->getContent(); } /** @@ -40,17 +68,6 @@ class OpenStackSource extends AlbumSourceBase implements IAlbumSource return 'global.album_sources.openstack'; } - /** - * Gets the absolute path to the given photo file. - * @param Photo $photo Photo to get the path to. - * @param string $thumbnail Thumbnail to get the image to. - * @return string - */ - public function getPathToPhoto(Photo $photo, $thumbnail = null) - { - // TODO: Implement getPathToPhoto() method. - } - /** * Gets the absolute URL to the given photo file. * @param Photo $photo Photo to get the URL to. @@ -59,48 +76,47 @@ class OpenStackSource extends AlbumSourceBase implements IAlbumSource */ public function getUrlToPhoto(Photo $photo, $thumbnail = null) { - return sprintf( - '%s/%s/%s/%s', - 'https://photos-dev-898b0644.cdn.memsites.com', - $this->album->url_alias, - is_null($thumbnail) ? $this->getOriginalsFolder() : $thumbnail, - $photo->storage_file_name - ); - } + $cdnUrl = $this->configuration->cdn_url; + if (strlen($cdnUrl) > 0) + { + if (substr($cdnUrl, strlen($cdnUrl) - 1, 1) == '/') + { + $cdnUrl = substr($cdnUrl, 0, strlen($cdnUrl) - 1); + } - public function saveOriginal(Photo $photo, $tempFilename) - { - $this->saveThumbnail($photo, $tempFilename, $this->getOriginalsFolder()); + return sprintf('%s/%s', $cdnUrl, $this->getPathToPhoto($photo, $thumbnail)); + } + + // Not using a CDN - use the standard download controller + $photoUrl = route('downloadPhoto', [ + 'albumUrlAlias' => $this->album->url_alias, + 'photoFilename' => $photo->storage_file_name + ]); + + if (!is_null($thumbnail)) + { + $photoUrl .= sprintf('?t=%s', urlencode($thumbnail)); + } + + return $photoUrl; } /** * Saves a generated thumbnail to its permanent location. - * @param Photo $photo Photo the thumbnail relates to. - * @param string $tempFilename Filename containing the thumbnail. - * @param string $thumbnail Name of the thumbnail. + * @param Photo $photo Photo the image relates to. + * @param string $tempFilename Filename containing the image. + * @param string $thumbnail Name of the thumbnail (or null for the original.) * @return mixed */ - public function saveThumbnail(Photo $photo, $tempFilename, $thumbnail) + public function saveThumbnail(Photo $photo, $tempFilename, $thumbnail = null) { + $photoPath = $this->getPathToPhoto($photo, $thumbnail); + $container = $this->getContainer(); - $container->uploadObject( - sprintf('%s/%s/%s', $this->album->url_alias, $thumbnail, $photo->storage_file_name), - fopen($tempFilename, 'r+') - ); + $container->uploadObject($photoPath, fopen($tempFilename, 'r+')); } - /** - * Saves an uploaded file to the container and returns the filename. - * @param File $uploadedFile The photo uploaded - * @param string $overrideFilename Specific file name to use, or null to randomly generate one. - * @return File - */ - public function saveUploadedPhoto(File $uploadedFile, $overrideFilename = null) - { - // TODO: Implement saveUploadedPhoto() method. - } - - private function getClient() + protected function getClient() { return new OpenStack($this->configuration->auth_url, [ 'username' => $this->configuration->username, @@ -109,17 +125,12 @@ class OpenStackSource extends AlbumSourceBase implements IAlbumSource ]); } - private function getContainer() + protected function getContainer() { return $this->getStorageService()->getContainer($this->configuration->container_name); } - private function getOriginalsFolder() - { - return '_originals'; - } - - private function getStorageService() + protected function getStorageService() { return $this->getClient()->objectStoreService( $this->configuration->service_name, @@ -127,4 +138,19 @@ class OpenStackSource extends AlbumSourceBase implements IAlbumSource 'publicURL' ); } + + private function getOriginalsFolder() + { + return '_originals'; + } + + private function getPathToPhoto(Photo $photo, $thumbnail = null) + { + return sprintf( + '%s/%s/%s', + $this->album->url_alias, + is_null($thumbnail) ? $this->getOriginalsFolder() : $thumbnail, + $photo->storage_file_name + ); + } } \ No newline at end of file diff --git a/app/Http/Controllers/Admin/PhotoController.php b/app/Http/Controllers/Admin/PhotoController.php index fab44df..aba26c6 100644 --- a/app/Http/Controllers/Admin/PhotoController.php +++ b/app/Http/Controllers/Admin/PhotoController.php @@ -112,7 +112,8 @@ class PhotoController extends Controller { $this->authorize('admin-access'); - settype($direction, 'boolean'); + settype($horizontal, 'boolean'); + settype($vertical, 'boolean'); $photo = Photo::where('id', intval($photoId))->first(); if (is_null($photo)) @@ -407,9 +408,7 @@ class PhotoController extends Controller return null; } - $numberChanged = 0; - - if ($request->has('bulk-apply')) + if ($request->has('bulk-action')) { $numberChanged = $this->applyBulkActions($request, $album); } @@ -445,6 +444,7 @@ class PhotoController extends Controller } $photoService = new PhotoService($photo); + $doNotSave = false; switch (strtolower($action)) { case 'change_album': @@ -466,6 +466,7 @@ class PhotoController extends Controller case 'delete': $photoService->delete(); + $doNotSave = true; break; case 'flip_both': @@ -493,7 +494,10 @@ class PhotoController extends Controller break; } - $photo->save(); + if (!$doNotSave) + { + $photo->save(); + } $numberChanged++; } diff --git a/app/Http/Controllers/Admin/StorageController.php b/app/Http/Controllers/Admin/StorageController.php index 796e9f9..a077954 100644 --- a/app/Http/Controllers/Admin/StorageController.php +++ b/app/Http/Controllers/Admin/StorageController.php @@ -78,7 +78,8 @@ class StorageController extends Controller 'password', 'service_name', 'service_region', - 'container_name' + 'container_name', + 'cdn_url' ])); $storage->is_active = true; $storage->is_default = (strtolower($request->get('is_default')) == 'on'); @@ -164,6 +165,11 @@ class StorageController extends Controller App::abort(404); } + if (isset($storage->password) && strlen($storage->password) > 0) + { + $storage->password = decrypt($storage->password); + } + return Theme::render('admin.edit_storage', ['storage' => $storage]); } @@ -184,7 +190,17 @@ class StorageController extends Controller App::abort(404); } - $storage->fill($request->only(['name'])); + $storage->fill($request->only([ + 'name', + 'auth_url', + 'tenant_name', + 'username', + 'password', + 'service_name', + 'service_region', + 'container_name', + 'cdn_url' + ])); $storage->is_active = (strtolower($request->get('is_active')) == 'on'); $storage->is_default = (strtolower($request->get('is_default')) == 'on'); @@ -193,6 +209,11 @@ class StorageController extends Controller $storage->is_default = false; } + if (isset($storage->password) && !empty($storage->password)) + { + $storage->password = encrypt($storage->password); + } + $storage->save(); if ($storage->is_default) diff --git a/app/Http/Controllers/Gallery/PhotoController.php b/app/Http/Controllers/Gallery/PhotoController.php index 69a3636..94708bf 100644 --- a/app/Http/Controllers/Gallery/PhotoController.php +++ b/app/Http/Controllers/Gallery/PhotoController.php @@ -11,6 +11,7 @@ use app\Http\Controllers\Admin\AlbumController; use App\Http\Controllers\Controller; use App\Http\Middleware\VerifyCsrfToken; use App\Photo; +use Guzzle\Http\Mimetypes; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Gate; use Symfony\Component\HttpFoundation\Request; @@ -53,10 +54,23 @@ class PhotoController extends Controller $thumbnail = $request->get('t'); if (is_null($thumbnail)) { - Gate::forUser($this->getUser())->authorize('photo.download_original', $photo); + $this->authorizeForUser($this->getUser(), 'photo.download_original', $photo); } - return response()->file($album->getAlbumSource()->getPathToPhoto($photo, $thumbnail)); + $photoStream = $album->getAlbumSource()->fetchPhotoContent($photo, $thumbnail); + $mimeType = Mimetypes::getInstance()->fromFilename($photo->storage_file_name); + + return response()->stream( + function() use ($photoStream) + { + echo $photoStream; + }, + 200, + [ + 'Content-Length' => $photoStream->getContentLength(), + 'Content-Type' => $mimeType + ] + ); } public function show(Request $request, $albumUrlAlias, $photoFilename) diff --git a/app/Http/Requests/StoreStorageRequest.php b/app/Http/Requests/StoreStorageRequest.php index 79980c6..7320fda 100644 --- a/app/Http/Requests/StoreStorageRequest.php +++ b/app/Http/Requests/StoreStorageRequest.php @@ -24,6 +24,8 @@ class StoreStorageRequest extends FormRequest */ public function rules() { + $result = []; + switch ($this->method()) { case 'POST': @@ -46,19 +48,35 @@ class StoreStorageRequest extends FormRequest $result['service_name'] = 'sometimes|required'; $result['service_region'] = 'sometimes|required'; $result['container_name'] = 'sometimes|required'; + $result['cdn_url'] = 'sometimes|url'; break; } - - return $result; + break; case 'PATCH': case 'PUT': $storageId = intval($this->segment(3)); - - return [ + $storage = Storage::find($storageId); + $result = [ 'name' => 'required|max:255|unique:storages,name,' . $storageId ]; + + switch ($storage->source) + { + case 'OpenStackSource': + $result['auth_url'] = 'sometimes|required'; + $result['tenant_name'] = 'sometimes|required'; + $result['username'] = 'sometimes|required'; + $result['password'] = 'sometimes|required'; + $result['service_name'] = 'sometimes|required'; + $result['service_region'] = 'sometimes|required'; + $result['container_name'] = 'sometimes|required'; + $result['cdn_url'] = 'sometimes|url'; + break; + } + break; } + return $result; } } \ No newline at end of file diff --git a/app/Services/PhotoService.php b/app/Services/PhotoService.php index 3470c43..fddc6e5 100644 --- a/app/Services/PhotoService.php +++ b/app/Services/PhotoService.php @@ -117,7 +117,7 @@ class PhotoService $this->photo->save(); // Save the original - $this->albumSource->saveOriginal($this->photo, $photoFile); + $this->albumSource->saveThumbnail($this->photo, $photoFile); $this->regenerateThumbnails($originalPhotoResource); } @@ -128,11 +128,11 @@ class PhotoService $currentSource = $this->photo->album->getAlbumSource(); $newSource = $newAlbum->getAlbumSource(); - // Get the current photo's path - $file = new File($currentSource->getPathToPhoto($this->photo)); + // First export the original photo from the storage provider + $photoPath = $this->downloadToTemporaryFolder(); // Save to the new album - $newSource->saveUploadedPhoto($file, $this->photo->storage_file_name); + $newSource->saveThumbnail($this->photo, $photoPath); // Delete the original $this->delete(); @@ -169,25 +169,39 @@ class PhotoService public function flip($horizontal, $vertical) { + // First export the original photo from the storage provider + $photoPath = $this->downloadToTemporaryFolder(); + $imageInfo = array(); - $photoPath = $this->albumSource->getPathToPhoto($this->photo); $originalPhotoImage = $this->imageHelper->openImage($photoPath, $imageInfo); if ($this->imageHelper->flipImage($originalPhotoImage, boolval($horizontal), boolval($vertical))) { $this->imageHelper->saveImage($originalPhotoImage, $photoPath, $imageInfo); + // Update and save the original image back to the storage provider + $this->albumSource->saveThumbnail($this->photo, $photoPath); + + // Re-create the thumbnails $this->regenerateThumbnails($originalPhotoImage); $this->photo->save(); } + + // Remove the temp file + @unlink($photoPath); } public function regenerateThumbnails($originalPhotoResource = null) { + $photoPath = null; + if (is_null($originalPhotoResource)) { + // First export the original photo from the storage provider + $photoPath = $this->downloadToTemporaryFolder(); + $imageInfo = null; - $originalPhotoResource = $this->imageHelper->openImage($this->albumSource->getPathToPhoto($this->photo), $imageInfo); + $originalPhotoResource = $this->imageHelper->openImage($photoPath, $imageInfo); } // Generate and save thumbnails @@ -200,16 +214,28 @@ class PhotoService $generatedThumbnailPath = $this->imageHelper->generateThumbnail($originalPhotoResource, $this->photo, $thumbnail); $this->albumSource->saveThumbnail($this->photo, $generatedThumbnailPath, $thumbnail['name']); } + + if (is_null($originalPhotoResource) && !is_null($photoPath)) + { + // Remove the temp file + @unlink($photoPath); + } } public function rotate($angle) { $imageInfo = array(); - $photoPath = $this->albumSource->getPathToPhoto($this->photo); + + // First export the photo from the storage provider + $photoPath = $this->downloadToTemporaryFolder(); + $originalPhotoImage = $this->imageHelper->openImage($photoPath, $imageInfo); $originalPhotoImage = $this->imageHelper->rotateImage($originalPhotoImage, intval($angle)); $this->imageHelper->saveImage($originalPhotoImage, $photoPath, $imageInfo); + // Update and save the original image back to the storage provider + $this->albumSource->saveThumbnail($this->photo, $photoPath); + if ($angle == 90 || $angle == 270) { $width = $this->photo->width; @@ -220,6 +246,27 @@ class PhotoService $this->regenerateThumbnails($originalPhotoImage); $this->photo->save(); + + // Remove the temp file + @unlink($photoPath); + } + + private function downloadToTemporaryFolder() + { + $photoPath = tempnam(sys_get_temp_dir(), 'BlueTwilight_'); + $photoHandle = fopen($photoPath, 'w'); + + $stream = $this->albumSource->fetchPhotoContent($this->photo); + $stream->rewind(); + while (!$stream->feof()) + { + fwrite($photoHandle, $stream->read(4096)); + } + fflush($photoHandle); + fclose($photoHandle); + $stream->close(); + + return $photoPath; } private function metadataCameraMake(array $exifData) diff --git a/app/Storage.php b/app/Storage.php index 595b79e..09e4094 100644 --- a/app/Storage.php +++ b/app/Storage.php @@ -27,7 +27,8 @@ class Storage extends Model 'password', 'service_name', 'service_region', - 'container_name' + 'container_name', + 'cdn_url' ]; public function albums() diff --git a/database/migrations/2016_10_28_083537_add_cdn_url_storage_column.php b/database/migrations/2016_10_28_083537_add_cdn_url_storage_column.php new file mode 100644 index 0000000..ebf1f9a --- /dev/null +++ b/database/migrations/2016_10_28_083537_add_cdn_url_storage_column.php @@ -0,0 +1,32 @@ +string('cdn_url')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('storages', function (Blueprint $table) { + $table->dropColumn('cdn_url'); + }); + } +} diff --git a/public/themes/base/js/app.js b/public/themes/base/js/app.js index 4d745e5..378f9d2 100644 --- a/public/themes/base/js/app.js +++ b/public/themes/base/js/app.js @@ -93,10 +93,16 @@ function EditPhotosViewModel(album_id, language, urls) { self.albums = ko.observableArray(); self.bulkModifyMethod = ko.observable(); self.photoIDs = ko.observableArray(); + self.isSubmitting = false; /* Called when the Apply button on the "bulk apply selected actions" form is clicked */ self.bulkModifySelected = function() { + if (self.isSubmitting) + { + return true; + } + var bulk_form = $('form#bulk-modify-form'); if (self.bulkModifyMethod() == 'change_album') @@ -104,8 +110,11 @@ function EditPhotosViewModel(album_id, language, urls) { // Prompt for the new album to move to self.promptForNewAlbum(function(dialog) { var album_id = $('select', dialog).val(); - $('input[name="new-album-id"]', form).val(album_id); - bulk_form.submit(); + $('input[name="new-album-id"]', bulk_form).val(album_id); + + self.isSubmitting = true; + $('button[name="bulk-apply"]', bulk_form).click(); + _bt_showLoadingModal(); }); return false; @@ -125,7 +134,9 @@ function EditPhotosViewModel(album_id, language, urls) { label: language.action_delete, className: "btn-danger", callback: function() { - bulk_form.submit(); + self.isSubmitting = true; + $('button[name="bulk-apply"]', bulk_form).click(); + _bt_showLoadingModal(); } } } @@ -151,6 +162,7 @@ function EditPhotosViewModel(album_id, language, urls) { { window.location.reload(); }); + _bt_showLoadingModal(); }); return false; diff --git a/resources/lang/en/forms.php b/resources/lang/en/forms.php index bc2d654..ec6b46a 100644 --- a/resources/lang/en/forms.php +++ b/resources/lang/en/forms.php @@ -31,6 +31,7 @@ return [ 'settings_restrict_originals_download_help' => 'With this option enabled, only the photo\'s owner can download the original high-resolution images.', 'storage_active_label' => 'Location is active. Uncheck to prevent creating new albums in this location.', 'storage_auth_url_label' => 'Authentication URL:', + 'storage_cdn_url_label' => 'Public CDN URL (if supported and enabled):', 'storage_container_name_label' => 'Container name:', 'storage_driver_label' => 'Storage driver:', 'storage_location_label' => 'Physical location:', diff --git a/resources/lang/en/global.php b/resources/lang/en/global.php index 2266f7e..7399a80 100644 --- a/resources/lang/en/global.php +++ b/resources/lang/en/global.php @@ -8,6 +8,7 @@ return [ 'copyright' => '© :years :link_startAndy Heathershaw:link_end', 'licensed_to' => 'Licensed to :name (:number)', 'version_number' => 'Version :version', + 'please_wait' => 'Please wait...', 'post_max_exceeded' => 'Your upload exceeded the maximum size the web server is configured to allow. Please check the value of the "post_max_size" parameter in php.ini.', 'powered_by' => 'Powered by :link_startBlue Twilight:link_end - the self-hosted photo gallery software.', 'units' => [ diff --git a/resources/views/themes/base/admin/create_storage.blade.php b/resources/views/themes/base/admin/create_storage.blade.php index f1db809..916f4b3 100644 --- a/resources/views/themes/base/admin/create_storage.blade.php +++ b/resources/views/themes/base/admin/create_storage.blade.php @@ -145,6 +145,17 @@ @endif + +
+ {!! Form::label('cdn_url', trans('forms.storage_cdn_url_label'), ['class' => 'control-label']) !!} + {!! Form::text('cdn_url', old('cdn_url'), ['class' => 'form-control']) !!} + + @if ($errors->has('cdn_url')) + + {{ $errors->first('cdn_url') }} + + @endif +
diff --git a/resources/views/themes/base/admin/edit_storage.blade.php b/resources/views/themes/base/admin/edit_storage.blade.php index 4f1c322..c6fa492 100644 --- a/resources/views/themes/base/admin/edit_storage.blade.php +++ b/resources/views/themes/base/admin/edit_storage.blade.php @@ -48,6 +48,112 @@ + @if ($storage->source == 'OpenStackSource') +
+
+
+
+ {!! Form::label('auth_url', trans('forms.storage_auth_url_label'), ['class' => 'control-label']) !!} + {!! Form::text('auth_url', old('auth_url'), ['class' => 'form-control']) !!} + + @if ($errors->has('auth_url')) + + {{ $errors->first('auth_url') }} + + @endif +
+
+
+
+ {!! Form::label('tenant_name', trans('forms.storage_tenant_name_label'), ['class' => 'control-label']) !!} + {!! Form::text('tenant_name', old('tenant_name'), ['class' => 'form-control']) !!} + + @if ($errors->has('tenant_name')) + + {{ $errors->first('tenant_name') }} + + @endif +
+
+
+ +
+
+
+ {!! Form::label('username', trans('forms.username_label'), ['class' => 'control-label']) !!} + {!! Form::text('username', old('username'), ['class' => 'form-control']) !!} + + @if ($errors->has('username')) + + {{ $errors->first('username') }} + + @endif +
+
+
+
+ {!! Form::label('password', trans('forms.password_label'), ['class' => 'control-label']) !!} + {!! Form::text('password', old('password'), ['class' => 'form-control']) !!} + + @if ($errors->has('password')) + + {{ $errors->first('password') }} + + @endif +
+
+
+ +
+
+
+ {!! Form::label('service_name', trans('forms.storage_service_name_label'), ['class' => 'control-label']) !!} + {!! Form::text('service_name', old('service_name'), ['class' => 'form-control']) !!} + + @if ($errors->has('service_name')) + + {{ $errors->first('service_name') }} + + @endif +
+
+
+
+ {!! Form::label('service_region', trans('forms.storage_service_region_label'), ['class' => 'control-label']) !!} + {!! Form::text('service_region', old('service_region'), ['class' => 'form-control']) !!} + + @if ($errors->has('service_region')) + + {{ $errors->first('service_region') }} + + @endif +
+
+
+ +
+ {!! Form::label('container_name', trans('forms.storage_container_name_label'), ['class' => 'control-label']) !!} + {!! Form::text('container_name', old('container_name'), ['class' => 'form-control']) !!} + + @if ($errors->has('container_name')) + + {{ $errors->first('container_name') }} + + @endif +
+ +
+ {!! Form::label('cdn_url', trans('forms.storage_cdn_url_label'), ['class' => 'control-label']) !!} + {!! Form::text('cdn_url', old('cdn_url'), ['class' => 'form-control']) !!} + + @if ($errors->has('cdn_url')) + + {{ $errors->first('cdn_url') }} + + @endif +
+ @endif +
@lang('forms.cancel_action') {!! Form::submit(trans('forms.save_action'), ['class' => 'btn btn-success']) !!} diff --git a/resources/views/themes/base/layout.blade.php b/resources/views/themes/base/layout.blade.php index 47fa6b5..3f27f4a 100644 --- a/resources/views/themes/base/layout.blade.php +++ b/resources/views/themes/base/layout.blade.php @@ -78,6 +78,14 @@