Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0ce4c368a3 | ||
|
1173b2baba | ||
|
c3ce6e1d71 | ||
|
e1ad66c9ef | ||
|
2caa1c8fbc |
@ -1,7 +1,7 @@
|
|||||||
APP_ENV=production
|
APP_ENV=local
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=false
|
APP_DEBUG=true
|
||||||
APP_LOG_LEVEL=warning
|
APP_LOG_LEVEL=debug
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
|
|
||||||
DB_CONNECTION=mysql
|
DB_CONNECTION=mysql
|
||||||
|
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -1,2 +1,3 @@
|
|||||||
* text=auto
|
* text=auto
|
||||||
resources/assets/* linguist-vendored
|
*.css linguist-vendored
|
||||||
|
*.scss linguist-vendored
|
||||||
|
157
Gruntfile.js
157
Gruntfile.js
@ -1,157 +0,0 @@
|
|||||||
/*
|
|
||||||
This Gruntfile downloads the necessary CSS and JS includes (Bootstrap, etc.) and creates a combined file ready for
|
|
||||||
deployment with the application.
|
|
||||||
|
|
||||||
Available tasks:
|
|
||||||
|
|
||||||
- build-debug: Builds the minified CSS and JS files
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = function(grunt)
|
|
||||||
{
|
|
||||||
var download_url = 'https://cdn.andysh.uk/';
|
|
||||||
|
|
||||||
const sass = require('node-sass');
|
|
||||||
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-cssmin');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
|
||||||
grunt.loadNpmTasks('grunt-curl');
|
|
||||||
grunt.loadNpmTasks('grunt-dart-sass');
|
|
||||||
grunt.loadNpmTasks('grunt-exec');
|
|
||||||
|
|
||||||
grunt.initConfig({
|
|
||||||
pkg: grunt.file.readJSON('package.json'),
|
|
||||||
clean: {
|
|
||||||
build_css: [
|
|
||||||
// Clean the build folder - downloaded files
|
|
||||||
'build/css',
|
|
||||||
// Clean the resources folder - compiled SASS files
|
|
||||||
'resources/css'
|
|
||||||
],
|
|
||||||
build_js: [
|
|
||||||
'build/js',
|
|
||||||
],
|
|
||||||
output: [
|
|
||||||
'public/css',
|
|
||||||
'public/js'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
concat: {
|
|
||||||
// Concatenate all third-party stylesheets into blue-twilight.css
|
|
||||||
bt_css: {
|
|
||||||
src: [
|
|
||||||
'build/css/*.css',
|
|
||||||
'resources/css/*.css'
|
|
||||||
],
|
|
||||||
dest: 'public/css/blue-twilight.css'
|
|
||||||
},
|
|
||||||
// Concatenate all third-party and application Javascripts into blue-twilight.js
|
|
||||||
bt_js: {
|
|
||||||
src: [
|
|
||||||
'build/js/*.js',
|
|
||||||
'resources/js/*.js',
|
|
||||||
],
|
|
||||||
dest: 'public/js/blue-twilight.js'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
curl: {
|
|
||||||
/* These elements are sorted according to the order we desire them to be loaded when concatenated */
|
|
||||||
/** CSS **/
|
|
||||||
bootstrap_css: {
|
|
||||||
src: download_url + 'bootstrap/4.4.1/css/bootstrap.css',
|
|
||||||
dest: 'build/css/01-bootstrap.css'
|
|
||||||
},
|
|
||||||
|
|
||||||
/** JS **/
|
|
||||||
jquery: {
|
|
||||||
src: download_url + 'jquery/3.4.1/jquery.js',
|
|
||||||
dest: 'build/js/01-jquery.js'
|
|
||||||
},
|
|
||||||
bootstrap_js: {
|
|
||||||
src: download_url + 'bootstrap/4.4.1/js/bootstrap.bundle.js',
|
|
||||||
dest: 'build/js/02-bootstrap.js'
|
|
||||||
},
|
|
||||||
bootbox_js: {
|
|
||||||
src: download_url + 'bootbox/5.3.3/bootbox.all.js',
|
|
||||||
dest: 'build/js/03-bootbox.js'
|
|
||||||
},
|
|
||||||
font_awesome_js: {
|
|
||||||
src: download_url + 'font-awesome/5.12.0/js/all.js',
|
|
||||||
dest: 'build/js/04-fontawesome.js'
|
|
||||||
},
|
|
||||||
vuejs: {
|
|
||||||
src: download_url + 'vuejs/2.6.11/vue.js',
|
|
||||||
dest: 'build/js/05-vuejs.js'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'dart-sass': {
|
|
||||||
bt_sass: {
|
|
||||||
options: {
|
|
||||||
sourceMap: false
|
|
||||||
},
|
|
||||||
files: [{
|
|
||||||
expand: true,
|
|
||||||
cwd: 'resources/sass/',
|
|
||||||
src: ['*.scss'],
|
|
||||||
dest: 'resources/css/',
|
|
||||||
ext: '.css'
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
cssmin: {
|
|
||||||
bt_css: {
|
|
||||||
files: {
|
|
||||||
'public/css/blue-twilight.min.css': ['public/css/blue-twilight.css']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
uglify: {
|
|
||||||
bt_js: {
|
|
||||||
options: {
|
|
||||||
sourceMap: true
|
|
||||||
},
|
|
||||||
files: {
|
|
||||||
'public/js/blue-twilight.min.js': ['public/js/blue-twilight.js']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Register our tasks
|
|
||||||
grunt.registerTask('build-css-debug', [
|
|
||||||
// Download third-party stylesheets
|
|
||||||
'curl:bootstrap_css',
|
|
||||||
// SASS our own CSS
|
|
||||||
'dart-sass:bt_sass',
|
|
||||||
// Create our blue-twilight.css
|
|
||||||
'concat:bt_css'
|
|
||||||
]);
|
|
||||||
|
|
||||||
grunt.registerTask('build-js-debug', [
|
|
||||||
// Download third-party Javascripts
|
|
||||||
'curl:jquery',
|
|
||||||
'curl:bootstrap_js',
|
|
||||||
'curl:bootbox_js',
|
|
||||||
'curl:font_awesome_js',
|
|
||||||
'curl:vuejs',
|
|
||||||
// Create our blue-twilight.js
|
|
||||||
'concat:bt_js'
|
|
||||||
]);
|
|
||||||
|
|
||||||
grunt.registerTask('build-css-release', [
|
|
||||||
'build-css-debug',
|
|
||||||
'cssmin:bt_css'
|
|
||||||
]);
|
|
||||||
|
|
||||||
grunt.registerTask('build-js-release', [
|
|
||||||
'build-js-debug',
|
|
||||||
'uglify:bt_js'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Shortcut tasks for the ones above
|
|
||||||
grunt.registerTask('clean-all', ['clean:build_css', 'clean:build_js', 'clean:output']);
|
|
||||||
grunt.registerTask('build-debug', ['clean-all', 'build-css-debug', 'build-js-debug']);
|
|
||||||
grunt.registerTask('build-release', ['clean-all', 'build-css-release', 'build-js-release']);
|
|
||||||
};
|
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use App\AlbumSources\AlbumSourceBase;
|
|
||||||
use App\AlbumSources\IAlbumSource;
|
use App\AlbumSources\IAlbumSource;
|
||||||
use App\AlbumSources\LocalFilesystemSource;
|
use App\AlbumSources\LocalFilesystemSource;
|
||||||
use App\Helpers\MiscHelper;
|
use App\Helpers\MiscHelper;
|
||||||
@ -22,7 +21,7 @@ class Album extends Model
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name', 'description', 'url_alias', 'user_id', 'storage_id', 'default_view', 'parent_album_id', 'url_path', 'is_permissions_inherited'
|
'name', 'description', 'url_alias', 'user_id', 'storage_id', 'default_view', 'parent_album_id', 'url_path'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,35 +103,6 @@ class Album extends Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Try and locate the parent album ID that permissions are inherited from.
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
public function effectiveAlbumIDForPermissions()
|
|
||||||
{
|
|
||||||
$current = $this;
|
|
||||||
|
|
||||||
while (!is_null($current->parent_album_id))
|
|
||||||
{
|
|
||||||
if ($current->is_permissions_inherited)
|
|
||||||
{
|
|
||||||
$current = $current->parent;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($current->parent_album_id) && $current->is_permissions_inherited)
|
|
||||||
{
|
|
||||||
// Use default permissions list
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $current->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateAlias()
|
public function generateAlias()
|
||||||
{
|
{
|
||||||
$this->url_alias = MiscHelper::capitaliseWord(preg_replace('/[^a-z0-9\-]/', '-', strtolower($this->name)));
|
$this->url_alias = MiscHelper::capitaliseWord(preg_replace('/[^a-z0-9\-]/', '-', strtolower($this->name)));
|
||||||
@ -159,7 +129,10 @@ class Album extends Model
|
|||||||
*/
|
*/
|
||||||
public function getAlbumSource()
|
public function getAlbumSource()
|
||||||
{
|
{
|
||||||
$source = AlbumSourceBase::make($this->storage->source);
|
$fullClassName = sprintf('App\AlbumSources\%s', $this->storage->source);
|
||||||
|
|
||||||
|
/** @var IAlbumSource $source */
|
||||||
|
$source = new $fullClassName;
|
||||||
$source->setAlbum($this);
|
$source->setAlbum($this);
|
||||||
$source->setConfiguration($this->storage);
|
$source->setConfiguration($this->storage);
|
||||||
|
|
||||||
@ -213,38 +186,21 @@ class Album extends Model
|
|||||||
|
|
||||||
public function thumbnailUrl($thumbnailName)
|
public function thumbnailUrl($thumbnailName)
|
||||||
{
|
{
|
||||||
/** @var Photo $photo */
|
|
||||||
$photo = $this->photos()
|
$photo = $this->photos()
|
||||||
->inRandomOrder()
|
->inRandomOrder()
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if (!is_null($photo))
|
if (!is_null($photo))
|
||||||
{
|
{
|
||||||
return $photo->thumbnailUrl($thumbnailName);
|
return $this->getAlbumSource()->getUrlToPhoto($photo, $thumbnailName);
|
||||||
}
|
|
||||||
|
|
||||||
// See if any child albums have an image
|
|
||||||
$images = [];
|
|
||||||
|
|
||||||
/** @var Album $childAlbum */
|
|
||||||
foreach ($this->children as $childAlbum)
|
|
||||||
{
|
|
||||||
if ($childAlbum->photos()->count() > 0)
|
|
||||||
{
|
|
||||||
$images[] = $childAlbum->thumbnailUrl($thumbnailName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($images) == 0)
|
|
||||||
{
|
|
||||||
// Rotate standard images
|
|
||||||
$images = [
|
|
||||||
asset('themes/base/images/empty-album-1.jpg'),
|
|
||||||
asset('themes/base/images/empty-album-2.jpg'),
|
|
||||||
asset('themes/base/images/empty-album-3.jpg')
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rotate standard images
|
||||||
|
$images = [
|
||||||
|
asset('themes/base/images/empty-album-1.jpg'),
|
||||||
|
asset('themes/base/images/empty-album-2.jpg'),
|
||||||
|
asset('themes/base/images/empty-album-3.jpg')
|
||||||
|
];
|
||||||
return $images[rand(0, count($images) - 1)];
|
return $images[rand(0, count($images) - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,11 +215,6 @@ class Album extends Model
|
|||||||
return route('viewAlbum', $this->url_path);
|
return route('viewAlbum', $this->url_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function user()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function userPermissions()
|
public function userPermissions()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Permission::class, 'album_user_permissions');
|
return $this->belongsToMany(Permission::class, 'album_user_permissions');
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class AlbumDefaultAnonymousPermission extends Model
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class AlbumDefaultGroupPermission extends Model
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class AlbumDefaultUserPermission extends Model
|
|
||||||
{
|
|
||||||
}
|
|
@ -17,36 +17,6 @@ abstract class AlbumSourceBase
|
|||||||
*/
|
*/
|
||||||
protected $configuration;
|
protected $configuration;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
private static $albumSourceCache = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes an album source class for the given source name (relative class name.)
|
|
||||||
* @param string $sourceName Name of the source.
|
|
||||||
* @return IAlbumSource
|
|
||||||
*/
|
|
||||||
public static function make($sourceName)
|
|
||||||
{
|
|
||||||
$fullClassName = sprintf('App\AlbumSources\%s', $sourceName);
|
|
||||||
|
|
||||||
if (!array_key_exists($fullClassName, self::$albumSourceCache))
|
|
||||||
{
|
|
||||||
/** @var IAlbumSource $source */
|
|
||||||
$source = app($fullClassName);
|
|
||||||
|
|
||||||
self::$albumSourceCache[$fullClassName] = $source;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$albumSourceCache[$fullClassName];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getConfiguration()
|
|
||||||
{
|
|
||||||
return $this->configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setAlbum(Album $album)
|
public function setAlbum(Album $album)
|
||||||
{
|
{
|
||||||
$this->album = $album;
|
$this->album = $album;
|
||||||
|
@ -2,14 +2,12 @@
|
|||||||
|
|
||||||
namespace App\AlbumSources;
|
namespace App\AlbumSources;
|
||||||
|
|
||||||
use App\Helpers\MiscHelper;
|
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
use Guzzle\Http\EntityBody;
|
||||||
use GuzzleHttp\Psr7\Stream;
|
use Guzzle\Http\Exception\ClientErrorResponseException;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use function GuzzleHttp\Psr7\stream_for;
|
|
||||||
|
|
||||||
class AmazonS3Source extends AlbumSourceBase implements IAlbumSource, IAnalysisQueueSource
|
class AmazonS3Source extends AlbumSourceBase implements IAlbumSource
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Deletes an entire album's media contents.
|
* Deletes an entire album's media contents.
|
||||||
@ -21,30 +19,6 @@ class AmazonS3Source extends AlbumSourceBase implements IAlbumSource, IAnalysisQ
|
|||||||
// The delete routine will have already removed all photos
|
// The delete routine will have already removed all photos
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a photo to be analysed from the storage source
|
|
||||||
* @param string $queueToken Queue token holding the photo
|
|
||||||
* @param string $fileName Filename of the photo to download
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function deleteItemFromAnalysisQueue($queueToken, $fileName)
|
|
||||||
{
|
|
||||||
$fileToDelete = $this->getPathToAnalysisQueueItem($queueToken, $fileName);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$this->getClient()->deleteObject([
|
|
||||||
'Bucket' => $this->configuration->container_name,
|
|
||||||
'Key' => $fileToDelete
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
catch (GuzzleException $ex)
|
|
||||||
{
|
|
||||||
// Don't worry if the file no longer exists
|
|
||||||
Log::warning('Failed deleting image from S3.', ['error' => $ex->getMessage(), 'path' => $fileToDelete]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a thumbnail file for a photo.
|
* Deletes a thumbnail file for a photo.
|
||||||
* @param Photo $photo Photo to delete the thumbnail from.
|
* @param Photo $photo Photo to delete the thumbnail from.
|
||||||
@ -62,37 +36,18 @@ class AmazonS3Source extends AlbumSourceBase implements IAlbumSource, IAnalysisQ
|
|||||||
'Key' => $this->getPathToPhoto($photo, $thumbnail)
|
'Key' => $this->getPathToPhoto($photo, $thumbnail)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
catch (GuzzleException $ex)
|
catch (ClientErrorResponseException $ex)
|
||||||
{
|
{
|
||||||
// Don't worry if the file no longer exists
|
// Don't worry if the file no longer exists
|
||||||
Log::warning('Failed deleting image from S3.', ['error' => $ex->getMessage(), 'path' => $photoPath]);
|
Log::warning('Failed deleting image from S3.', ['error' => $ex->getMessage(), 'path' => $photoPath]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Downloads a photo to be analysed from the storage source to a temporary file
|
|
||||||
* @param string $queueToken Queue token holding the photo
|
|
||||||
* @param string $fileName Filename of the photo to download
|
|
||||||
* @return string Path to the photo that was downloaded
|
|
||||||
*/
|
|
||||||
public function fetchItemFromAnalysisQueue($queueToken, $fileName)
|
|
||||||
{
|
|
||||||
$tempFile = tempnam(sys_get_temp_dir(), 'BlueTwilight_');
|
|
||||||
|
|
||||||
$this->getClient()->getObject([
|
|
||||||
'Bucket' => $this->configuration->container_name,
|
|
||||||
'Key' => $this->getPathToAnalysisQueueItem($queueToken, $fileName),
|
|
||||||
'SaveAs' => $tempFile
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $tempFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the contents of a thumbnail for a photo.
|
* Fetches the contents of a thumbnail for a photo.
|
||||||
* @param Photo $photo Photo to fetch the thumbnail for.
|
* @param Photo $photo Photo to fetch the thumbnail for.
|
||||||
* @param string $thumbnail Thumbnail to fetch (or null to fetch the original.)
|
* @param string $thumbnail Thumbnail to fetch (or null to fetch the original.)
|
||||||
* @return Stream
|
* @return EntityBody
|
||||||
*/
|
*/
|
||||||
public function fetchPhotoContent(Photo $photo, $thumbnail = null)
|
public function fetchPhotoContent(Photo $photo, $thumbnail = null)
|
||||||
{
|
{
|
||||||
@ -106,7 +61,7 @@ class AmazonS3Source extends AlbumSourceBase implements IAlbumSource, IAnalysisQ
|
|||||||
'SaveAs' => $tempFile
|
'SaveAs' => $tempFile
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return stream_for(fopen($tempFile, 'r+'));
|
return EntityBody::factory(fopen($tempFile, 'r+'));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -131,19 +86,7 @@ class AmazonS3Source extends AlbumSourceBase implements IAlbumSource, IAnalysisQ
|
|||||||
*/
|
*/
|
||||||
public function getUrlToPhoto(Photo $photo, $thumbnail = null)
|
public function getUrlToPhoto(Photo $photo, $thumbnail = null)
|
||||||
{
|
{
|
||||||
$client = $this->getClient();
|
return $this->getClient()->getObjectUrl($this->configuration->container_name, $this->getPathToPhoto($photo, $thumbnail));
|
||||||
|
|
||||||
if ($this->configuration->s3_signed_urls)
|
|
||||||
{
|
|
||||||
$cmd = $client->getCommand('GetObject', [
|
|
||||||
'Bucket' => $this->configuration->container_name,
|
|
||||||
'Key' => $this->getPathToPhoto($photo, $thumbnail)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (string)$client->createPresignedRequest($cmd, '+5 minutes')->getUri();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $client->getObjectUrl($this->configuration->container_name, $this->getPathToPhoto($photo, $thumbnail));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,33 +100,7 @@ class AmazonS3Source extends AlbumSourceBase implements IAlbumSource, IAnalysisQ
|
|||||||
{
|
{
|
||||||
$photoPath = $this->getPathToPhoto($photo, $thumbnail);
|
$photoPath = $this->getPathToPhoto($photo, $thumbnail);
|
||||||
|
|
||||||
$uploadAcl = $this->configuration->s3_signed_urls
|
$this->getClient()->upload($this->configuration->container_name, $photoPath, fopen($tempFilename, 'r+'), 'public-read');
|
||||||
? 'private'
|
|
||||||
: 'public-read';
|
|
||||||
|
|
||||||
$this->getClient()->upload($this->configuration->container_name, $photoPath, fopen($tempFilename, 'r+'), $uploadAcl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uploads a new file to the analysis queue specified by queue token.
|
|
||||||
*
|
|
||||||
* @param string $sourceFilePath Path to the file to upload to the analysis queue
|
|
||||||
* @param string $queueToken Queue token to hold the photo
|
|
||||||
* @param string $overrideFilename Use a specific filename, or false to set a specific name
|
|
||||||
* @return string Path to the file
|
|
||||||
*/
|
|
||||||
public function uploadToAnalysisQueue($sourceFilePath, $queueToken, $overrideFilename = null)
|
|
||||||
{
|
|
||||||
$targetPath = sprintf(
|
|
||||||
'%s/%s',
|
|
||||||
$this->getPathToAnalysisQueue($queueToken),
|
|
||||||
is_null($overrideFilename) ? MiscHelper::randomString(20) : $overrideFilename
|
|
||||||
);
|
|
||||||
|
|
||||||
// Note: we don't use "public-read" here as it will not be publicly-accessible, and will be retrieved by an authenticated client
|
|
||||||
$this->getClient()->upload($this->configuration->container_name, $targetPath, fopen($sourceFilePath, 'r+'));
|
|
||||||
|
|
||||||
return $targetPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getClient()
|
private function getClient()
|
||||||
@ -210,16 +127,6 @@ class AmazonS3Source extends AlbumSourceBase implements IAlbumSource, IAnalysisQ
|
|||||||
return '_originals';
|
return '_originals';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getPathToAnalysisQueue($queueToken)
|
|
||||||
{
|
|
||||||
return sprintf('analysis-queue/%s', $queueToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getPathToAnalysisQueueItem($queueToken, $fileName)
|
|
||||||
{
|
|
||||||
return sprintf('%s/%s', $this->getPathToAnalysisQueue($queueToken), $fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getPathToPhoto(Photo $photo, $thumbnail = null)
|
private function getPathToPhoto(Photo $photo, $thumbnail = null)
|
||||||
{
|
{
|
||||||
return sprintf(
|
return sprintf(
|
||||||
|
@ -1,247 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\AlbumSources;
|
|
||||||
|
|
||||||
use App\BackblazeB2FileIdCache;
|
|
||||||
use App\Photo;
|
|
||||||
use App\Services\BackblazeB2Service;
|
|
||||||
use App\Storage;
|
|
||||||
use GuzzleHttp\Psr7\Stream;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use function GuzzleHttp\Psr7\stream_for;
|
|
||||||
|
|
||||||
class BackblazeB2Source extends AlbumSourceBase implements IAlbumSource
|
|
||||||
{
|
|
||||||
const BUCKET_TYPE_AUTO = 0;
|
|
||||||
const BUCKET_TYPE_PRIVATE = 1;
|
|
||||||
const BUCKET_TYPE_PUBLIC = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var BackblazeB2Service
|
|
||||||
*/
|
|
||||||
private $backblaze;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of bucket which determines what type of URLs to generate to images.
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
private $bucketType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Token used to download files from a private bucket.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $downloadToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes an entire album's media contents.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function deleteAlbumContents()
|
|
||||||
{
|
|
||||||
// No need to do anything for the album container - once the files are gone, the virtual folder is also gone
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a thumbnail file for a photo.
|
|
||||||
* @param Photo $photo Photo to delete the thumbnail from.
|
|
||||||
* @param string $thumbnail Thumbnail to delete (or null to delete the original.)
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function deleteThumbnail(Photo $photo, $thumbnail = null)
|
|
||||||
{
|
|
||||||
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
||||||
|
|
||||||
// Create or update our cache record
|
|
||||||
|
|
||||||
$b2Cache = $this->getB2FileFromCache($pathOnStorage);
|
|
||||||
if (is_null($b2Cache))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->getClient()->deleteFile($b2Cache->b2_file_id, $pathOnStorage);
|
|
||||||
$b2Cache->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 Stream
|
|
||||||
*/
|
|
||||||
public function fetchPhotoContent(Photo $photo, $thumbnail = null)
|
|
||||||
{
|
|
||||||
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
||||||
|
|
||||||
// First we need the file ID
|
|
||||||
|
|
||||||
$b2Cache = $this->getB2FileFromCache($pathOnStorage);
|
|
||||||
if (is_null($b2Cache))
|
|
||||||
{
|
|
||||||
return stream_for('');
|
|
||||||
}
|
|
||||||
|
|
||||||
return stream_for($this->getClient()->downloadFile($b2Cache->b2_file_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of this album source.
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName()
|
|
||||||
{
|
|
||||||
return 'global.album_sources.backblaze_b2';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the absolute URL to the given photo file.
|
|
||||||
* @param Photo $photo Photo to get the URL to.
|
|
||||||
* @param string $thumbnail Thumbnail to get the image to.
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getUrlToPhoto(Photo $photo, $thumbnail = null)
|
|
||||||
{
|
|
||||||
$client = $this->getClient();
|
|
||||||
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
||||||
|
|
||||||
switch ($this->bucketType)
|
|
||||||
{
|
|
||||||
case self::BUCKET_TYPE_PRIVATE:
|
|
||||||
if (is_null($this->downloadToken))
|
|
||||||
{
|
|
||||||
$this->downloadToken = $client->getDownloadAuthToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once I sort out the issue with b2_download_file_by_id, this line can be removed
|
|
||||||
return sprintf('%s/file/%s/%s?Authorization=%s', $client->getDownloadUrl(), $this->configuration->container_name, $pathOnStorage, $this->downloadToken);
|
|
||||||
|
|
||||||
$b2Cache = $this->getB2FileFromCache($pathOnStorage);
|
|
||||||
if (is_null($b2Cache))
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return sprintf('%s/b2api/v2/b2_download_file_by_id?fileId=%s&Authorization=%s', $client->getDownloadUrl(), urlencode($b2Cache->b2_file_id), urlencode($this->downloadToken));
|
|
||||||
|
|
||||||
case self::BUCKET_TYPE_PUBLIC:
|
|
||||||
/*
|
|
||||||
* From https://www.backblaze.com/b2/docs/b2_download_file_by_name.html:
|
|
||||||
* The base URL to use comes from the b2_authorize_account call, and looks something like
|
|
||||||
* https://f345.backblazeb2.com. The "f" in the URL stands for "file", and the number is the cluster
|
|
||||||
* number containing your account. To this base, you add "file/", your bucket name, a "/", and then the
|
|
||||||
* name of the file. The file name may itself include more "/" characters.
|
|
||||||
*/
|
|
||||||
return sprintf('%s/file/%s/%s', $client->getDownloadUrl(), $this->configuration->container_name, $pathOnStorage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves a generated thumbnail to its permanent location.
|
|
||||||
* @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 = null)
|
|
||||||
{
|
|
||||||
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
||||||
|
|
||||||
$b2Cache = $this->getB2FileFromCache($pathOnStorage);
|
|
||||||
if (!is_null($b2Cache))
|
|
||||||
{
|
|
||||||
// Delete the current file version if we're replacing a file that already exists
|
|
||||||
$this->getClient()->deleteFile($b2Cache->b2_file_id, $pathOnStorage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload the file to B2
|
|
||||||
$b2FileID = $this->getClient()->uploadFile($tempFilename, $pathOnStorage);
|
|
||||||
|
|
||||||
// Create or update our cache record
|
|
||||||
if (is_null($b2Cache))
|
|
||||||
{
|
|
||||||
$b2Cache = new BackblazeB2FileIdCache([
|
|
||||||
'photo_id' => $photo->id,
|
|
||||||
'storage_path' => $pathOnStorage,
|
|
||||||
'b2_file_id' => $b2FileID
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$b2Cache->b2_file_id = $b2FileID;
|
|
||||||
}
|
|
||||||
|
|
||||||
$b2Cache->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setConfiguration(Storage $configuration)
|
|
||||||
{
|
|
||||||
parent::setConfiguration($configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $pathOnStorage
|
|
||||||
* @return BackblazeB2FileIdCache|null
|
|
||||||
*/
|
|
||||||
private function getB2FileFromCache($pathOnStorage)
|
|
||||||
{
|
|
||||||
$b2Cache = BackblazeB2FileIdCache::where('storage_path', $pathOnStorage)->first();
|
|
||||||
if (is_null($b2Cache))
|
|
||||||
{
|
|
||||||
// TODO: lookup the file on B2 to get the file ID
|
|
||||||
Log::warning(sprintf('B2 file ID not found in cache: %s', $pathOnStorage));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $b2Cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getClient()
|
|
||||||
{
|
|
||||||
if (is_null($this->backblaze))
|
|
||||||
{
|
|
||||||
$this->backblaze = new BackblazeB2Service();
|
|
||||||
$this->backblaze->setCredentials(decrypt($this->configuration->access_key), decrypt($this->configuration->secret_key));
|
|
||||||
$this->backblaze->authorizeAccount();
|
|
||||||
$this->backblaze->setBucketName($this->configuration->container_name);
|
|
||||||
|
|
||||||
if (intval($this->configuration->b2_bucket_type) == self::BUCKET_TYPE_AUTO)
|
|
||||||
{
|
|
||||||
/* Auto-detect the type of bucket in use on B2 */
|
|
||||||
|
|
||||||
switch ($this->backblaze->getBucketType())
|
|
||||||
{
|
|
||||||
case 'allPrivate':
|
|
||||||
$this->configuration->b2_bucket_type = self::BUCKET_TYPE_PRIVATE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'allPublic':
|
|
||||||
$this->configuration->b2_bucket_type = self::BUCKET_TYPE_PUBLIC;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->configuration->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the bucket type
|
|
||||||
$this->bucketType = $this->configuration->b2_bucket_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->backblaze;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\AlbumSources;
|
|
||||||
|
|
||||||
use App\Photo;
|
|
||||||
use App\Services\DropboxService;
|
|
||||||
use GuzzleHttp\Psr7\Stream;
|
|
||||||
use function GuzzleHttp\Psr7\stream_for;
|
|
||||||
|
|
||||||
class DropboxSource extends AlbumSourceBase implements IAlbumSource
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var DropboxService
|
|
||||||
*/
|
|
||||||
private $dropboxClient;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes an entire album's media contents.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function deleteAlbumContents()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$albumPathOnStorage = sprintf('/%s', $this->album->url_alias);
|
|
||||||
|
|
||||||
$this->getClient()->deleteFile($albumPathOnStorage);
|
|
||||||
}
|
|
||||||
catch (\Exception $ex)
|
|
||||||
{
|
|
||||||
// Don't worry too much if the delete fails - the file may have been removed on Dropbox itself
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a thumbnail file for a photo.
|
|
||||||
* @param Photo $photo Photo to delete the thumbnail from.
|
|
||||||
* @param string $thumbnail Thumbnail to delete (or null to delete the original.)
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function deleteThumbnail(Photo $photo, $thumbnail = null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
||||||
|
|
||||||
$this->getClient()->deleteFile($pathOnStorage);
|
|
||||||
}
|
|
||||||
catch (\Exception $ex)
|
|
||||||
{
|
|
||||||
// Don't worry too much if the delete fails - the file may have been removed on Dropbox itself
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 Stream
|
|
||||||
*/
|
|
||||||
public function fetchPhotoContent(Photo $photo, $thumbnail = null)
|
|
||||||
{
|
|
||||||
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
||||||
|
|
||||||
return stream_for($this->getClient()->downloadFile($pathOnStorage));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of this album source.
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName()
|
|
||||||
{
|
|
||||||
return 'global.album_sources.dropbox';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the absolute URL to the given photo file.
|
|
||||||
* @param Photo $photo Photo to get the URL to.
|
|
||||||
* @param string $thumbnail Thumbnail to get the image to.
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getUrlToPhoto(Photo $photo, $thumbnail = null)
|
|
||||||
{
|
|
||||||
$photoUrl = route('downloadPhoto', [
|
|
||||||
'albumUrlAlias' => $this->album->url_path,
|
|
||||||
'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 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 = null)
|
|
||||||
{
|
|
||||||
$pathOnStorage = $this->getPathToPhoto($photo, $thumbnail);
|
|
||||||
|
|
||||||
$this->getClient()->uploadFile($tempFilename, $pathOnStorage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getClient()
|
|
||||||
{
|
|
||||||
if (is_null($this->dropboxClient))
|
|
||||||
{
|
|
||||||
$this->dropboxClient = new DropboxService();
|
|
||||||
$this->dropboxClient->setAccessToken(decrypt($this->configuration->access_token));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->dropboxClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,8 @@ namespace App\AlbumSources;
|
|||||||
use App\Album;
|
use App\Album;
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
use App\Storage;
|
use App\Storage;
|
||||||
use GuzzleHttp\Psr7\Stream;
|
use Guzzle\Http\EntityBody;
|
||||||
|
use Symfony\Component\HttpFoundation\File\File;
|
||||||
|
|
||||||
interface IAlbumSource
|
interface IAlbumSource
|
||||||
{
|
{
|
||||||
@ -27,16 +28,10 @@ interface IAlbumSource
|
|||||||
* Fetches the contents of a thumbnail for a photo.
|
* Fetches the contents of a thumbnail for a photo.
|
||||||
* @param Photo $photo Photo to fetch the thumbnail for.
|
* @param Photo $photo Photo to fetch the thumbnail for.
|
||||||
* @param string $thumbnail Thumbnail to fetch (or null to fetch the original.)
|
* @param string $thumbnail Thumbnail to fetch (or null to fetch the original.)
|
||||||
* @return Stream
|
* @return EntityBody
|
||||||
*/
|
*/
|
||||||
function fetchPhotoContent(Photo $photo, $thumbnail = null);
|
function fetchPhotoContent(Photo $photo, $thumbnail = null);
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the configuration of the source.
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
function getConfiguration();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the name of this album source.
|
* Gets the name of this album source.
|
||||||
* @return string
|
* @return string
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\AlbumSources;
|
|
||||||
|
|
||||||
use App\Storage;
|
|
||||||
|
|
||||||
interface IAnalysisQueueSource
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Deletes a photo to be analysed from the storage source
|
|
||||||
* @param string $queueToken Queue token holding the photo
|
|
||||||
* @param string $fileName Filename of the photo to download
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function deleteItemFromAnalysisQueue($queueToken, $fileName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Downloads a photo to be analysed from the storage source to a temporary file
|
|
||||||
* @param string $queueToken Queue token holding the photo
|
|
||||||
* @param string $fileName Filename of the photo to download
|
|
||||||
* @return string Path to the photo that was downloaded
|
|
||||||
*/
|
|
||||||
function fetchItemFromAnalysisQueue($queueToken, $fileName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Storage $configuration
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
function setConfiguration(Storage $configuration);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uploads a new file to the analysis queue specified by queue token.
|
|
||||||
*
|
|
||||||
* @param string $sourceFilePath Path to the file to upload to the analysis queue
|
|
||||||
* @param string $queueToken Queue token to hold the photo
|
|
||||||
* @param string $overrideFilename Use a specific filename, or false to set a specific name
|
|
||||||
* @return string Path to the file
|
|
||||||
*/
|
|
||||||
function uploadToAnalysisQueue($sourceFilePath, $queueToken, $overrideFilename = null);
|
|
||||||
}
|
|
@ -2,18 +2,18 @@
|
|||||||
|
|
||||||
namespace App\AlbumSources;
|
namespace App\AlbumSources;
|
||||||
|
|
||||||
use App\Helpers\FileHelper;
|
use App\Album;
|
||||||
use App\Helpers\MiscHelper;
|
use App\Helpers\MiscHelper;
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
use GuzzleHttp\Psr7\Stream;
|
use App\Services\PhotoService;
|
||||||
|
use Guzzle\Http\EntityBody;
|
||||||
use Symfony\Component\HttpFoundation\File\File;
|
use Symfony\Component\HttpFoundation\File\File;
|
||||||
use function GuzzleHttp\Psr7\stream_for;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Driver for managing files on the local filesystem.
|
* Driver for managing files on the local filesystem.
|
||||||
* @package App\AlbumSources
|
* @package App\AlbumSources
|
||||||
*/
|
*/
|
||||||
class LocalFilesystemSource extends AlbumSourceBase implements IAlbumSource, IAnalysisQueueSource
|
class LocalFilesystemSource extends AlbumSourceBase implements IAlbumSource
|
||||||
{
|
{
|
||||||
public function deleteAlbumContents()
|
public function deleteAlbumContents()
|
||||||
{
|
{
|
||||||
@ -38,7 +38,7 @@ class LocalFilesystemSource extends AlbumSourceBase implements IAlbumSource, IAn
|
|||||||
* Fetches the contents of a thumbnail for a photo.
|
* Fetches the contents of a thumbnail for a photo.
|
||||||
* @param Photo $photo Photo to fetch the thumbnail for.
|
* @param Photo $photo Photo to fetch the thumbnail for.
|
||||||
* @param string $thumbnail Thumbnail to fetch (or null to fetch the original.)
|
* @param string $thumbnail Thumbnail to fetch (or null to fetch the original.)
|
||||||
* @return Stream
|
* @return EntityBody
|
||||||
*/
|
*/
|
||||||
public function fetchPhotoContent(Photo $photo, $thumbnail = null)
|
public function fetchPhotoContent(Photo $photo, $thumbnail = null)
|
||||||
{
|
{
|
||||||
@ -51,7 +51,7 @@ class LocalFilesystemSource extends AlbumSourceBase implements IAlbumSource, IAn
|
|||||||
'r+'
|
'r+'
|
||||||
);
|
);
|
||||||
|
|
||||||
return stream_for($fh);
|
return EntityBody::factory($fh);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName()
|
public function getName()
|
||||||
@ -120,86 +120,4 @@ class LocalFilesystemSource extends AlbumSourceBase implements IAlbumSource, IAn
|
|||||||
|
|
||||||
rmdir($directory);
|
rmdir($directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a photo to be analysed from the storage source
|
|
||||||
* @param string $queueToken Queue token holding the photo
|
|
||||||
* @param string $fileName Filename of the photo to download
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function deleteItemFromAnalysisQueue($queueToken, $fileName)
|
|
||||||
{
|
|
||||||
$queueFolder = $this->getQueuePath($queueToken);
|
|
||||||
$filePath = $this->getQueueItemPath($queueToken, $fileName);
|
|
||||||
@unlink($filePath);
|
|
||||||
|
|
||||||
// Delete the parent folder if empty
|
|
||||||
FileHelper::deleteIfEmpty($queueFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Downloads a photo to be analysed from the storage source to a temporary file
|
|
||||||
* @param string $queueToken Queue token holding the photo
|
|
||||||
* @param string $fileName Filename of the photo to download
|
|
||||||
* @return string Path to the photo that was downloaded
|
|
||||||
*/
|
|
||||||
public function fetchItemFromAnalysisQueue($queueToken, $fileName)
|
|
||||||
{
|
|
||||||
// Don't actually need to download anything as it's already local
|
|
||||||
return $this->getQueueItemPath($queueToken, $fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uploads a new file to the analysis queue specified by queue token.
|
|
||||||
*
|
|
||||||
* @param string $sourceFilePath Path to the file to upload to the analysis queue
|
|
||||||
* @param string $queueToken Queue token to hold the photo
|
|
||||||
* @param string $overrideFilename Use a specific filename, or false to set a specific name
|
|
||||||
* @return string Path to the file
|
|
||||||
*/
|
|
||||||
public function uploadToAnalysisQueue($sourceFilePath, $queueToken, $overrideFilename = null)
|
|
||||||
{
|
|
||||||
$uploadedFile = new File($sourceFilePath);
|
|
||||||
|
|
||||||
$tempFilename = $this->getQueueItemPath(
|
|
||||||
$queueToken,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
copy($uploadedFile->getRealPath(), $tempFilename);
|
|
||||||
return $tempFilename;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getQueueItemPath($queueToken, $fileName)
|
|
||||||
{
|
|
||||||
return join(DIRECTORY_SEPARATOR, [$this->getQueuePath($queueToken), $fileName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getQueuePath($queueUid)
|
|
||||||
{
|
|
||||||
$path = join(DIRECTORY_SEPARATOR, [
|
|
||||||
dirname(dirname(__DIR__)),
|
|
||||||
'storage',
|
|
||||||
'app',
|
|
||||||
'analysis-queue',
|
|
||||||
str_replace(['.', '/', '\\'], '', $queueUid)
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!file_exists($path))
|
|
||||||
{
|
|
||||||
@mkdir($path, 0755, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -3,16 +3,12 @@
|
|||||||
namespace App\AlbumSources;
|
namespace App\AlbumSources;
|
||||||
|
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
use GuzzleHttp\Client;
|
use Guzzle\Http\EntityBody;
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
use Guzzle\Http\Exception\ClientErrorResponseException;
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Stream;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use OpenStack\Common\Error\BadResponseError;
|
use OpenCloud\ObjectStore\Exception\ObjectNotFoundException;
|
||||||
use OpenStack\Common\Transport\Utils as TransportUtils;
|
use OpenCloud\OpenStack;
|
||||||
use OpenStack\Identity\v2\Service as IdentityV2Service;
|
use Symfony\Component\HttpFoundation\File\File;
|
||||||
use OpenStack\OpenStack;
|
|
||||||
use function GuzzleHttp\Psr7\stream_for;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Driver for managing files on an OpenStack Keystone+Swift compatible platform.
|
* Driver for managing files on an OpenStack Keystone+Swift compatible platform.
|
||||||
@ -42,14 +38,9 @@ class OpenStackSource extends AlbumSourceBase implements IAlbumSource
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$this->getContainer()->getObject($photoPath)->delete();
|
$this->getContainer()->deleteObject($photoPath);
|
||||||
}
|
}
|
||||||
catch (GuzzleException $ex)
|
catch (ClientErrorResponseException $ex)
|
||||||
{
|
|
||||||
// Don't worry if the file no longer exists
|
|
||||||
Log::warning('Failed deleting image from OpenStack.', ['error' => $ex->getMessage(), 'path' => $photoPath]);
|
|
||||||
}
|
|
||||||
catch (BadResponseError $ex)
|
|
||||||
{
|
{
|
||||||
// Don't worry if the file no longer exists
|
// Don't worry if the file no longer exists
|
||||||
Log::warning('Failed deleting image from OpenStack.', ['error' => $ex->getMessage(), 'path' => $photoPath]);
|
Log::warning('Failed deleting image from OpenStack.', ['error' => $ex->getMessage(), 'path' => $photoPath]);
|
||||||
@ -60,13 +51,13 @@ class OpenStackSource extends AlbumSourceBase implements IAlbumSource
|
|||||||
* Fetches the contents of a thumbnail for a photo.
|
* Fetches the contents of a thumbnail for a photo.
|
||||||
* @param Photo $photo Photo to fetch the thumbnail for.
|
* @param Photo $photo Photo to fetch the thumbnail for.
|
||||||
* @param string $thumbnail Thumbnail to fetch (or null to fetch the original.)
|
* @param string $thumbnail Thumbnail to fetch (or null to fetch the original.)
|
||||||
* @return Stream
|
* @return EntityBody
|
||||||
*/
|
*/
|
||||||
public function fetchPhotoContent(Photo $photo, $thumbnail = null)
|
public function fetchPhotoContent(Photo $photo, $thumbnail = null)
|
||||||
{
|
{
|
||||||
$object = $this->getContainer()->getObject($this->getPathToPhoto($photo, $thumbnail));
|
$object = $this->getContainer()->getObject($this->getPathToPhoto($photo, $thumbnail));
|
||||||
|
|
||||||
return stream_for($object->download());
|
return $object->getContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,33 +113,17 @@ class OpenStackSource extends AlbumSourceBase implements IAlbumSource
|
|||||||
{
|
{
|
||||||
$photoPath = $this->getPathToPhoto($photo, $thumbnail);
|
$photoPath = $this->getPathToPhoto($photo, $thumbnail);
|
||||||
|
|
||||||
$createOptions = [
|
$container = $this->getContainer();
|
||||||
'name' => $photoPath,
|
$container->uploadObject($photoPath, fopen($tempFilename, 'r+'));
|
||||||
'content' => file_get_contents($tempFilename)
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->getContainer()->createObject($createOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getClient()
|
protected function getClient()
|
||||||
{
|
{
|
||||||
$authURL = $this->configuration->auth_url;
|
return new OpenStack($this->configuration->auth_url, [
|
||||||
|
|
||||||
$options = [
|
|
||||||
'authUrl' => $authURL,
|
|
||||||
'username' => $this->configuration->username,
|
'username' => $this->configuration->username,
|
||||||
'password' => decrypt($this->configuration->password),
|
'password' => decrypt($this->configuration->password),
|
||||||
'tenantName' => $this->configuration->tenant_name,
|
'tenantName' => $this->configuration->tenant_name,
|
||||||
'region' => $this->configuration->service_region,
|
]);
|
||||||
'identityService' => IdentityV2Service::factory(
|
|
||||||
new Client([
|
|
||||||
'base_uri' => TransportUtils::normalizeUrl($authURL),
|
|
||||||
'handler' => HandlerStack::create(),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
];
|
|
||||||
|
|
||||||
return new OpenStack($options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getContainer()
|
protected function getContainer()
|
||||||
@ -158,14 +133,11 @@ class OpenStackSource extends AlbumSourceBase implements IAlbumSource
|
|||||||
|
|
||||||
protected function getStorageService()
|
protected function getStorageService()
|
||||||
{
|
{
|
||||||
return $this->getClient()->objectStoreV1([
|
return $this->getClient()->objectStoreService(
|
||||||
'catalogName' => $this->getStorageServiceCatalogName()
|
$this->configuration->service_name,
|
||||||
]);
|
$this->configuration->service_region,
|
||||||
}
|
'publicURL'
|
||||||
|
);
|
||||||
protected function getStorageServiceCatalogName()
|
|
||||||
{
|
|
||||||
return $this->configuration->service_name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getOriginalsFolder()
|
protected function getOriginalsFolder()
|
||||||
|
@ -3,15 +3,26 @@
|
|||||||
namespace App\AlbumSources;
|
namespace App\AlbumSources;
|
||||||
|
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
use App\Services\Rackspace\Identity\v2\Service as RackspaceIdentityV2Service;
|
use App\Storage;
|
||||||
use App\Services\Rackspace\ObjectStoreCdn\v1\Models\Container;
|
use OpenCloud\Rackspace;
|
||||||
use App\Services\Rackspace\Rackspace;
|
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use OpenStack\Common\Transport\Utils as TransportUtils;
|
|
||||||
|
|
||||||
class RackspaceSource extends OpenStackSource
|
class RackspaceSource extends OpenStackSource
|
||||||
{
|
{
|
||||||
|
protected function getClient()
|
||||||
|
{
|
||||||
|
$endpoint = Rackspace::US_IDENTITY_ENDPOINT;
|
||||||
|
|
||||||
|
if ($this->configuration->service_region == 'LON')
|
||||||
|
{
|
||||||
|
$endpoint = Rackspace::UK_IDENTITY_ENDPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Rackspace($endpoint, [
|
||||||
|
'username' => $this->configuration->username,
|
||||||
|
'apiKey' => decrypt($this->configuration->password)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the name of this album source.
|
* Gets the name of this album source.
|
||||||
* @return string
|
* @return string
|
||||||
@ -30,14 +41,12 @@ class RackspaceSource extends OpenStackSource
|
|||||||
public function getUrlToPhoto(Photo $photo, $thumbnail = null)
|
public function getUrlToPhoto(Photo $photo, $thumbnail = null)
|
||||||
{
|
{
|
||||||
$isCdnEnabled = false;
|
$isCdnEnabled = false;
|
||||||
$cdnService = $this->getCdnService();
|
$cdnService = $this->getStorageService()->getCdnService();
|
||||||
|
|
||||||
$thisCdnContainer = null;
|
$thisCdnContainer = null;
|
||||||
|
|
||||||
/** @var Container $cdnContainer */
|
|
||||||
foreach ($cdnService->listContainers() as $cdnContainer)
|
foreach ($cdnService->listContainers() as $cdnContainer)
|
||||||
{
|
{
|
||||||
if ($cdnContainer->cdn_enabled && strtolower($cdnContainer->name) == strtolower($this->configuration->container_name))
|
if ($cdnContainer->name == $this->configuration->container_name)
|
||||||
{
|
{
|
||||||
$isCdnEnabled = true;
|
$isCdnEnabled = true;
|
||||||
$thisCdnContainer = $cdnContainer;
|
$thisCdnContainer = $cdnContainer;
|
||||||
@ -46,47 +55,9 @@ class RackspaceSource extends OpenStackSource
|
|||||||
|
|
||||||
if ($isCdnEnabled)
|
if ($isCdnEnabled)
|
||||||
{
|
{
|
||||||
return sprintf('%s/%s', $thisCdnContainer->cdn_ssl_uri, $this->getPathToPhoto($photo, $thumbnail));
|
return sprintf('%s/%s', $thisCdnContainer->getCdnSslUri(), $this->getPathToPhoto($photo, $thumbnail));
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::getPathToPhoto($photo, $thumbnail);
|
return parent::getPathToPhoto($photo, $thumbnail);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getCdnService()
|
|
||||||
{
|
|
||||||
return $this->getClient()->objectStoreCdnV1();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getClient()
|
|
||||||
{
|
|
||||||
$authURL = config('services.rackspace.authentication_url');
|
|
||||||
|
|
||||||
// Uncomment the commented out lines below and in the $options array to get a 'storage/logs/openstack.log' file
|
|
||||||
// with passed HTTP traffic
|
|
||||||
//$logger = new Logger('MyLog');
|
|
||||||
//$logger->pushHandler(new StreamHandler(__DIR__ . '/../../storage/logs/openstack.log'), Logger::DEBUG);
|
|
||||||
|
|
||||||
$options = [
|
|
||||||
'authUrl' => $authURL,
|
|
||||||
'username' => $this->configuration->username,
|
|
||||||
'apiKey' => decrypt($this->configuration->password),
|
|
||||||
'region' => $this->configuration->service_region,
|
|
||||||
'identityService' => RackspaceIdentityV2Service::factory(
|
|
||||||
new Client([
|
|
||||||
'base_uri' => TransportUtils::normalizeUrl($authURL),
|
|
||||||
'handler' => HandlerStack::create(),
|
|
||||||
])
|
|
||||||
),
|
|
||||||
//'debugLog' => true,
|
|
||||||
//'logger' => $logger,
|
|
||||||
//'messageFormatter' => new MessageFormatter('{req_body} - {res_body}')
|
|
||||||
];
|
|
||||||
|
|
||||||
return new Rackspace($options);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getStorageServiceCatalogName()
|
|
||||||
{
|
|
||||||
return 'cloudFiles';
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class BackblazeB2FileIdCache extends Model
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $fillable = [
|
|
||||||
'photo_id',
|
|
||||||
'storage_path',
|
|
||||||
'b2_file_id'
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,274 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Photo;
|
|
||||||
use App\QueueItem;
|
|
||||||
use App\Services\PhotoService;
|
|
||||||
use App\Services\RabbitMQService;
|
|
||||||
use App\User;
|
|
||||||
use App\UserActivity;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class ProcessQueueCommand extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'bt-queue:process';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Processes items in the processing queue.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
if (!UserConfig::isImageProcessingQueueEnabled())
|
|
||||||
{
|
|
||||||
$this->output->error('The image processing queue is not enabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
$rabbitmq = new RabbitMQService();
|
|
||||||
|
|
||||||
$this->output->writeln('Monitoring queue');
|
|
||||||
|
|
||||||
$rabbitmq->waitOnQueue([$this, 'processQueueItem']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes a single item from the queue.
|
|
||||||
*
|
|
||||||
* @param $msg
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function processQueueItem($msg)
|
|
||||||
{
|
|
||||||
$queueItemID = intval($msg->body);
|
|
||||||
|
|
||||||
$this->output->writeln(sprintf('Processing queue item %d', $queueItemID));
|
|
||||||
|
|
||||||
/** @var QueueItem $queueItem */
|
|
||||||
$queueItem = QueueItem::where('id', $queueItemID)->first();
|
|
||||||
if (is_null($queueItem))
|
|
||||||
{
|
|
||||||
$this->output->writeln('Queue item does not exist; skipping');
|
|
||||||
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
switch (strtolower($queueItem->action_type))
|
|
||||||
{
|
|
||||||
case 'photo.analyse':
|
|
||||||
$this->processPhotoAnalyseMessage($queueItem);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'photo.bulk_action.change_album':
|
|
||||||
case 'photo.bulk_action.delete':
|
|
||||||
case 'photo.bulk_action.flip_both':
|
|
||||||
case 'photo.bulk_action.flip_horizontal':
|
|
||||||
case 'photo.bulk_action.flip_vertical':
|
|
||||||
case 'photo.bulk_action.refresh_thumbnails':
|
|
||||||
case 'photo.bulk_action.rotate_left':
|
|
||||||
case 'photo.bulk_action.rotate_right':
|
|
||||||
$this->processPhotoBulkActionMessage($queueItem);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$this->output->writeln(sprintf('Action %s is not recognised, skipping', $queueItem->action_type));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$queueItem->completed_at = new \DateTime();
|
|
||||||
$queueItem->save();
|
|
||||||
}
|
|
||||||
catch (\Exception $ex)
|
|
||||||
{
|
|
||||||
$this->output->error($ex->getMessage());
|
|
||||||
$queueItem->error_message = $ex->getMessage();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
$queueItem->completed_at = new \DateTime();
|
|
||||||
$queueItem->save();
|
|
||||||
|
|
||||||
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createActivityRecord(Photo $photo, $type, $userID, $activityDateTime = null)
|
|
||||||
{
|
|
||||||
if (is_null($activityDateTime))
|
|
||||||
{
|
|
||||||
$activityDateTime = new \DateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
$userActivity = new UserActivity();
|
|
||||||
$userActivity->user_id = $userID;
|
|
||||||
$userActivity->activity_at = $activityDateTime;
|
|
||||||
$userActivity->type = $type;
|
|
||||||
$userActivity->photo_id = $photo->id;
|
|
||||||
$userActivity->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processPhotoAnalyseMessage(QueueItem $queueItem)
|
|
||||||
{
|
|
||||||
$this->output->writeln(sprintf('Analysing photo ID %l (batch: %s)', $queueItem->photo_id, $queueItem->batch_reference));
|
|
||||||
|
|
||||||
/** @var Photo $photo */
|
|
||||||
$photo = $queueItem->photo;
|
|
||||||
if (is_null($photo))
|
|
||||||
{
|
|
||||||
$this->output->writeln('Photo does not exist; skipping');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IF CHANGING THIS LOGIC, ALSO CHECK PhotoController::analyse */
|
|
||||||
$photoService = new PhotoService($photo);
|
|
||||||
$photoService->analyse($queueItem->batch_reference);
|
|
||||||
|
|
||||||
// Log an activity record for the user's feed (remove an existing one as the date may have changed)
|
|
||||||
$this->removeExistingActivityRecords($photo, 'photo.taken');
|
|
||||||
|
|
||||||
if (!is_null($photo->taken_at))
|
|
||||||
{
|
|
||||||
// Log an activity record for the user's feed
|
|
||||||
$this->createActivityRecord($photo, 'photo.taken', $queueItem->user_id, $photo->taken_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
$queueItem->is_successful = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processPhotoBulkActionMessage(QueueItem $queueItem)
|
|
||||||
{
|
|
||||||
$action = str_replace('photo.bulk_action.', '', $queueItem->action_type);
|
|
||||||
$this->output->writeln(sprintf('Apply action \'%s\' to photo ID %l (batch: %s)', $action, $queueItem->photo_id, $queueItem->batch_reference));
|
|
||||||
|
|
||||||
/** @var Photo $photo */
|
|
||||||
$photo = $queueItem->photo;
|
|
||||||
if (is_null($photo))
|
|
||||||
{
|
|
||||||
$this->output->writeln('Photo does not exist; skipping');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var User $user */
|
|
||||||
$user = $queueItem->user;
|
|
||||||
if (is_null($user))
|
|
||||||
{
|
|
||||||
$this->output->writeln('User does not exist; skipping');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$photoService = new PhotoService($photo);
|
|
||||||
|
|
||||||
switch (strtolower($action))
|
|
||||||
{
|
|
||||||
/* This needs a bit more work - we need to also store the new album ID in the queue_items table */
|
|
||||||
case 'change_album':
|
|
||||||
if ($user->can('change-metadata', $photo))
|
|
||||||
{
|
|
||||||
$newAlbum = Album::where('id', intval($queueItem->new_album_id))->first();
|
|
||||||
if (is_null($newAlbum) || !$user->can('upload-photos', $newAlbum))
|
|
||||||
{
|
|
||||||
$this->output->writeln('Target album does not exist or user does not have permission.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->output->writeln(sprintf('Moving photo to album \'%s\'', $newAlbum->name));
|
|
||||||
$photoService->changeAlbum($newAlbum);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'delete':
|
|
||||||
if ($user->can('delete', $photo))
|
|
||||||
{
|
|
||||||
$photoService->delete();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'flip_both':
|
|
||||||
if ($user->can('manipulate', $photo))
|
|
||||||
{
|
|
||||||
$photoService->flip(true, true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'flip_horizontal':
|
|
||||||
if ($user->can('manipulate', $photo))
|
|
||||||
{
|
|
||||||
$photoService->flip(true, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'flip_vertical':
|
|
||||||
if ($user->can('manipulate', $photo))
|
|
||||||
{
|
|
||||||
$photoService->flip(false, true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'refresh_thumbnails':
|
|
||||||
if ($user->can('change-metadata', $photo))
|
|
||||||
{
|
|
||||||
$photoService->regenerateThumbnails();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'rotate_left':
|
|
||||||
if ($user->can('manipulate', $photo))
|
|
||||||
{
|
|
||||||
$photoService->rotate(90);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'rotate_right':
|
|
||||||
if ($user->can('manipulate', $photo))
|
|
||||||
{
|
|
||||||
$photoService->rotate(270);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$this->output->writeln(sprintf('Action \'%s\' not recognised; skipping'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function removeExistingActivityRecords(Photo $photo, $type)
|
|
||||||
{
|
|
||||||
$existingFeedRecords = UserActivity::where([
|
|
||||||
'user_id' => $photo->user_id,
|
|
||||||
'photo_id' => $photo->id,
|
|
||||||
'type' => $type
|
|
||||||
])->get();
|
|
||||||
|
|
||||||
foreach ($existingFeedRecords as $existingFeedRecord)
|
|
||||||
{
|
|
||||||
$existingFeedRecord->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\EmailLog;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Http\Middleware\GlobalConfiguration;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Mail\Message;
|
|
||||||
|
|
||||||
class SendEmailsCommand extends Command
|
|
||||||
{
|
|
||||||
private $maxPerBatch;
|
|
||||||
private $numberOfAttempts;
|
|
||||||
private $secondsBetweenPolls;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'bt-queue:send-emails {--poll}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Sends e-mails queued in the database.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
|
|
||||||
$this->maxPerBatch = config('mail.queue.emails_per_batch');
|
|
||||||
$this->numberOfAttempts = config('mail.queue.max_attempts');
|
|
||||||
$this->secondsBetweenPolls = config('mail.queue.seconds_between_polls');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
if (!UserConfig::get('queue_emails'))
|
|
||||||
{
|
|
||||||
$this->output->error('E-mail queueing is not enabled. E-mails are being sent immediately.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->output->writeln('Setting mail configuration');
|
|
||||||
|
|
||||||
GlobalConfiguration::updateMailConfig();
|
|
||||||
|
|
||||||
$this->output->writeln('E-mail queue runner started');
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
$emailsToSend = EmailLog::where([
|
|
||||||
['sent_at', null],
|
|
||||||
['number_attempts', '<', $this->numberOfAttempts]
|
|
||||||
])->limit($this->maxPerBatch)->get();
|
|
||||||
|
|
||||||
$this->output->writeln(sprintf(
|
|
||||||
'%d e-mail%s to send',
|
|
||||||
$emailsToSend->count(),
|
|
||||||
$emailsToSend->count() == 1 ? '' : 's'
|
|
||||||
));
|
|
||||||
|
|
||||||
/** @var EmailLog $emailToSend */
|
|
||||||
foreach ($emailsToSend as $emailToSend)
|
|
||||||
{
|
|
||||||
$this->sendEmail($emailToSend);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->option('poll'))
|
|
||||||
{
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
sleep($this->secondsBetweenPolls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sendEmail(EmailLog $emailLog)
|
|
||||||
{
|
|
||||||
$this->output->writeln(sprintf('Sending message with subject \'%s\'', $emailLog->subject));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
app('mailer')->send(
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
function (Message $message) use ($emailLog)
|
|
||||||
{
|
|
||||||
$message->setFrom($emailLog->sender_address, $emailLog->sender_name);
|
|
||||||
$message->setSubject($emailLog->subject);
|
|
||||||
|
|
||||||
$this->addAddresses($emailLog->to_addresses, $message, 'To');
|
|
||||||
$this->addAddresses($emailLog->cc_addresses, $message, 'Cc');
|
|
||||||
$this->addAddresses($emailLog->bcc_addresses, $message, 'Bcc');
|
|
||||||
|
|
||||||
$message->addPart($emailLog->body_plain, 'text/plain');
|
|
||||||
$message->setBody($emailLog->body_html, 'text/html');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
$emailLog->sent_at = new \DateTime();
|
|
||||||
|
|
||||||
$this->output->writeln('Send completed');
|
|
||||||
}
|
|
||||||
catch (\Exception $ex)
|
|
||||||
{
|
|
||||||
$this->output->error(sprintf('Send failed: %s', $ex->getMessage()));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
$emailLog->number_attempts++;
|
|
||||||
$emailLog->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addAddresses($dbFieldData, Message $message, $property)
|
|
||||||
{
|
|
||||||
$decoded = json_decode($dbFieldData);
|
|
||||||
|
|
||||||
if (is_array($decoded))
|
|
||||||
{
|
|
||||||
foreach ($decoded as $addressInfo)
|
|
||||||
{
|
|
||||||
$this->output->writeln(sprintf('Adding %s address: \'"%s" <%s>\'', $property, $addressInfo->name, $addressInfo->address));
|
|
||||||
|
|
||||||
$message->{"set{$property}"}($addressInfo->address, $addressInfo->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,6 @@ use App\Console\Commands\ProcessUploadCommand;
|
|||||||
use App\Console\Commands\RegenerateThumbnailsCommand;
|
use App\Console\Commands\RegenerateThumbnailsCommand;
|
||||||
use App\Upload;
|
use App\Upload;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
use Illuminate\Foundation\Application;
|
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
class Kernel extends ConsoleKernel
|
class Kernel extends ConsoleKernel
|
||||||
@ -37,11 +36,5 @@ class Kernel extends ConsoleKernel
|
|||||||
protected function commands()
|
protected function commands()
|
||||||
{
|
{
|
||||||
require base_path('routes/console.php');
|
require base_path('routes/console.php');
|
||||||
|
|
||||||
// We can only auto-load commands for Laravel 5.5.0 or above
|
|
||||||
if (version_compare(Application::VERSION, '5.5.0') >= 0)
|
|
||||||
{
|
|
||||||
$this->load(__DIR__.'/Commands');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class EmailLog extends Model
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $fillable = [
|
|
||||||
'sender_user_id',
|
|
||||||
'queued_at',
|
|
||||||
'sent_at',
|
|
||||||
'sender_name',
|
|
||||||
'sender_address',
|
|
||||||
'to_addresses',
|
|
||||||
'cc_addresses',
|
|
||||||
'subject',
|
|
||||||
'body_plain',
|
|
||||||
'body_html'
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Exceptions;
|
|
||||||
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
class BackblazeRetryException extends \Exception
|
|
||||||
{
|
|
||||||
private $innerException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getInnerException()
|
|
||||||
{
|
|
||||||
return $this->innerException;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct($httpCode, \Exception $innerException)
|
|
||||||
{
|
|
||||||
parent::__construct('Backblaze requested to retry the request');
|
|
||||||
|
|
||||||
$this->innerException = $innerException;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Exceptions;
|
|
||||||
|
|
||||||
class DropboxRetryException extends \Exception
|
|
||||||
{
|
|
||||||
private $innerException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getInnerException()
|
|
||||||
{
|
|
||||||
return $this->innerException;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct($httpCode, \Exception $innerException)
|
|
||||||
{
|
|
||||||
parent::__construct('Dropbox requested to retry the request');
|
|
||||||
|
|
||||||
$this->innerException = $innerException;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class ExternalService extends Model
|
|
||||||
{
|
|
||||||
public const DROPBOX = 'dropbox';
|
|
||||||
public const FACEBOOK = 'facebook';
|
|
||||||
public const GOOGLE = 'google';
|
|
||||||
public const TWITTER = 'twitter';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $fillable = ['name', 'service_type', 'app_id', 'app_secret'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all possible service configurations for the given service type.
|
|
||||||
* @param $serviceType
|
|
||||||
* @return ExternalService[]
|
|
||||||
*/
|
|
||||||
public static function getForService($serviceType)
|
|
||||||
{
|
|
||||||
return ExternalService::where('service_type', $serviceType)->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isDropbox()
|
|
||||||
{
|
|
||||||
// This logic must be mirrored in external_services.js
|
|
||||||
return $this->service_type == self::DROPBOX;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isFacebook()
|
|
||||||
{
|
|
||||||
// This logic must be mirrored in external_services.js
|
|
||||||
return $this->service_type == self::FACEBOOK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isGoogle()
|
|
||||||
{
|
|
||||||
// This logic must be mirrored in external_services.js
|
|
||||||
return $this->service_type == self::GOOGLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isTwitter()
|
|
||||||
{
|
|
||||||
// This logic must be mirrored in external_services.js
|
|
||||||
return $this->service_type == self::TWITTER;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Facade;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Facade;
|
|
||||||
|
|
||||||
class Misc extends Facade
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the registered name of the component.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected static function getFacadeAccessor()
|
|
||||||
{
|
|
||||||
return 'misc';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Helpers;
|
|
||||||
|
|
||||||
use App\AlbumSources\IAnalysisQueueSource;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Storage;
|
|
||||||
|
|
||||||
class AnalysisQueueHelper
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Gets the storage queue source in use.
|
|
||||||
* @return IAnalysisQueueSource
|
|
||||||
*/
|
|
||||||
public static function getStorageQueueSource()
|
|
||||||
{
|
|
||||||
$queueStorage = Storage::find(UserConfig::get('analysis_queue_storage_location'));
|
|
||||||
|
|
||||||
$queueSource = self::createStorageSource($queueStorage);
|
|
||||||
if (is_null($queueSource))
|
|
||||||
{
|
|
||||||
throw new \Exception(sprintf('Queue storage \'%s\' does not support the analysis queue', $queueStorage->name));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $queueSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a list of compatible storage sources for the analysis queue.
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getCompatibleStorages()
|
|
||||||
{
|
|
||||||
$storageSources = [];
|
|
||||||
|
|
||||||
foreach (Storage::where('is_active', true)->orderBy('name')->get() as $storage)
|
|
||||||
{
|
|
||||||
$queueSource = self::createStorageSource($storage);
|
|
||||||
|
|
||||||
if (is_null($queueSource))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$storageSources[$storage->id] = $storage->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $storageSources;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function createStorageSource(Storage $queueStorage)
|
|
||||||
{
|
|
||||||
$fullClassName = sprintf('App\AlbumSources\%s', $queueStorage->source);
|
|
||||||
|
|
||||||
/** @var IAnalysisQueueSource $source */
|
|
||||||
$source = new $fullClassName;
|
|
||||||
if (!$source instanceof IAnalysisQueueSource)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$source->setConfiguration($queueStorage);
|
|
||||||
|
|
||||||
return $source;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,21 +2,17 @@
|
|||||||
|
|
||||||
namespace App\Helpers;
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
use App\Album;
|
||||||
use App\AlbumSources\AmazonS3Source;
|
use App\AlbumSources\AmazonS3Source;
|
||||||
use App\AlbumSources\BackblazeB2Source;
|
|
||||||
use App\AlbumSources\DropboxSource;
|
|
||||||
use App\AlbumSources\IAlbumSource;
|
use App\AlbumSources\IAlbumSource;
|
||||||
use App\AlbumSources\LocalFilesystemSource;
|
use App\AlbumSources\LocalFilesystemSource;
|
||||||
use App\AlbumSources\OpenStackSource;
|
use App\AlbumSources\OpenStackSource;
|
||||||
|
use App\AlbumSources\OpenStackV1Source;
|
||||||
use App\AlbumSources\RackspaceSource;
|
use App\AlbumSources\RackspaceSource;
|
||||||
use App\Configuration;
|
use App\Configuration;
|
||||||
use App\Storage;
|
|
||||||
|
|
||||||
class ConfigHelper
|
class ConfigHelper
|
||||||
{
|
{
|
||||||
/** @var mixed Cache of configuration values */
|
|
||||||
private $cache;
|
|
||||||
|
|
||||||
public function allowedAlbumViews()
|
public function allowedAlbumViews()
|
||||||
{
|
{
|
||||||
return ['default', 'slideshow'];
|
return ['default', 'slideshow'];
|
||||||
@ -27,17 +23,8 @@ class ConfigHelper
|
|||||||
return [
|
return [
|
||||||
'Y-m-d - H:i',
|
'Y-m-d - H:i',
|
||||||
'd/m/Y - H:i',
|
'd/m/Y - H:i',
|
||||||
'd-m-Y - H:i',
|
|
||||||
'd.m.Y - H:i',
|
|
||||||
'd/M/Y - H:i',
|
|
||||||
'd-M-Y - H:i',
|
|
||||||
'm/d/Y - H:i',
|
'm/d/Y - H:i',
|
||||||
'm-d-Y - H:i',
|
|
||||||
'm.d.Y - H:i',
|
|
||||||
'jS F Y - H:i',
|
'jS F Y - H:i',
|
||||||
'jS M. Y - H:i',
|
|
||||||
'F jS Y - H:i',
|
|
||||||
'M. jS Y - H:i'
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +35,6 @@ class ConfigHelper
|
|||||||
$classes = [
|
$classes = [
|
||||||
LocalFilesystemSource::class,
|
LocalFilesystemSource::class,
|
||||||
AmazonS3Source::class,
|
AmazonS3Source::class,
|
||||||
BackblazeB2Source::class,
|
|
||||||
DropboxSource::class,
|
|
||||||
OpenStackSource::class,
|
OpenStackSource::class,
|
||||||
RackspaceSource::class
|
RackspaceSource::class
|
||||||
];
|
];
|
||||||
@ -102,35 +87,15 @@ class ConfigHelper
|
|||||||
$currentAppName = $this->get('app_name', false);
|
$currentAppName = $this->get('app_name', false);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'albums_menu_parents_only' => false,
|
|
||||||
'albums_menu_number_items' => 10,
|
|
||||||
'allow_photo_comments' => false,
|
|
||||||
'allow_photo_comments_anonymous' => true,
|
|
||||||
'allow_self_registration' => true,
|
'allow_self_registration' => true,
|
||||||
'analysis_queue_storage_location' => Storage::where('is_default', true)->first()->id,
|
|
||||||
'analytics_code' => '',
|
'analytics_code' => '',
|
||||||
'app_name' => trans('global.app_name'),
|
'app_name' => trans('global.app_name'),
|
||||||
'date_format' => $this->allowedDateFormats()[0],
|
'date_format' => $this->allowedDateFormats()[0],
|
||||||
'default_album_view' => $this->allowedAlbumViews()[0],
|
'default_album_view' => $this->allowedAlbumViews()[0],
|
||||||
'enable_visitor_hits' => false,
|
'enable_visitor_hits' => false,
|
||||||
'facebook_external_service_id' => 0,
|
|
||||||
'google_external_service_id' => 0,
|
|
||||||
'hotlink_protection' => false,
|
'hotlink_protection' => false,
|
||||||
'items_per_page' => 12,
|
'items_per_page' => 12,
|
||||||
'items_per_page_admin' => 10,
|
'items_per_page_admin' => 10,
|
||||||
'moderate_anonymous_users' => true,
|
|
||||||
'moderate_known_users' => true,
|
|
||||||
'photo_comments_allowed_html' => 'p,div,span,a,b,i,u',
|
|
||||||
'photo_comments_thread_depth' => 3,
|
|
||||||
'public_statistics' => true,
|
|
||||||
'queue_emails' => false,
|
|
||||||
'rabbitmq_enabled' => false,
|
|
||||||
'rabbitmq_server' => 'localhost',
|
|
||||||
'rabbitmq_password' => encrypt('guest'),
|
|
||||||
'rabbitmq_port' => 5672,
|
|
||||||
'rabbitmq_queue' => 'blue_twilight',
|
|
||||||
'rabbitmq_username' => 'guest',
|
|
||||||
'rabbitmq_vhost' => '/',
|
|
||||||
'recaptcha_enabled_registration' => false,
|
'recaptcha_enabled_registration' => false,
|
||||||
'recaptcha_secret_key' => '',
|
'recaptcha_secret_key' => '',
|
||||||
'recaptcha_site_key' => '',
|
'recaptcha_site_key' => '',
|
||||||
@ -143,24 +108,13 @@ class ConfigHelper
|
|||||||
'smtp_password' => '',
|
'smtp_password' => '',
|
||||||
'smtp_port' => 25,
|
'smtp_port' => 25,
|
||||||
'smtp_username' => '',
|
'smtp_username' => '',
|
||||||
'social_facebook_login' => false,
|
'theme' => 'default'
|
||||||
'social_google_login' => false,
|
|
||||||
'social_twitter_login' => false,
|
|
||||||
'social_user_feeds' => false,
|
|
||||||
'social_user_profiles' => false,
|
|
||||||
'theme' => 'default',
|
|
||||||
'twitter_external_service_id' => 0
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get($key, $defaultIfUnset = true)
|
public function get($key, $defaultIfUnset = true)
|
||||||
{
|
{
|
||||||
if (is_null($this->cache))
|
$config = Configuration::where('key', $key)->first();
|
||||||
{
|
|
||||||
$this->loadCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
$config = isset($this->cache[$key]) ? $this->cache[$key] : null;
|
|
||||||
|
|
||||||
if (is_null($config))
|
if (is_null($config))
|
||||||
{
|
{
|
||||||
@ -180,7 +134,6 @@ class ConfigHelper
|
|||||||
{
|
{
|
||||||
$results = array();
|
$results = array();
|
||||||
|
|
||||||
/** @var Configuration $config */
|
|
||||||
foreach (Configuration::all() as $config)
|
foreach (Configuration::all() as $config)
|
||||||
{
|
{
|
||||||
$results[$config->key] = $config->value;
|
$results[$config->key] = $config->value;
|
||||||
@ -191,64 +144,15 @@ class ConfigHelper
|
|||||||
|
|
||||||
public function getOrCreateModel($key)
|
public function getOrCreateModel($key)
|
||||||
{
|
{
|
||||||
$config = isset($this->cache[$key]) ? $this->cache[$key] : null;
|
$config = Configuration::where('key', $key)->first();
|
||||||
if (is_null($config) || $config === false)
|
if (is_null($config) || $config === false)
|
||||||
{
|
{
|
||||||
$config = new Configuration();
|
$config = new Configuration();
|
||||||
$config->key = $key;
|
$config->key = $key;
|
||||||
$config->value = '';
|
$config->value = '';
|
||||||
$config->save();
|
$config->save();
|
||||||
|
|
||||||
$this->loadCache();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isImageProcessingQueueEnabled()
|
|
||||||
{
|
|
||||||
return $this->get('rabbitmq_enabled') &&
|
|
||||||
!empty($this->get('rabbitmq_server')) &&
|
|
||||||
!empty($this->get('rabbitmq_port')) &&
|
|
||||||
!empty($this->get('rabbitmq_username')) &&
|
|
||||||
!empty($this->get('rabbitmq_password')) &&
|
|
||||||
!empty($this->get('rabbitmq_queue')) &&
|
|
||||||
!empty($this->get('rabbitmq_vhost'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isLoginWithFacebookEnabled()
|
|
||||||
{
|
|
||||||
return boolval($this->get('social_facebook_login')) &&
|
|
||||||
intval($this->get('facebook_external_service_id')) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isLoginWithGoogleEnabled()
|
|
||||||
{
|
|
||||||
return boolval($this->get('social_google_login')) &&
|
|
||||||
intval($this->get('google_external_service_id')) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isLoginWithTwitterEnabled()
|
|
||||||
{
|
|
||||||
return boolval($this->get('social_twitter_login')) &&
|
|
||||||
intval($this->get('twitter_external_service_id')) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isSocialMediaLoginEnabled()
|
|
||||||
{
|
|
||||||
return $this->isLoginWithFacebookEnabled() ||
|
|
||||||
$this->isLoginWithGoogleEnabled() ||
|
|
||||||
$this->isLoginWithTwitterEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function loadCache()
|
|
||||||
{
|
|
||||||
$this->cache = null;
|
|
||||||
|
|
||||||
/** @var Configuration $config */
|
|
||||||
foreach (Configuration::all() as $config)
|
|
||||||
{
|
|
||||||
$this->cache[$config->key] = $config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -33,12 +33,17 @@ class DbHelper
|
|||||||
|
|
||||||
public static function getAlbumsForCurrentUser($parentID = -1)
|
public static function getAlbumsForCurrentUser($parentID = -1)
|
||||||
{
|
{
|
||||||
$query = self::getAlbumsForCurrentUser_NonPaged('list', $parentID);
|
$query = self::getAlbumsForCurrentUser_NonPaged();
|
||||||
|
|
||||||
|
if ($parentID == 0)
|
||||||
|
{
|
||||||
|
$query = $query->where('albums.parent_album_id', null);
|
||||||
|
}
|
||||||
|
|
||||||
return $query->paginate(UserConfig::get('items_per_page'));
|
return $query->paginate(UserConfig::get('items_per_page'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getAlbumsForCurrentUser_NonPaged($permission = 'list', $parentAlbumID = -1)
|
public static function getAlbumsForCurrentUser_NonPaged()
|
||||||
{
|
{
|
||||||
$albumsQuery = Album::query();
|
$albumsQuery = Album::query();
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
@ -47,23 +52,40 @@ class DbHelper
|
|||||||
{
|
{
|
||||||
/* Admin users always get everything, therefore no filters are necessary */
|
/* Admin users always get everything, therefore no filters are necessary */
|
||||||
}
|
}
|
||||||
|
else if (is_null($user))
|
||||||
|
{
|
||||||
|
/* Anonymous users need to check the album_anonymous_permissions table. If not in this table, you're not allowed! */
|
||||||
|
|
||||||
|
$albumsQuery = Album::join('album_anonymous_permissions', 'album_anonymous_permissions.album_id', '=', 'albums.id')
|
||||||
|
->join('permissions', 'permissions.id', '=', 'album_anonymous_permissions.permission_id')
|
||||||
|
->where([
|
||||||
|
['permissions.section', 'album'],
|
||||||
|
['permissions.description', 'list']
|
||||||
|
]);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$helper = new PermissionsHelper();
|
/*
|
||||||
$albumIDs = $helper->getAlbumIDs($permission, $user);
|
Other users need to check either the album_group_permissions or album_user_permissions table. If not in either of these tables,
|
||||||
//dd($albumIDs->toArray());
|
you're not allowed!
|
||||||
$albumsQuery->whereIn('albums.id', $albumIDs);
|
*/
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
$parentAlbumID = intval($parentAlbumID);
|
$albumsQuery = Album::leftJoin('album_group_permissions', 'album_group_permissions.album_id', '=', 'albums.id')
|
||||||
if ($parentAlbumID == 0)
|
->leftJoin('album_user_permissions', 'album_user_permissions.album_id', '=', 'albums.id')
|
||||||
{
|
->leftJoin('permissions AS group_permissions', 'group_permissions.id', '=', 'album_group_permissions.permission_id')
|
||||||
$albumsQuery->where('albums.parent_album_id', null);
|
->leftJoin('permissions AS user_permissions', 'user_permissions.id', '=', 'album_user_permissions.permission_id')
|
||||||
}
|
->leftJoin('user_groups', 'user_groups.group_id', '=', 'album_group_permissions.group_id')
|
||||||
else if ($parentAlbumID > 0)
|
->where('albums.user_id', $user->id)
|
||||||
{
|
->orWhere([
|
||||||
$albumsQuery->where('albums.parent_album_id', $parentAlbumID);
|
['group_permissions.section', 'album'],
|
||||||
|
['group_permissions.description', 'list'],
|
||||||
|
['user_groups.user_id', $user->id]
|
||||||
|
])
|
||||||
|
->orWhere([
|
||||||
|
['user_permissions.section', 'album'],
|
||||||
|
['user_permissions.description', 'list'],
|
||||||
|
['album_user_permissions.user_id', $user->id]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $albumsQuery->select('albums.*')
|
return $albumsQuery->select('albums.*')
|
||||||
@ -81,14 +103,4 @@ class DbHelper
|
|||||||
{
|
{
|
||||||
return Album::where('url_path', $urlPath)->first();
|
return Album::where('url_path', $urlPath)->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getChildAlbumsCount(Album $album)
|
|
||||||
{
|
|
||||||
return self::getAlbumsForCurrentUser_NonPaged('list', $album->id)->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getChildAlbums(Album $album)
|
|
||||||
{
|
|
||||||
return self::getAlbumsForCurrentUser_NonPaged('list', $album->id)->get();
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -46,7 +46,7 @@ class FileHelper
|
|||||||
|
|
||||||
if (!file_exists($path))
|
if (!file_exists($path))
|
||||||
{
|
{
|
||||||
@mkdir($path, 0755, true);
|
mkdir($path, 0755, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $path;
|
return $path;
|
||||||
|
@ -52,68 +52,14 @@ class MiscHelper
|
|||||||
return (int) $val;
|
return (int) $val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a decimal (e.g. 3.5) to a fraction (e.g. 7/2).
|
|
||||||
* Adapted from: http://jonisalonen.com/2012/converting-decimal-numbers-to-ratios/
|
|
||||||
*
|
|
||||||
* @param float $decimal the decimal number.
|
|
||||||
*
|
|
||||||
* @return array|bool a 1/2 would be [1, 2] array (this can be imploded with '/' to form a string)
|
|
||||||
*/
|
|
||||||
public static function decimalToFraction($decimal)
|
|
||||||
{
|
|
||||||
if ($decimal < 0 || !is_numeric($decimal)) {
|
|
||||||
// Negative digits need to be passed in as positive numbers
|
|
||||||
// and prefixed as negative once the response is imploded.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ($decimal == 0) {
|
|
||||||
return [0, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
$tolerance = 1.e-4;
|
|
||||||
|
|
||||||
$numerator = 1;
|
|
||||||
$h2 = 0;
|
|
||||||
$denominator = 0;
|
|
||||||
$k2 = 1;
|
|
||||||
$b = 1 / $decimal;
|
|
||||||
do {
|
|
||||||
$b = 1 / $b;
|
|
||||||
$a = floor($b);
|
|
||||||
$aux = $numerator;
|
|
||||||
$numerator = $a * $numerator + $h2;
|
|
||||||
$h2 = $aux;
|
|
||||||
$aux = $denominator;
|
|
||||||
$denominator = $a * $denominator + $k2;
|
|
||||||
$k2 = $aux;
|
|
||||||
$b = $b - $a;
|
|
||||||
} while (abs($decimal - $numerator / $denominator) > $decimal * $tolerance);
|
|
||||||
|
|
||||||
return [
|
|
||||||
$numerator,
|
|
||||||
$denominator
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function ensureHasTrailingSlash($string)
|
|
||||||
{
|
|
||||||
if (strlen($string) > 0 && substr($string, strlen($string) - 1, 1) != '/')
|
|
||||||
{
|
|
||||||
$string .= '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getEnvironmentFilePath()
|
public static function getEnvironmentFilePath()
|
||||||
{
|
{
|
||||||
return sprintf('%s/.env', dirname(dirname(__DIR__)));
|
return sprintf('%s/.env', dirname(dirname(__DIR__)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getEnvironmentSetting($settingName, $envFile = null)
|
public static function getEnvironmentSetting($settingName)
|
||||||
{
|
{
|
||||||
$envFile = $envFile ?? MiscHelper::getEnvironmentFilePath();
|
$envFile = MiscHelper::getEnvironmentFilePath();
|
||||||
|
|
||||||
if (!file_exists($envFile))
|
if (!file_exists($envFile))
|
||||||
{
|
{
|
||||||
@ -134,12 +80,6 @@ class MiscHelper
|
|||||||
return MiscHelper::getEnvironmentSetting('APP_INSTALLED');
|
return MiscHelper::getEnvironmentSetting('APP_INSTALLED');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function isExecEnabled()
|
|
||||||
{
|
|
||||||
$disabled = explode(',', ini_get('disable_functions'));
|
|
||||||
return !in_array('exec', $disabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests whether the provided URL belongs to the current application (i.e. both scheme and hostname match.)
|
* Tests whether the provided URL belongs to the current application (i.e. both scheme and hostname match.)
|
||||||
* @param $url
|
* @param $url
|
||||||
|
@ -1,258 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Helpers;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\AlbumDefaultAnonymousPermission;
|
|
||||||
use App\AlbumDefaultGroupPermission;
|
|
||||||
use App\AlbumDefaultUserPermission;
|
|
||||||
use App\Permission;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class PermissionsHelper
|
|
||||||
{
|
|
||||||
public function getAlbumIDs($permission = 'list', User $user = null)
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
// First check if the anonymous user can do what is being requested - if so, the permission would also inherit
|
|
||||||
// to logged-in users
|
|
||||||
$anonymousUsersCan = DB::table('album_permissions_cache')
|
|
||||||
->join('permissions', 'permissions.id', '=', 'album_permissions_cache.permission_id')
|
|
||||||
->where([
|
|
||||||
['album_permissions_cache.user_id', null],
|
|
||||||
['permissions.section', 'album'],
|
|
||||||
['permissions.description', $permission]
|
|
||||||
])
|
|
||||||
->select('album_permissions_cache.album_id')
|
|
||||||
->distinct()
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($anonymousUsersCan as $item)
|
|
||||||
{
|
|
||||||
$result[] = $item->album_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = DB::table('album_permissions_cache')
|
|
||||||
->join('permissions', 'permissions.id', '=', 'album_permissions_cache.permission_id')
|
|
||||||
->where([
|
|
||||||
['album_permissions_cache.user_id', (is_null($user) || $user->isAnonymous() ? null : $user->id)],
|
|
||||||
['permissions.section', 'album'],
|
|
||||||
['permissions.description', $permission]
|
|
||||||
])
|
|
||||||
->select('album_permissions_cache.album_id')
|
|
||||||
->distinct()
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($query as $item)
|
|
||||||
{
|
|
||||||
if (!in_array($item->album_id, $result))
|
|
||||||
{
|
|
||||||
$result[] = $item->album_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rebuildCache()
|
|
||||||
{
|
|
||||||
$this->rebuildAlbumCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function userCan_Album(Album $album, User $user, $permission)
|
|
||||||
{
|
|
||||||
// First check if the anonymous user can do what is being requested - if so, the permission would also inherit
|
|
||||||
// to logged-in users
|
|
||||||
$anonymousUsersCan = DB::table('album_permissions_cache')
|
|
||||||
->join('permissions', 'permissions.id', '=', 'album_permissions_cache.permission_id')
|
|
||||||
->where([
|
|
||||||
['album_permissions_cache.album_id', $album->id],
|
|
||||||
['album_permissions_cache.user_id', null],
|
|
||||||
['permissions.section', 'album'],
|
|
||||||
['permissions.description', $permission]
|
|
||||||
])
|
|
||||||
->count() > 0;
|
|
||||||
|
|
||||||
if ($anonymousUsersCan)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DB::table('album_permissions_cache')
|
|
||||||
->join('permissions', 'permissions.id', '=', 'album_permissions_cache.permission_id')
|
|
||||||
->where([
|
|
||||||
['album_permissions_cache.album_id', $album->id],
|
|
||||||
['album_permissions_cache.user_id', (is_null($user) || $user->isAnonymous() ? null : $user->id)],
|
|
||||||
['permissions.section', 'album'],
|
|
||||||
['permissions.description', $permission]
|
|
||||||
])
|
|
||||||
->count() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function usersWhoCan_Album(Album $album, $permission)
|
|
||||||
{
|
|
||||||
$users = DB::table('album_permissions_cache')
|
|
||||||
->join('permissions', 'permissions.id', '=', 'album_permissions_cache.permission_id')
|
|
||||||
->where([
|
|
||||||
['album_permissions_cache.album_id', $album->id],
|
|
||||||
['permissions.section', 'album'],
|
|
||||||
['permissions.description', $permission]
|
|
||||||
])
|
|
||||||
->get();
|
|
||||||
|
|
||||||
// Include the album's owner (who can do everything)
|
|
||||||
$users->push($album->user);
|
|
||||||
|
|
||||||
return $users;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function rebuildAlbumCache()
|
|
||||||
{
|
|
||||||
// Get a list of albums
|
|
||||||
$albums = Album::all();
|
|
||||||
|
|
||||||
// Get a list of all configured permissions
|
|
||||||
$albumUserPermissions = DB::table('album_user_permissions')->get();
|
|
||||||
$albumGroupPermissions = DB::table('album_group_permissions')->get();
|
|
||||||
$albumAnonPermissions = DB::table('album_anonymous_permissions')->get();
|
|
||||||
|
|
||||||
$defaultAlbumUserPermissions = AlbumDefaultUserPermission::all();
|
|
||||||
$defaultAlbumGroupPermissions = AlbumDefaultGroupPermission::all();
|
|
||||||
$defaultAnonPermissions = AlbumDefaultAnonymousPermission::all();
|
|
||||||
|
|
||||||
// Get a list of all user->group memberships
|
|
||||||
$userGroups = DB::table('user_groups')->get();
|
|
||||||
|
|
||||||
// Build a matrix of new permissions
|
|
||||||
$permissionsCache = [];
|
|
||||||
|
|
||||||
/** @var Album $album */
|
|
||||||
foreach ($albums as $album)
|
|
||||||
{
|
|
||||||
$effectiveAlbumID = $album->effectiveAlbumIDForPermissions();
|
|
||||||
|
|
||||||
if ($effectiveAlbumID === 0)
|
|
||||||
{
|
|
||||||
/* Use the default permissions list */
|
|
||||||
|
|
||||||
foreach ($defaultAnonPermissions as $anonymousPermission)
|
|
||||||
{
|
|
||||||
$permissionsCache[] = [
|
|
||||||
'album_id' => $album->id,
|
|
||||||
'permission_id' => $anonymousPermission->permission_id,
|
|
||||||
'created_at' => new \DateTime(),
|
|
||||||
'updated_at' => new \DateTime()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($defaultAlbumUserPermissions as $userPermission)
|
|
||||||
{
|
|
||||||
$permissionsCache[] = [
|
|
||||||
'user_id' => $userPermission->user_id,
|
|
||||||
'album_id' => $album->id,
|
|
||||||
'permission_id' => $userPermission->permission_id,
|
|
||||||
'created_at' => new \DateTime(),
|
|
||||||
'updated_at' => new \DateTime()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($defaultAlbumGroupPermissions as $groupPermission)
|
|
||||||
{
|
|
||||||
// Get a list of users in this group, and add one per user
|
|
||||||
$usersInGroup = array_filter($userGroups->toArray(), function ($item) use ($groupPermission)
|
|
||||||
{
|
|
||||||
return $item->group_id = $groupPermission->group_id;
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach ($usersInGroup as $userGroup)
|
|
||||||
{
|
|
||||||
$permissionsCache[] = [
|
|
||||||
'user_id' => $userGroup->user_id,
|
|
||||||
'album_id' => $album->id,
|
|
||||||
'permission_id' => $groupPermission->permission_id,
|
|
||||||
'created_at' => new \DateTime(),
|
|
||||||
'updated_at' => new \DateTime()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Use the specified album-specific permissions */
|
|
||||||
$anonymousPermissions = array_filter($albumAnonPermissions->toArray(), function ($item) use ($effectiveAlbumID)
|
|
||||||
{
|
|
||||||
return ($item->album_id == $effectiveAlbumID);
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach ($anonymousPermissions as $anonymousPermission)
|
|
||||||
{
|
|
||||||
$permissionsCache[] = [
|
|
||||||
'album_id' => $album->id,
|
|
||||||
'permission_id' => $anonymousPermission->permission_id,
|
|
||||||
'created_at' => new \DateTime(),
|
|
||||||
'updated_at' => new \DateTime()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$userPermissions = array_filter($albumUserPermissions->toArray(), function ($item) use ($effectiveAlbumID)
|
|
||||||
{
|
|
||||||
return ($item->album_id == $effectiveAlbumID);
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach ($userPermissions as $userPermission)
|
|
||||||
{
|
|
||||||
$permissionsCache[] = [
|
|
||||||
'user_id' => $userPermission->user_id,
|
|
||||||
'album_id' => $album->id,
|
|
||||||
'permission_id' => $userPermission->permission_id,
|
|
||||||
'created_at' => new \DateTime(),
|
|
||||||
'updated_at' => new \DateTime()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$groupPermissions = array_filter($albumGroupPermissions->toArray(), function ($item) use ($effectiveAlbumID)
|
|
||||||
{
|
|
||||||
return ($item->album_id == $effectiveAlbumID);
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach ($groupPermissions as $groupPermission)
|
|
||||||
{
|
|
||||||
// Get a list of users in this group, and add one per user
|
|
||||||
$usersInGroup = array_filter($userGroups->toArray(), function ($item) use ($groupPermission)
|
|
||||||
{
|
|
||||||
return $item->group_id = $groupPermission->group_id;
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach ($usersInGroup as $userGroup)
|
|
||||||
{
|
|
||||||
$permissionsCache[] = [
|
|
||||||
'user_id' => $userGroup->user_id,
|
|
||||||
'album_id' => $album->id,
|
|
||||||
'permission_id' => $groupPermission->permission_id,
|
|
||||||
'created_at' => new \DateTime(),
|
|
||||||
'updated_at' => new \DateTime()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->savePermissionsCache($permissionsCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function savePermissionsCache(array $cacheToSave)
|
|
||||||
{
|
|
||||||
DB::transaction(function() use ($cacheToSave)
|
|
||||||
{
|
|
||||||
DB::table('album_permissions_cache')->truncate();
|
|
||||||
|
|
||||||
foreach ($cacheToSave as $cacheItem)
|
|
||||||
{
|
|
||||||
DB::table('album_permissions_cache')->insert($cacheItem);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,37 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Helpers;
|
namespace App\Helpers;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class ValidationHelper
|
class ValidationHelper
|
||||||
{
|
{
|
||||||
public function albumPathUnique($attribute, $value, $parameters, $validator)
|
|
||||||
{
|
|
||||||
$data = $validator->getData();
|
|
||||||
$parentID = intval($data['parent_album_id']);
|
|
||||||
$name = $data['name'];
|
|
||||||
|
|
||||||
if ($parentID === 0)
|
|
||||||
{
|
|
||||||
$parentID = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$queryParams = [
|
|
||||||
['name', $name],
|
|
||||||
['parent_album_id', $parentID]
|
|
||||||
];
|
|
||||||
|
|
||||||
if (count($parameters) > 0)
|
|
||||||
{
|
|
||||||
$existingAlbumID = intval($parameters[0]);
|
|
||||||
$queryParams[] = ['id', '<>', $existingAlbumID];
|
|
||||||
}
|
|
||||||
|
|
||||||
$count = DB::table('albums')->where($queryParams)->count();
|
|
||||||
|
|
||||||
return ($count == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function directoryExists($attribute, $value, $parameters, $validator)
|
public function directoryExists($attribute, $value, $parameters, $validator)
|
||||||
{
|
{
|
||||||
return file_exists($value) && is_dir($value);
|
return file_exists($value) && is_dir($value);
|
||||||
|
@ -3,26 +3,20 @@
|
|||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Album;
|
use App\Album;
|
||||||
use App\AlbumDefaultAnonymousPermission;
|
|
||||||
use App\AlbumDefaultGroupPermission;
|
|
||||||
use App\AlbumDefaultUserPermission;
|
|
||||||
use App\AlbumRedirect;
|
use App\AlbumRedirect;
|
||||||
use App\Facade\Theme;
|
use App\Facade\Theme;
|
||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Group;
|
use App\Group;
|
||||||
use App\Helpers\DbHelper;
|
use App\Helpers\DbHelper;
|
||||||
use App\Helpers\MiscHelper;
|
use App\Helpers\MiscHelper;
|
||||||
use App\Helpers\PermissionsHelper;
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests;
|
use App\Http\Requests;
|
||||||
use App\Label;
|
|
||||||
use App\Permission;
|
use App\Permission;
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
use App\Services\AlbumService;
|
use App\Services\AlbumService;
|
||||||
use App\Services\PhotoService;
|
use App\Services\PhotoService;
|
||||||
use App\Storage;
|
use App\Storage;
|
||||||
use App\User;
|
use App\User;
|
||||||
use App\UserActivity;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
@ -31,30 +25,6 @@ use Illuminate\Support\Facades\View;
|
|||||||
|
|
||||||
class AlbumController extends Controller
|
class AlbumController extends Controller
|
||||||
{
|
{
|
||||||
public static function doesGroupHaveDefaultPermission(Group $group, Permission $permission)
|
|
||||||
{
|
|
||||||
return AlbumDefaultGroupPermission::where([
|
|
||||||
'group_id' => $group->id,
|
|
||||||
'permission_id' => $permission->id
|
|
||||||
])->count() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function doesUserHaveDefaultPermission($user, Permission $permission)
|
|
||||||
{
|
|
||||||
// User will be null for anonymous users
|
|
||||||
if (is_null($user))
|
|
||||||
{
|
|
||||||
return AlbumDefaultAnonymousPermission::where(['permission_id' => $permission->id])->count() > 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return AlbumDefaultUserPermission::where([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'permission_id' => $permission->id
|
|
||||||
])->count() > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
@ -73,7 +43,7 @@ class AlbumController extends Controller
|
|||||||
|
|
||||||
if (count($photos) == 0)
|
if (count($photos) == 0)
|
||||||
{
|
{
|
||||||
return redirect(route('albums.show', ['album' => $album->id]));
|
return redirect(route('albums.show', ['id' => $album->id]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Theme::render('admin.analyse_album', ['album' => $album, 'photos' => $photos, 'queue_token' => $queue_token]);
|
return Theme::render('admin.analyse_album', ['album' => $album, 'photos' => $photos, 'queue_token' => $queue_token]);
|
||||||
@ -110,41 +80,6 @@ class AlbumController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function defaultPermissions()
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-albums');
|
|
||||||
|
|
||||||
$addNewGroups = [];
|
|
||||||
$existingGroups = [];
|
|
||||||
foreach (Group::orderBy('name')->get() as $group)
|
|
||||||
{
|
|
||||||
if (AlbumDefaultGroupPermission::where('group_id', $group->id)->count() == 0)
|
|
||||||
{
|
|
||||||
$addNewGroups[] = $group;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$existingGroups[] = $group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$existingUsers = [];
|
|
||||||
foreach (User::orderBy('name')->get() as $user)
|
|
||||||
{
|
|
||||||
if (AlbumDefaultUserPermission::where('user_id', $user->id)->count() > 0)
|
|
||||||
{
|
|
||||||
$existingUsers[] = $user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('admin.album_default_permissions', [
|
|
||||||
'add_new_groups' => $addNewGroups,
|
|
||||||
'all_permissions' => Permission::where('section', 'album')->get(),
|
|
||||||
'existing_groups' => $existingGroups,
|
|
||||||
'existing_users' => $existingUsers
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete($id)
|
public function delete($id)
|
||||||
{
|
{
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-albums');
|
$this->authorizeAccessToAdminPanel('admin:manage-albums');
|
||||||
@ -170,7 +105,7 @@ class AlbumController extends Controller
|
|||||||
|
|
||||||
$redirect->delete();
|
$redirect->delete();
|
||||||
$request->session()->flash('success', trans('admin.delete_redirect_success_message'));
|
$request->session()->flash('success', trans('admin.delete_redirect_success_message'));
|
||||||
return redirect(route('albums.show', ['album' => $id, 'tab' => 'redirects']));
|
return redirect(route('albums.show', ['id' => $id, 'tab' => 'redirects']));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,12 +120,6 @@ class AlbumController extends Controller
|
|||||||
|
|
||||||
$album = $this->loadAlbum($id, 'delete');
|
$album = $this->loadAlbum($id, 'delete');
|
||||||
|
|
||||||
if ($album->children()->count() > 0)
|
|
||||||
{
|
|
||||||
$request->session()->flash('error', trans('admin.delete_album_failed_children', ['album' => $album->name]));
|
|
||||||
return redirect(route('albums.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all the photo files
|
// Delete all the photo files
|
||||||
/** @var Photo $photo */
|
/** @var Photo $photo */
|
||||||
foreach ($album->photos as $photo)
|
foreach ($album->photos as $photo)
|
||||||
@ -243,10 +172,6 @@ class AlbumController extends Controller
|
|||||||
|
|
||||||
// Only get top-level albums
|
// Only get top-level albums
|
||||||
$albums = DbHelper::getAlbumsForCurrentUser(0);
|
$albums = DbHelper::getAlbumsForCurrentUser(0);
|
||||||
foreach ($albums as $album)
|
|
||||||
{
|
|
||||||
$this->loadChildAlbums($album);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('admin.list_albums', [
|
return Theme::render('admin.list_albums', [
|
||||||
'albums' => $albums,
|
'albums' => $albums,
|
||||||
@ -254,153 +179,6 @@ class AlbumController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for editing the specified resource.
|
|
||||||
*
|
|
||||||
* @param int $id
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function metadata(Request $request, $id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-albums');
|
|
||||||
|
|
||||||
/** @var Album $album */
|
|
||||||
$album = $this->loadAlbum($id);
|
|
||||||
|
|
||||||
$photosNeededToUpdate = $album->photos()->where('metadata_version', '<', PhotoService::METADATA_VERSION)->get();
|
|
||||||
|
|
||||||
return Theme::render('admin.album_metadata', [
|
|
||||||
'album' => $album,
|
|
||||||
'current_metadata' => PhotoService::METADATA_VERSION,
|
|
||||||
'photos' => $photosNeededToUpdate,
|
|
||||||
'queue_token' => MiscHelper::randomString()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDefaultGroupPermissions(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-albums');
|
|
||||||
|
|
||||||
if ($request->get('action') == 'add_group' && $request->has('group_id'))
|
|
||||||
{
|
|
||||||
/* Add a new group to the default permission list */
|
|
||||||
|
|
||||||
/** @var Group $group */
|
|
||||||
$group = Group::where('id', $request->get('group_id'))->first();
|
|
||||||
if (is_null($group))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link all default permissions to the group
|
|
||||||
/** @var Permission $permission */
|
|
||||||
foreach (Permission::where(['section' => 'album', 'is_default' => true])->get() as $permission)
|
|
||||||
{
|
|
||||||
$defaultPermission = new AlbumDefaultGroupPermission();
|
|
||||||
$defaultPermission->group_id = $group->id;
|
|
||||||
$defaultPermission->permission_id = $permission->id;
|
|
||||||
$defaultPermission->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ($request->get('action') == 'update_group_permissions')
|
|
||||||
{
|
|
||||||
/* Update existing group permissions for this album */
|
|
||||||
AlbumDefaultGroupPermission::truncate();
|
|
||||||
|
|
||||||
$permissions = $request->get('permissions');
|
|
||||||
if (is_array($permissions))
|
|
||||||
{
|
|
||||||
foreach ($permissions as $groupID => $permissionIDs)
|
|
||||||
{
|
|
||||||
foreach ($permissionIDs as $permissionID)
|
|
||||||
{
|
|
||||||
$defaultPermission = new AlbumDefaultGroupPermission();
|
|
||||||
$defaultPermission->group_id = $groupID;
|
|
||||||
$defaultPermission->permission_id = $permissionID;
|
|
||||||
$defaultPermission->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebuild the permissions cache
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$helper->rebuildCache();
|
|
||||||
|
|
||||||
return redirect(route('albums.defaultPermissions'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDefaultUserPermissions(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-albums');
|
|
||||||
|
|
||||||
if ($request->get('action') == 'add_user' && $request->has('user_id'))
|
|
||||||
{
|
|
||||||
/* Add a new user to the permission list for this album */
|
|
||||||
|
|
||||||
/** @var User $user */
|
|
||||||
$user = User::where('id', $request->get('user_id'))->first();
|
|
||||||
if (is_null($user))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link all default permissions to the group
|
|
||||||
/** @var Permission $permission */
|
|
||||||
foreach (Permission::where(['section' => 'album', 'is_default' => true])->get() as $permission)
|
|
||||||
{
|
|
||||||
$defaultPermission = new AlbumDefaultUserPermission();
|
|
||||||
$defaultPermission->user_id = $user->id;
|
|
||||||
$defaultPermission->permission_id = $permission->id;
|
|
||||||
$defaultPermission->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ($request->get('action') == 'update_user_permissions')
|
|
||||||
{
|
|
||||||
/* Update existing user and anonymous permissions for this album */
|
|
||||||
AlbumDefaultAnonymousPermission::truncate();
|
|
||||||
AlbumDefaultUserPermission::truncate();
|
|
||||||
|
|
||||||
$permissions = $request->get('permissions');
|
|
||||||
if (is_array($permissions))
|
|
||||||
{
|
|
||||||
if (isset($permissions['anonymous']))
|
|
||||||
{
|
|
||||||
foreach ($permissions['anonymous'] as $permissionID)
|
|
||||||
{
|
|
||||||
$defaultPermission = new AlbumDefaultAnonymousPermission();
|
|
||||||
$defaultPermission->permission_id = $permissionID;
|
|
||||||
$defaultPermission->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($permissions as $key => $value)
|
|
||||||
{
|
|
||||||
$userID = intval($key);
|
|
||||||
if ($userID == 0)
|
|
||||||
{
|
|
||||||
// Skip non-numeric IDs (e.g. anonymous)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($value as $permissionID)
|
|
||||||
{
|
|
||||||
$defaultPermission = new AlbumDefaultUserPermission();
|
|
||||||
$defaultPermission->user_id = $userID;
|
|
||||||
$defaultPermission->permission_id = $permissionID;
|
|
||||||
$defaultPermission->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebuild the permissions cache
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$helper->rebuildCache();
|
|
||||||
|
|
||||||
return redirect(route('albums.defaultPermissions'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setGroupPermissions(Request $request, $id)
|
public function setGroupPermissions(Request $request, $id)
|
||||||
{
|
{
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-albums');
|
$this->authorizeAccessToAdminPanel('admin:manage-albums');
|
||||||
@ -454,10 +232,6 @@ class AlbumController extends Controller
|
|||||||
|
|
||||||
$album->save();
|
$album->save();
|
||||||
|
|
||||||
// Rebuild the permissions cache
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$helper->rebuildCache();
|
|
||||||
|
|
||||||
return redirect(route('albums.show', [$album->id, 'tab' => 'permissions']));
|
return redirect(route('albums.show', [$album->id, 'tab' => 'permissions']));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,10 +307,6 @@ class AlbumController extends Controller
|
|||||||
|
|
||||||
$album->save();
|
$album->save();
|
||||||
|
|
||||||
// Rebuild the permissions cache
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$helper->rebuildCache();
|
|
||||||
|
|
||||||
return redirect(route('albums.show', [$album->id, 'tab' => 'permissions']));
|
return redirect(route('albums.show', [$album->id, 'tab' => 'permissions']));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,7 +394,6 @@ class AlbumController extends Controller
|
|||||||
'existing_users' => $existingUsers,
|
'existing_users' => $existingUsers,
|
||||||
'file_upload_limit' => $fileUploadLimit,
|
'file_upload_limit' => $fileUploadLimit,
|
||||||
'is_upload_enabled' => $isUploadEnabled,
|
'is_upload_enabled' => $isUploadEnabled,
|
||||||
'labels' => Label::all(),
|
|
||||||
'max_post_limit' => $postLimit,
|
'max_post_limit' => $postLimit,
|
||||||
'max_post_limit_bulk' => $fileUploadOrPostLowerLimit,
|
'max_post_limit_bulk' => $fileUploadOrPostLowerLimit,
|
||||||
'photos' => $photos,
|
'photos' => $photos,
|
||||||
@ -646,7 +415,6 @@ class AlbumController extends Controller
|
|||||||
|
|
||||||
$album = new Album();
|
$album = new Album();
|
||||||
$album->fill($request->only(['name', 'description', 'storage_id', 'parent_album_id']));
|
$album->fill($request->only(['name', 'description', 'storage_id', 'parent_album_id']));
|
||||||
$album->is_permissions_inherited = (strtolower($request->get('is_permissions_inherited')) == 'on');
|
|
||||||
|
|
||||||
if (strlen($album->parent_album_id) == 0)
|
if (strlen($album->parent_album_id) == 0)
|
||||||
{
|
{
|
||||||
@ -660,52 +428,22 @@ class AlbumController extends Controller
|
|||||||
$album->generateUrlPath();
|
$album->generateUrlPath();
|
||||||
$album->save();
|
$album->save();
|
||||||
|
|
||||||
// Link the default permissions (if a public album)
|
// Link all default permissions to anonymous users (if a public album)
|
||||||
$isPrivate = (strtolower($request->get('is_private')) == 'on');
|
$isPrivate = (strtolower($request->get('is_private')) == 'on');
|
||||||
if (!$album->is_permissions_inherited && !$isPrivate)
|
|
||||||
|
if (!$isPrivate)
|
||||||
{
|
{
|
||||||
$defaultAlbumUserPermissions = AlbumDefaultUserPermission::all();
|
/** @var Permission $permission */
|
||||||
$defaultAlbumGroupPermissions = AlbumDefaultGroupPermission::all();
|
foreach (Permission::where(['section' => 'album', 'is_default' => true])->get() as $permission)
|
||||||
$defaultAnonPermissions = AlbumDefaultAnonymousPermission::all();
|
|
||||||
|
|
||||||
/** @var AlbumDefaultAnonymousPermission $permission */
|
|
||||||
foreach ($defaultAnonPermissions as $permission)
|
|
||||||
{
|
{
|
||||||
$album->anonymousPermissions()->attach($permission->permission_id, [
|
$album->anonymousPermissions()->attach($permission->id, [
|
||||||
'created_at' => new \DateTime(),
|
|
||||||
'updated_at' => new \DateTime()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var AlbumDefaultGroupPermission $permission */
|
|
||||||
foreach ($defaultAlbumGroupPermissions as $permission)
|
|
||||||
{
|
|
||||||
$album->groupPermissions()->attach($permission->permission_id, [
|
|
||||||
'group_id' => $permission->group_id,
|
|
||||||
'created_at' => new \DateTime(),
|
|
||||||
'updated_at' => new \DateTime()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var AlbumDefaultUserPermission $permission */
|
|
||||||
foreach ($defaultAlbumUserPermissions as $permission)
|
|
||||||
{
|
|
||||||
$album->userPermissions()->attach($permission->permission_id, [
|
|
||||||
'user_id' => $permission->user_id,
|
|
||||||
'created_at' => new \DateTime(),
|
'created_at' => new \DateTime(),
|
||||||
'updated_at' => new \DateTime()
|
'updated_at' => new \DateTime()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an activity record
|
return redirect(route('albums.show', ['id' => $album->id]));
|
||||||
$this->createActivityRecord($album, 'album.created');
|
|
||||||
|
|
||||||
// Rebuild the permissions cache
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$helper->rebuildCache();
|
|
||||||
|
|
||||||
return redirect(route('albums.show', ['album' => $album->id]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function storeRedirect(Requests\StoreAlbumRedirectRequest $request, $id)
|
public function storeRedirect(Requests\StoreAlbumRedirectRequest $request, $id)
|
||||||
@ -720,7 +458,7 @@ class AlbumController extends Controller
|
|||||||
$redirect->save();
|
$redirect->save();
|
||||||
|
|
||||||
$request->session()->flash('success', trans('admin.create_redirect_success_message'));
|
$request->session()->flash('success', trans('admin.create_redirect_success_message'));
|
||||||
return redirect(route('albums.show', ['album' => $id, 'tab' => 'redirects']));
|
return redirect(route('albums.show', ['id' => $id, 'tab' => 'redirects']));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -738,7 +476,6 @@ class AlbumController extends Controller
|
|||||||
$currentParentID = $album->parent_album_id;
|
$currentParentID = $album->parent_album_id;
|
||||||
|
|
||||||
$album->fill($request->only(['name', 'description', 'parent_album_id']));
|
$album->fill($request->only(['name', 'description', 'parent_album_id']));
|
||||||
$album->is_permissions_inherited = (strtolower($request->get('is_permissions_inherited')) == 'on');
|
|
||||||
|
|
||||||
if (strlen($album->parent_album_id) == 0)
|
if (strlen($album->parent_album_id) == 0)
|
||||||
{
|
{
|
||||||
@ -775,29 +512,9 @@ class AlbumController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$album->save();
|
$album->save();
|
||||||
|
|
||||||
// Rebuild the permissions cache
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$helper->rebuildCache();
|
|
||||||
|
|
||||||
$request->session()->flash('success', trans('admin.album_saved_successfully', ['name' => $album->name]));
|
$request->session()->flash('success', trans('admin.album_saved_successfully', ['name' => $album->name]));
|
||||||
|
|
||||||
return redirect(route('albums.show', ['album' => $id]));
|
return redirect(route('albums.show', ['id' => $id]));
|
||||||
}
|
|
||||||
|
|
||||||
private function createActivityRecord(Album $album, $type, $activityDateTime = null)
|
|
||||||
{
|
|
||||||
if (is_null($activityDateTime))
|
|
||||||
{
|
|
||||||
$activityDateTime = new \DateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
$userActivity = new UserActivity();
|
|
||||||
$userActivity->user_id = $this->getUser()->id;
|
|
||||||
$userActivity->activity_at = $activityDateTime;
|
|
||||||
$userActivity->type = $type;
|
|
||||||
$userActivity->album_id = $album->id;
|
|
||||||
$userActivity->save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -817,13 +534,4 @@ class AlbumController extends Controller
|
|||||||
|
|
||||||
return $album;
|
return $album;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function loadChildAlbums(Album $album)
|
|
||||||
{
|
|
||||||
$album->child_albums = DbHelper::getChildAlbums($album);
|
|
||||||
foreach ($album->child_albums as $childAlbum)
|
|
||||||
{
|
|
||||||
$this->loadChildAlbums($childAlbum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -3,25 +3,19 @@
|
|||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Album;
|
use App\Album;
|
||||||
use App\ExternalService;
|
use App\Configuration;
|
||||||
use App\Facade\Theme;
|
use App\Facade\Theme;
|
||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Group;
|
use App\Group;
|
||||||
use App\Helpers\AnalysisQueueHelper;
|
use App\Helpers\ConfigHelper;
|
||||||
use App\Helpers\DbHelper;
|
use App\Helpers\DbHelper;
|
||||||
use App\Helpers\MiscHelper;
|
|
||||||
use App\Helpers\PermissionsHelper;
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\SaveSettingsRequest;
|
use App\Http\Requests\SaveSettingsRequest;
|
||||||
use App\Label;
|
|
||||||
use App\Mail\TestMailConfig;
|
use App\Mail\TestMailConfig;
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
use App\PhotoComment;
|
|
||||||
use App\Services\GiteaService;
|
|
||||||
use App\Services\PhotoService;
|
|
||||||
use App\Storage;
|
|
||||||
use App\User;
|
use App\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
@ -29,143 +23,29 @@ use Illuminate\Support\Facades\View;
|
|||||||
|
|
||||||
class DefaultController extends Controller
|
class DefaultController extends Controller
|
||||||
{
|
{
|
||||||
private $passwordSettingKeys;
|
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
View::share('is_admin', true);
|
View::share('is_admin', true);
|
||||||
|
|
||||||
$this->passwordSettingKeys = [
|
|
||||||
'rabbitmq_password',
|
|
||||||
'smtp_password',
|
|
||||||
'facebook_app_secret',
|
|
||||||
'google_app_secret',
|
|
||||||
'twitter_app_secret'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function about()
|
|
||||||
{
|
|
||||||
return Theme::render('admin.about', [
|
|
||||||
'current_version' => config('app.version'),
|
|
||||||
'licence_text' => file_get_contents(sprintf('%s/LICENSE', dirname(dirname(dirname(dirname(__DIR__))))))
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function aboutLatestRelease()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$giteaService = new GiteaService();
|
|
||||||
$releaseInfo = $giteaService->checkForLatestRelease();
|
|
||||||
|
|
||||||
// Convert the publish date so we can re-format it with the user's settings
|
|
||||||
$publishDate = \DateTime::createFromFormat('Y-m-d\TH:i:sP', $releaseInfo->published_at);
|
|
||||||
|
|
||||||
// HTML-ify the body text
|
|
||||||
$body = nl2br($releaseInfo->body);
|
|
||||||
$body = preg_replace('/\*\*(.+)\*\*/', '<b>$1</b>', $body);
|
|
||||||
|
|
||||||
// Remove the "v" from the release name
|
|
||||||
$version = substr($releaseInfo->tag_name, 1);
|
|
||||||
|
|
||||||
// Determine if we can upgrade
|
|
||||||
$canUpgrade = version_compare($version, config('app.version')) > 0;
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'can_upgrade' => $canUpgrade,
|
|
||||||
'body' => $body,
|
|
||||||
'name' => $version,
|
|
||||||
'publish_date' => $publishDate->format(UserConfig::get('date_format')),
|
|
||||||
'url' => $releaseInfo->html_url
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
catch (\Exception $ex)
|
|
||||||
{
|
|
||||||
return response()->json(['error' => $ex->getMessage()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function metadataUpgrade()
|
|
||||||
{
|
|
||||||
$albumIDs = DbHelper::getAlbumIDsForCurrentUser();
|
|
||||||
|
|
||||||
$photoMetadata = DB::table('photos')
|
|
||||||
->whereIn('album_id', $albumIDs)
|
|
||||||
->select([
|
|
||||||
'album_id',
|
|
||||||
DB::raw('MIN(metadata_version) AS min_metadata_version')
|
|
||||||
])
|
|
||||||
->groupBy('album_id')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$resultingAlbumIDs = [];
|
|
||||||
foreach ($photoMetadata as $metadata)
|
|
||||||
{
|
|
||||||
if (isset($metadata->min_metadata_version) && $metadata->min_metadata_version > 0)
|
|
||||||
{
|
|
||||||
$resultingAlbumIDs[$metadata->album_id] = $metadata->min_metadata_version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now load the full album definitions
|
|
||||||
$albumsQuery = DbHelper::getAlbumsForCurrentUser_NonPaged();
|
|
||||||
$albumsQuery->whereIn('id', array_keys($resultingAlbumIDs));
|
|
||||||
$albums = $albumsQuery->paginate(UserConfig::get('items_per_page'));
|
|
||||||
|
|
||||||
/** @var Album $album */
|
|
||||||
foreach ($resultingAlbumIDs as $albumID => $metadataMinVersion)
|
|
||||||
{
|
|
||||||
foreach ($albums as $album)
|
|
||||||
{
|
|
||||||
if ($album->id == $albumID)
|
|
||||||
{
|
|
||||||
$album->min_metadata_version = $metadataMinVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('admin.metadata_upgrade', [
|
|
||||||
'albums' => $albums,
|
|
||||||
'current_metadata_version' => PhotoService::METADATA_VERSION
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$this->authorizeAccessToAdminPanel();
|
$this->authorizeAccessToAdminPanel();
|
||||||
|
|
||||||
$albumCount = count(DbHelper::getAlbumIDsForCurrentUser());
|
$albumCount = DbHelper::getAlbumsForCurrentUser()->count();
|
||||||
$photoCount = Photo::all()->count();
|
$photoCount = Photo::all()->count();
|
||||||
$groupCount = Group::all()->count();
|
$groupCount = Group::all()->count();
|
||||||
$labelCount = Label::all()->count();
|
|
||||||
$commentCount = PhotoComment::whereNotNull('approved_at')->count();
|
|
||||||
$userCount = User::where('is_activated', true)->count();
|
$userCount = User::where('is_activated', true)->count();
|
||||||
|
|
||||||
$minMetadataVersion = Photo::min('metadata_version');
|
|
||||||
$metadataUpgradeNeeded = $minMetadataVersion > 0 && $minMetadataVersion < PhotoService::METADATA_VERSION;
|
|
||||||
|
|
||||||
// Default to a supported function call to get the OS version
|
|
||||||
$osVersion = sprintf('%s %s', php_uname('s'), php_uname('r'));
|
|
||||||
|
|
||||||
// If the exec() function is enabled, we can do a bit better
|
|
||||||
if (MiscHelper::isExecEnabled())
|
|
||||||
{
|
|
||||||
$osVersion = exec('lsb_release -ds 2>/dev/null || cat /etc/*release 2>/dev/null | head -n1 || uname -om');
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('admin.index', [
|
return Theme::render('admin.index', [
|
||||||
'album_count' => $albumCount,
|
'album_count' => $albumCount,
|
||||||
'app_version' => config('app.version'),
|
'app_version' => config('app.version'),
|
||||||
'comment_count' => $commentCount,
|
|
||||||
'group_count' => $groupCount,
|
'group_count' => $groupCount,
|
||||||
'label_count' => $labelCount,
|
|
||||||
'memory_limit' => ini_get('memory_limit'),
|
'memory_limit' => ini_get('memory_limit'),
|
||||||
'metadata_upgrade_needed' => $metadataUpgradeNeeded,
|
|
||||||
'photo_count' => $photoCount,
|
'photo_count' => $photoCount,
|
||||||
'php_version' => phpversion(),
|
'php_version' => phpversion(),
|
||||||
'os_version' => $osVersion,
|
'os_version' => exec('lsb_release -ds 2>/dev/null || cat /etc/*release 2>/dev/null | head -n1 || uname -om'),
|
||||||
'server_name' => gethostname(),
|
'server_name' => gethostname(),
|
||||||
'upload_file_size' => ini_get('upload_max_filesize'),
|
'upload_file_size' => ini_get('upload_max_filesize'),
|
||||||
'upload_max_limit' => ini_get('post_max_size'),
|
'upload_max_limit' => ini_get('post_max_size'),
|
||||||
@ -173,104 +53,26 @@ class DefaultController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function quickUpload(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-albums');
|
|
||||||
|
|
||||||
$returnUrl = $request->headers->get('referer');
|
|
||||||
if (!MiscHelper::isSafeUrl($returnUrl))
|
|
||||||
{
|
|
||||||
$returnUrl = route('home');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-validate the upload before passing to the Photos controller
|
|
||||||
$files = $request->files->get('photo');
|
|
||||||
if (!is_array($files) || count($files) == 0)
|
|
||||||
{
|
|
||||||
$request->session()->flash('error', trans('admin.quick_upload.no_image_provided'));
|
|
||||||
return redirect($returnUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
$albumID = $request->get('album_id');
|
|
||||||
if (intval($albumID) == 0)
|
|
||||||
{
|
|
||||||
$albumName = trim($request->get('album_name'));
|
|
||||||
if (strlen($albumName) == 0)
|
|
||||||
{
|
|
||||||
$request->session()->flash('error', trans('admin.quick_upload.no_album_selected'));
|
|
||||||
return redirect($returnUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
$album = new Album();
|
|
||||||
$album->storage_id = Storage::where('is_default', true)->first()->id;
|
|
||||||
$album->user_id = Auth::user()->id;
|
|
||||||
$album->default_view = UserConfig::get('default_album_view');
|
|
||||||
$album->name = $albumName;
|
|
||||||
$album->description = '';
|
|
||||||
$album->is_permissions_inherited = true;
|
|
||||||
$album->save();
|
|
||||||
|
|
||||||
// Rebuild the permissions cache
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$helper->rebuildCache();
|
|
||||||
|
|
||||||
$request->request->set('album_id', $album->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var PhotoController $photoController */
|
|
||||||
$photoController = app(PhotoController::class);
|
|
||||||
return $photoController->store($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rebuildPermissionsCache()
|
|
||||||
{
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$helper->rebuildCache();
|
|
||||||
|
|
||||||
return response()->json(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function saveSettings(SaveSettingsRequest $request)
|
public function saveSettings(SaveSettingsRequest $request)
|
||||||
{
|
{
|
||||||
$this->authorizeAccessToAdminPanel('admin:configure');
|
$this->authorizeAccessToAdminPanel('admin:configure');
|
||||||
|
|
||||||
|
$passwordKeys = [
|
||||||
|
'smtp_password'
|
||||||
|
];
|
||||||
$checkboxKeys = [
|
$checkboxKeys = [
|
||||||
'albums_menu_parents_only',
|
|
||||||
'allow_photo_comments',
|
|
||||||
'allow_photo_comments_anonymous',
|
|
||||||
'allow_self_registration',
|
'allow_self_registration',
|
||||||
'enable_visitor_hits',
|
'enable_visitor_hits',
|
||||||
'hotlink_protection',
|
'hotlink_protection',
|
||||||
'moderate_anonymous_users',
|
|
||||||
'moderate_known_users',
|
|
||||||
'queue_emails',
|
|
||||||
'rabbitmq_enabled',
|
|
||||||
'recaptcha_enabled_registration',
|
'recaptcha_enabled_registration',
|
||||||
'remove_copyright',
|
'remove_copyright',
|
||||||
'require_email_verification',
|
'require_email_verification',
|
||||||
'restrict_original_download',
|
'restrict_original_download',
|
||||||
'smtp_encryption',
|
'smtp_encryption',
|
||||||
'social_facebook_login',
|
|
||||||
'social_google_login',
|
|
||||||
'social_twitter_login',
|
|
||||||
'social_user_feeds',
|
|
||||||
'social_user_profiles'
|
|
||||||
];
|
];
|
||||||
$updateKeys = [
|
$updateKeys = [
|
||||||
'albums_menu_number_items',
|
|
||||||
'analysis_queue_storage_location',
|
|
||||||
'app_name',
|
'app_name',
|
||||||
'date_format',
|
'date_format',
|
||||||
'facebook_external_service_id',
|
|
||||||
'google_external_service_id',
|
|
||||||
'photo_comments_allowed_html',
|
|
||||||
'photo_comments_thread_depth',
|
|
||||||
'rabbitmq_server',
|
|
||||||
'rabbitmq_port',
|
|
||||||
'rabbitmq_username',
|
|
||||||
'rabbitmq_password',
|
|
||||||
'rabbitmq_queue',
|
|
||||||
'rabbitmq_vhost',
|
|
||||||
'sender_address',
|
'sender_address',
|
||||||
'sender_name',
|
'sender_name',
|
||||||
'smtp_server',
|
'smtp_server',
|
||||||
@ -278,7 +80,6 @@ class DefaultController extends Controller
|
|||||||
'smtp_username',
|
'smtp_username',
|
||||||
'smtp_password',
|
'smtp_password',
|
||||||
'theme',
|
'theme',
|
||||||
'twitter_external_service_id',
|
|
||||||
'recaptcha_site_key',
|
'recaptcha_site_key',
|
||||||
'recaptcha_secret_key',
|
'recaptcha_secret_key',
|
||||||
'analytics_code'
|
'analytics_code'
|
||||||
@ -293,24 +94,17 @@ class DefaultController extends Controller
|
|||||||
// Bit of a hack when the browser returns an empty password field - meaning the user didn't change it
|
// Bit of a hack when the browser returns an empty password field - meaning the user didn't change it
|
||||||
// - don't touch it!
|
// - don't touch it!
|
||||||
if (
|
if (
|
||||||
(
|
$key == 'smtp_password' &&
|
||||||
$key == 'smtp_password' &&
|
strlen($config->value) > 0 &&
|
||||||
strlen($config->value) > 0 &&
|
strlen($request->request->get($key)) == 0 &&
|
||||||
strlen($request->request->get($key)) == 0 &&
|
strlen($request->request->get('smtp_username')) > 0
|
||||||
strlen($request->request->get('smtp_username')) > 0
|
|
||||||
) || (
|
|
||||||
$key == 'rabbitmq_password' &&
|
|
||||||
strlen($config->value) > 0 &&
|
|
||||||
strlen($request->request->get($key)) == 0 &&
|
|
||||||
strlen($request->request->get('rabbitmq_username')) > 0
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$config->value = $request->request->get($key);
|
$config->value = $request->request->get($key);
|
||||||
if (in_array($key, $this->passwordSettingKeys) && strlen($config->value) > 0)
|
if (in_array($key, $passwordKeys) && strlen($config->value) > 0)
|
||||||
{
|
{
|
||||||
$config->value = encrypt($config->value);
|
$config->value = encrypt($config->value);
|
||||||
}
|
}
|
||||||
@ -359,43 +153,13 @@ class DefaultController extends Controller
|
|||||||
$dateFormatsLookup[$dateFormat] = date($dateFormat);
|
$dateFormatsLookup[$dateFormat] = date($dateFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->passwordSettingKeys as $passwordSettingKey)
|
|
||||||
{
|
|
||||||
if (isset($config[$passwordSettingKey]) && !empty($config[$passwordSettingKey]))
|
|
||||||
{
|
|
||||||
$config[$passwordSettingKey] = decrypt($config[$passwordSettingKey]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$themeNamesLookup = UserConfig::allowedThemeNames();
|
$themeNamesLookup = UserConfig::allowedThemeNames();
|
||||||
|
|
||||||
// Storage sources for the Image Processing tab
|
|
||||||
$storageSources = AnalysisQueueHelper::getCompatibleStorages();
|
|
||||||
|
|
||||||
// External services
|
|
||||||
$externalServices = ExternalService::all();
|
|
||||||
$facebookServices = $externalServices->filter(function (ExternalService $item)
|
|
||||||
{
|
|
||||||
return $item->service_type == ExternalService::FACEBOOK;
|
|
||||||
});
|
|
||||||
$googleServices = $externalServices->filter(function (ExternalService $item)
|
|
||||||
{
|
|
||||||
return $item->service_type == ExternalService::GOOGLE;
|
|
||||||
});
|
|
||||||
$twitterServices = $externalServices->filter(function (ExternalService $item)
|
|
||||||
{
|
|
||||||
return $item->service_type == ExternalService::TWITTER;
|
|
||||||
});
|
|
||||||
|
|
||||||
return Theme::render('admin.settings', [
|
return Theme::render('admin.settings', [
|
||||||
'config' => $config,
|
'config' => $config,
|
||||||
'date_formats' => $dateFormatsLookup,
|
'date_formats' => $dateFormatsLookup,
|
||||||
'facebookServices' => $facebookServices,
|
|
||||||
'googleServices' => $googleServices,
|
|
||||||
'storage_sources' => $storageSources,
|
|
||||||
'success' => $request->session()->get('success'),
|
'success' => $request->session()->get('success'),
|
||||||
'theme_names' => $themeNamesLookup,
|
'theme_names' => $themeNamesLookup
|
||||||
'twitterServices' => $twitterServices
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class GroupController extends Controller
|
|||||||
|
|
||||||
public function delete($id)
|
public function delete($id)
|
||||||
{
|
{
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-groups');
|
$this->authorizeAccessToAdminPanel();
|
||||||
|
|
||||||
$group = Group::where('id', intval($id))->first();
|
$group = Group::where('id', intval($id))->first();
|
||||||
if (is_null($group))
|
if (is_null($group))
|
||||||
|
@ -1,125 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
|
||||||
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Http\Requests\StoreLabelRequest;
|
|
||||||
use App\Label;
|
|
||||||
use App\Photo;
|
|
||||||
use Illuminate\Support\Facades\App;
|
|
||||||
use Illuminate\Support\Facades\View;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
|
|
||||||
class LabelController extends Controller
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware(['auth', 'max_post_size_exceeded']);
|
|
||||||
View::share('is_admin', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies a comma-separated string of label IDs and/or new label texts to a photo. This is called from the
|
|
||||||
* PhotoController - not directly via a route.
|
|
||||||
* @param Photo $photo Photo to apply the labels to
|
|
||||||
* @param string $labelString CSV string of label IDs and new labels to create (e.g. "1,2,Florida,nature" would
|
|
||||||
* link label IDs 1 and 2, and create 2 new labels called Florida and nature.)
|
|
||||||
*/
|
|
||||||
public function applyLabelsToPhoto(Photo $photo, $labelString)
|
|
||||||
{
|
|
||||||
foreach (explode(',', $labelString) as $labelText)
|
|
||||||
{
|
|
||||||
$labelID = intval($labelText);
|
|
||||||
if (intval($labelID) == 0)
|
|
||||||
{
|
|
||||||
// Check if the label already exists
|
|
||||||
$labelToUse = Label::where('name', $labelText)->first();
|
|
||||||
|
|
||||||
if (is_null($labelToUse))
|
|
||||||
{
|
|
||||||
// Create new label
|
|
||||||
$labelToUse = new Label();
|
|
||||||
$labelToUse->name = $labelText;
|
|
||||||
$labelToUse->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
$labelID = $labelToUse->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$photo->labels()->attach(intval($labelID));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete($id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel();
|
|
||||||
|
|
||||||
$label = $this->loadLabel($id);
|
|
||||||
return Theme::render('admin.delete_label', ['label' => $label]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the specified resource from storage.
|
|
||||||
*
|
|
||||||
* @param int $id
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function destroy(Request $request, $id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-labels');
|
|
||||||
|
|
||||||
$label = $this->loadLabel($id);
|
|
||||||
$label->delete();
|
|
||||||
|
|
||||||
$request->session()->flash('success', trans('admin.delete_label_success_message', ['name' => $label->name]));
|
|
||||||
|
|
||||||
return redirect(route('labels.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function index()
|
|
||||||
{
|
|
||||||
$labels = Label::withCount('photos')->get();
|
|
||||||
return Theme::render('admin.list_labels', [
|
|
||||||
'labels' => $labels
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource in storage.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function store(StoreLabelRequest $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-labels');
|
|
||||||
|
|
||||||
$label = new Label();
|
|
||||||
$label->fill($request->only(['name']));
|
|
||||||
$label->save();
|
|
||||||
|
|
||||||
return redirect(route('labels.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $id
|
|
||||||
* @return Album
|
|
||||||
*/
|
|
||||||
private function loadLabel($id)
|
|
||||||
{
|
|
||||||
$label = Label::where('id', intval($id))->first();
|
|
||||||
if (is_null($label))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $label;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,375 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Notifications\PhotoCommentApproved;
|
|
||||||
use App\Notifications\PhotoCommentApprovedUser;
|
|
||||||
use App\Notifications\PhotoCommentRepliedTo;
|
|
||||||
use App\Photo;
|
|
||||||
use App\PhotoComment;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\View;
|
|
||||||
|
|
||||||
class PhotoCommentController extends Controller
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('auth');
|
|
||||||
View::share('is_admin', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function applyBulkAction(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-comments');
|
|
||||||
|
|
||||||
$commentIDs = $request->get('comment_ids');
|
|
||||||
if (is_null($commentIDs) || !is_array($commentIDs) || count($commentIDs) == 0)
|
|
||||||
{
|
|
||||||
$request->session()->flash('warning', trans('admin.no_comments_selected_message'));
|
|
||||||
return redirect(route('comments.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$comments = PhotoComment::whereIn('id', $commentIDs)->get();
|
|
||||||
$commentsActioned = 0;
|
|
||||||
|
|
||||||
if ($request->has('bulk_delete'))
|
|
||||||
{
|
|
||||||
/** @var PhotoComment $comment */
|
|
||||||
foreach ($comments as $comment)
|
|
||||||
{
|
|
||||||
$comment->delete();
|
|
||||||
$commentsActioned++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$request->session()->flash('success', trans_choice('admin.bulk_comments_deleted', $commentsActioned, ['number' => $commentsActioned]));
|
|
||||||
}
|
|
||||||
else if ($request->has('bulk_approve'))
|
|
||||||
{
|
|
||||||
/** @var PhotoComment $comment */
|
|
||||||
foreach ($comments as $comment)
|
|
||||||
{
|
|
||||||
if ($comment->isApproved())
|
|
||||||
{
|
|
||||||
// Don't make changes if already approved
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as approved
|
|
||||||
$comment->approved_at = new \DateTime();
|
|
||||||
$comment->approved_user_id = $this->getUser()->id;
|
|
||||||
|
|
||||||
// The comment may have already been rejected - remove the data if so
|
|
||||||
$comment->rejected_at = null;
|
|
||||||
$comment->rejected_user_id = null;
|
|
||||||
|
|
||||||
// Send the notification e-mail to the owner
|
|
||||||
|
|
||||||
$comment->save();
|
|
||||||
$commentsActioned++;
|
|
||||||
|
|
||||||
// Send e-mail notification
|
|
||||||
$photo = $comment->photo;
|
|
||||||
$album = $photo->album;
|
|
||||||
$this->notifyAlbumOwnerAndPoster($album, $photo, $comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
$request->session()->flash('success', trans_choice('admin.bulk_comments_approved', $commentsActioned, ['number' => $commentsActioned]));
|
|
||||||
}
|
|
||||||
else if ($request->has('bulk_reject'))
|
|
||||||
{
|
|
||||||
/** @var PhotoComment $comment */
|
|
||||||
foreach ($comments as $comment)
|
|
||||||
{
|
|
||||||
if ($comment->isRejected())
|
|
||||||
{
|
|
||||||
// Don't make changes if already rejected
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as rejected
|
|
||||||
$comment->rejected_at = new \DateTime();
|
|
||||||
$comment->rejected_user_id = $this->getUser()->id;
|
|
||||||
|
|
||||||
// The comment may have already been approved - remove the data if so
|
|
||||||
$comment->approved_at = null;
|
|
||||||
$comment->approved_user_id = null;
|
|
||||||
|
|
||||||
$comment->save();
|
|
||||||
$commentsActioned++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$request->session()->flash('success', trans_choice('admin.bulk_comments_approved', $commentsActioned, ['number' => $commentsActioned]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect(route('comments.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function approve($id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-comments');
|
|
||||||
|
|
||||||
$comment = $this->loadCommentByID($id);
|
|
||||||
|
|
||||||
return Theme::render('admin.approve_comment', ['comment' => $comment]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function bulkAction(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-comments');
|
|
||||||
|
|
||||||
$commentIDs = $request->get('comment_ids');
|
|
||||||
if (is_null($commentIDs) || !is_array($commentIDs) || count($commentIDs) == 0)
|
|
||||||
{
|
|
||||||
$request->session()->flash('warning', trans('admin.no_comments_selected_message'));
|
|
||||||
return redirect(route('comments.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($request->has('bulk_delete'))
|
|
||||||
{
|
|
||||||
if (count($commentIDs) == 1)
|
|
||||||
{
|
|
||||||
// Single comment selected - redirect to the single delete page
|
|
||||||
return redirect(route('comments.delete', ['comment' => $commentIDs[0]]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show the view to confirm the delete
|
|
||||||
return Theme::render('admin.bulk_delete_comments', [
|
|
||||||
'comment_count' => count($commentIDs),
|
|
||||||
'comment_ids' => $commentIDs
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
else if ($request->has('bulk_approve'))
|
|
||||||
{
|
|
||||||
if (count($commentIDs) == 1)
|
|
||||||
{
|
|
||||||
// Single comment selected - redirect to the single approve page
|
|
||||||
return redirect(route('comments.approve', ['comment' => $commentIDs[0]]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show the view to confirm the approval
|
|
||||||
return Theme::render('admin.bulk_approve_comments', [
|
|
||||||
'comment_count' => count($commentIDs),
|
|
||||||
'comment_ids' => $commentIDs
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
else if ($request->has('bulk_reject'))
|
|
||||||
{
|
|
||||||
if (count($commentIDs) == 1)
|
|
||||||
{
|
|
||||||
// Single comment selected - redirect to the single reject page
|
|
||||||
return redirect(route('comments.reject', ['comment' => $commentIDs[0]]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show the view to confirm the rejection
|
|
||||||
return Theme::render('admin.bulk_reject_comments', [
|
|
||||||
'comment_count' => count($commentIDs),
|
|
||||||
'comment_ids' => $commentIDs
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unrecognised action - simply redirect back to the index page
|
|
||||||
return redirect(route('comments.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function confirmApprove(Request $request, $id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-comments');
|
|
||||||
|
|
||||||
$comment = $this->loadCommentByID($id);
|
|
||||||
|
|
||||||
if ($comment->isApproved())
|
|
||||||
{
|
|
||||||
// Comment has already been approved
|
|
||||||
return redirect(route('comments.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as approved
|
|
||||||
$comment->approved_at = new \DateTime();
|
|
||||||
$comment->approved_user_id = $this->getUser()->id;
|
|
||||||
|
|
||||||
// The comment may have already been rejected - remove the data if so
|
|
||||||
$comment->rejected_at = null;
|
|
||||||
$comment->rejected_user_id = null;
|
|
||||||
|
|
||||||
$comment->save();
|
|
||||||
|
|
||||||
$request->session()->flash('success', trans('admin.comment_approval_successful', [
|
|
||||||
'author_name' => $comment->authorDisplayName()
|
|
||||||
]));
|
|
||||||
|
|
||||||
// Send e-mail notification
|
|
||||||
$photo = $comment->photo;
|
|
||||||
$album = $photo->album;
|
|
||||||
$this->notifyAlbumOwnerAndPoster($album, $photo, $comment);
|
|
||||||
|
|
||||||
return redirect(route('comments.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function confirmReject(Request $request, $id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-comments');
|
|
||||||
|
|
||||||
$comment = $this->loadCommentByID($id);
|
|
||||||
|
|
||||||
if ($comment->isRejected())
|
|
||||||
{
|
|
||||||
// Comment has already been rejected
|
|
||||||
return redirect(route('comments.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as rejected
|
|
||||||
$comment->rejected_at = new \DateTime();
|
|
||||||
$comment->rejected_user_id = $this->getUser()->id;
|
|
||||||
|
|
||||||
// The comment may have already been approved - remove the data if so
|
|
||||||
$comment->approved_at = null;
|
|
||||||
$comment->approved_user_id = null;
|
|
||||||
|
|
||||||
$comment->save();
|
|
||||||
|
|
||||||
$request->session()->flash('success', trans('admin.comment_rejection_successful', [
|
|
||||||
'author_name' => $comment->authorDisplayName()
|
|
||||||
]));
|
|
||||||
|
|
||||||
return redirect(route('comments.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete($id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-comments');
|
|
||||||
|
|
||||||
$comment = $this->loadCommentByID($id);
|
|
||||||
|
|
||||||
return Theme::render('admin.delete_comment', ['comment' => $comment]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy(Request $request, $id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-comments');
|
|
||||||
|
|
||||||
/** @var PhotoComment $comment */
|
|
||||||
$comment = $this->loadCommentByID($id);
|
|
||||||
|
|
||||||
$comment->delete();
|
|
||||||
$request->session()->flash('success', trans('admin.comment_deletion_successful', [
|
|
||||||
'author_name' => $comment->authorDisplayName()
|
|
||||||
]));
|
|
||||||
|
|
||||||
return redirect(route('comments.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-comments');
|
|
||||||
|
|
||||||
$validStatusList = [
|
|
||||||
'all',
|
|
||||||
'pending',
|
|
||||||
'approved',
|
|
||||||
'rejected'
|
|
||||||
];
|
|
||||||
|
|
||||||
$filterStatus = $request->get('status', 'all');
|
|
||||||
if (!in_array($filterStatus, $validStatusList))
|
|
||||||
{
|
|
||||||
$filterStatus = $validStatusList[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
$comments = PhotoComment::with('photo')
|
|
||||||
->with('photo.album')
|
|
||||||
->orderBy('created_at', 'desc');
|
|
||||||
|
|
||||||
switch (strtolower($filterStatus))
|
|
||||||
{
|
|
||||||
case 'approved':
|
|
||||||
$comments->whereNotNull('approved_at')
|
|
||||||
->whereNull('rejected_at');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'pending':
|
|
||||||
$comments->whereNull('approved_at')
|
|
||||||
->whereNull('rejected_at');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'rejected':
|
|
||||||
$comments->whereNull('approved_at')
|
|
||||||
->whereNotNull('rejected_at');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('admin.list_comments', [
|
|
||||||
'comments' => $comments->paginate(UserConfig::get('items_per_page')),
|
|
||||||
'filter_status' => $filterStatus,
|
|
||||||
'success' => $request->session()->get('success'),
|
|
||||||
'warning' => $request->session()->get('warning')
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reject($id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-comments');
|
|
||||||
|
|
||||||
$comment = $this->loadCommentByID($id);
|
|
||||||
|
|
||||||
return Theme::render('admin.reject_comment', ['comment' => $comment]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a given comment by its ID.
|
|
||||||
* @param $id
|
|
||||||
* @return PhotoComment
|
|
||||||
*/
|
|
||||||
private function loadCommentByID($id)
|
|
||||||
{
|
|
||||||
$comment = PhotoComment::where('id', intval($id))->first();
|
|
||||||
if (is_null($comment))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends an e-mail notification to an album's owned that a comment has been posted/approved.
|
|
||||||
* @param Album $album
|
|
||||||
* @param Photo $photo
|
|
||||||
* @param PhotoComment $comment
|
|
||||||
*/
|
|
||||||
private function notifyAlbumOwnerAndPoster(Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
/** @var User $owner */
|
|
||||||
$owner = $album->user;
|
|
||||||
|
|
||||||
$owner->notify(new PhotoCommentApproved($album, $photo, $comment));
|
|
||||||
|
|
||||||
// Also send a notification to the comment poster
|
|
||||||
$poster = new User();
|
|
||||||
$poster->name = $comment->authorDisplayName();
|
|
||||||
$poster->email = $comment->authorEmail();
|
|
||||||
|
|
||||||
$poster->notify(new PhotoCommentApprovedUser($album, $photo, $comment));
|
|
||||||
|
|
||||||
// Send notification to the parent comment owner (if this is a reply)
|
|
||||||
if (!is_null($comment->parent_comment_id))
|
|
||||||
{
|
|
||||||
$parentComment = $this->loadCommentByID($comment->parent_comment_id);
|
|
||||||
|
|
||||||
if (is_null($parentComment))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$parentPoster = new User();
|
|
||||||
$parentPoster->name = $parentComment->authorDisplayName();
|
|
||||||
$parentPoster->email = $parentComment->authorEmail();
|
|
||||||
|
|
||||||
$parentPoster->notify(new PhotoCommentRepliedTo($album, $photo, $comment));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,25 +3,25 @@
|
|||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Album;
|
use App\Album;
|
||||||
use App\Facade\UserConfig;
|
use App\AlbumSources\IAlbumSource;
|
||||||
use App\Helpers\AnalysisQueueHelper;
|
use App\Facade\Image;
|
||||||
|
use App\Facade\Theme;
|
||||||
use App\Helpers\FileHelper;
|
use App\Helpers\FileHelper;
|
||||||
|
use App\Helpers\ImageHelper;
|
||||||
use App\Helpers\MiscHelper;
|
use App\Helpers\MiscHelper;
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Http\Requests\UpdatePhotosBulkRequest;
|
use App\Http\Requests\UpdatePhotosBulkRequest;
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
use App\QueueItem;
|
|
||||||
use App\Services\PhotoService;
|
use App\Services\PhotoService;
|
||||||
use App\Services\RabbitMQService;
|
|
||||||
use App\Upload;
|
use App\Upload;
|
||||||
use App\UploadPhoto;
|
use App\UploadPhoto;
|
||||||
use App\UserActivity;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
|
use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
|
||||||
use Symfony\Component\HttpFoundation\File\File;
|
use Symfony\Component\HttpFoundation\File\File;
|
||||||
|
|
||||||
class PhotoController extends Controller
|
class PhotoController extends Controller
|
||||||
@ -48,76 +48,17 @@ class PhotoController extends Controller
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (UserConfig::isImageProcessingQueueEnabled())
|
$photoService = new PhotoService($photo);
|
||||||
{
|
$photoService->analyse($queue_token);
|
||||||
// Find the last record that is analysing this photo
|
|
||||||
$photoQueueItem = QueueItem::where('photo_id', $photo->id)
|
|
||||||
->orderBy('queued_at', 'desc')
|
|
||||||
->limit(1)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
$timeToWait = 60;
|
$result['is_successful'] = true;
|
||||||
$timeWaited = 0;
|
|
||||||
$continueToMonitor = true;
|
|
||||||
|
|
||||||
while ($continueToMonitor && $timeWaited < $timeToWait)
|
|
||||||
{
|
|
||||||
$continueToMonitor = is_null($photoQueueItem->completed_at);
|
|
||||||
if ($continueToMonitor)
|
|
||||||
{
|
|
||||||
sleep(1);
|
|
||||||
$timeWaited++;
|
|
||||||
|
|
||||||
$photoQueueItem = QueueItem::where('id', $photoQueueItem->id)->first();
|
|
||||||
$continueToMonitor = is_null($photoQueueItem->completed_at);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$didComplete = !is_null($photoQueueItem->completed_at);
|
|
||||||
if (!$didComplete)
|
|
||||||
{
|
|
||||||
$result['message'] = 'Timed out waiting for queue processing.';
|
|
||||||
}
|
|
||||||
else if (!$photoQueueItem->is_successful)
|
|
||||||
{
|
|
||||||
$result['is_successful'] = false;
|
|
||||||
$result['message'] = $photoQueueItem->error_message;
|
|
||||||
|
|
||||||
// Remove the photo from the album if it was newly-uploaded and couldn't be processed
|
|
||||||
if (intval($photo->metadata_version) === 0)
|
|
||||||
{
|
|
||||||
$photo->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$result['is_successful'] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* IF CHANGING THIS LOGIC, ALSO CHECK ProcessQueueCommand::processPhotoAnalyseMessage */
|
|
||||||
$photoService = new PhotoService($photo);
|
|
||||||
$photoService->analyse($queue_token);
|
|
||||||
|
|
||||||
// Log an activity record for the user's feed (remove an existing one as the date may have changed)
|
|
||||||
$this->removeExistingActivityRecords($photo, 'photo.taken');
|
|
||||||
|
|
||||||
if (!is_null($photo->taken_at))
|
|
||||||
{
|
|
||||||
// Log an activity record for the user's feed
|
|
||||||
$this->createActivityRecord($photo, 'photo.taken', $photo->taken_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result['is_successful'] = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (\Exception $ex)
|
catch (\Exception $ex)
|
||||||
{
|
{
|
||||||
$result['is_successful'] = false;
|
$result['is_successful'] = false;
|
||||||
$result['message'] = $ex->getMessage();
|
$result['message'] = $ex->getMessage();
|
||||||
|
|
||||||
// Remove the photo if it cannot be analysed (only if there isn't currently a version of metadata)
|
// Remove the photo if it cannot be analysed
|
||||||
$photo->delete();
|
$photo->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +103,7 @@ class PhotoController extends Controller
|
|||||||
$request->session()->flash('success', trans('admin.delete_photo_successful_message', ['name' => $photo->name]));
|
$request->session()->flash('success', trans('admin.delete_photo_successful_message', ['name' => $photo->name]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function flip(Request $request, $photoId, $horizontal, $vertical)
|
public function flip($photoId, $horizontal, $vertical)
|
||||||
{
|
{
|
||||||
$this->authorizeAccessToAdminPanel();
|
$this->authorizeAccessToAdminPanel();
|
||||||
|
|
||||||
@ -173,11 +114,6 @@ class PhotoController extends Controller
|
|||||||
|
|
||||||
$photoService = new PhotoService($photo);
|
$photoService = new PhotoService($photo);
|
||||||
$photoService->flip($horizontal, $vertical);
|
$photoService->flip($horizontal, $vertical);
|
||||||
|
|
||||||
// Log an activity record for the user's feed
|
|
||||||
$this->createActivityRecord($photo, 'photo.edited');
|
|
||||||
|
|
||||||
return $photo->thumbnailUrl($request->get('t', 'admin-preview'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function move(Request $request, $photoId)
|
public function move(Request $request, $photoId)
|
||||||
@ -207,49 +143,13 @@ class PhotoController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reAnalyse($id, $queue_token)
|
public function regenerateThumbnails($photoId)
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel();
|
|
||||||
|
|
||||||
/** @var Photo $photo */
|
|
||||||
$photo = $this->loadPhoto($id);
|
|
||||||
$result = ['is_successful' => false, 'message' => ''];
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$photoService = new PhotoService($photo);
|
|
||||||
$photoService->downloadOriginalToFolder(FileHelper::getQueuePath($queue_token));
|
|
||||||
$photoService->analyse($queue_token);
|
|
||||||
|
|
||||||
// Log an activity record for the user's feed (remove an existing one as the date may have changed)
|
|
||||||
$this->removeExistingActivityRecords($photo, 'photo.taken');
|
|
||||||
|
|
||||||
if (!is_null($photo->taken_at))
|
|
||||||
{
|
|
||||||
// Log an activity record for the user's feed
|
|
||||||
$this->createActivityRecord($photo, 'photo.taken', $photo->taken_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result['is_successful'] = true;
|
|
||||||
}
|
|
||||||
catch (\Exception $ex)
|
|
||||||
{
|
|
||||||
$result['is_successful'] = false;
|
|
||||||
$result['message'] = $ex->getMessage();
|
|
||||||
|
|
||||||
// Unlike the analyse method, we don't remove the photo if it cannot be analysed
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json($result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function regenerateThumbnails(Request $request, $photoId)
|
|
||||||
{
|
{
|
||||||
$this->authorizeAccessToAdminPanel();
|
$this->authorizeAccessToAdminPanel();
|
||||||
|
|
||||||
$photo = $this->loadPhoto($photoId, 'change-metadata');
|
$photo = $this->loadPhoto($photoId, 'change-metadata');
|
||||||
|
|
||||||
$result = ['is_successful' => false, 'message' => '', 'thumbnail_url' => ''];
|
$result = ['is_successful' => false, 'message' => ''];
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -257,7 +157,6 @@ class PhotoController extends Controller
|
|||||||
$photoService->regenerateThumbnails();
|
$photoService->regenerateThumbnails();
|
||||||
|
|
||||||
$result['is_successful'] = true;
|
$result['is_successful'] = true;
|
||||||
$result['thumbnail_url'] = $photo->thumbnailUrl($request->get('t', 'admin-preview'));
|
|
||||||
}
|
}
|
||||||
catch (\Exception $ex)
|
catch (\Exception $ex)
|
||||||
{
|
{
|
||||||
@ -268,7 +167,7 @@ class PhotoController extends Controller
|
|||||||
return response()->json($result);
|
return response()->json($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rotate(Request $request, $photoId, $angle)
|
public function rotate($photoId, $angle)
|
||||||
{
|
{
|
||||||
$this->authorizeAccessToAdminPanel();
|
$this->authorizeAccessToAdminPanel();
|
||||||
|
|
||||||
@ -276,17 +175,12 @@ class PhotoController extends Controller
|
|||||||
|
|
||||||
if ($angle != 90 && $angle != 180 && $angle != 270)
|
if ($angle != 90 && $angle != 180 && $angle != 270)
|
||||||
{
|
{
|
||||||
App::abort(400);
|
App::aport(400);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$photoService = new PhotoService($photo);
|
$photoService = new PhotoService($photo);
|
||||||
$photoService->rotate($angle);
|
$photoService->rotate($angle);
|
||||||
|
|
||||||
// Log an activity record for the user's feed
|
|
||||||
$this->createActivityRecord($photo, 'photo.edited');
|
|
||||||
|
|
||||||
return $photo->thumbnailUrl($request->get('t', 'admin-preview'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -312,7 +206,7 @@ class PhotoController extends Controller
|
|||||||
throw new \Exception('No queue_token value was provided!');
|
throw new \Exception('No queue_token value was provided!');
|
||||||
}
|
}
|
||||||
|
|
||||||
$queueStorage = AnalysisQueueHelper::getStorageQueueSource();
|
$queueFolder = FileHelper::getQueuePath($queueUid);
|
||||||
|
|
||||||
foreach ($photoFiles as $photoFile)
|
foreach ($photoFiles as $photoFile)
|
||||||
{
|
{
|
||||||
@ -323,70 +217,21 @@ class PhotoController extends Controller
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
try
|
/** @var File $savedFile */
|
||||||
{
|
$savedFile = FileHelper::saveUploadedFile($photoFile, $queueFolder);
|
||||||
if ($request->has('photo_id'))
|
|
||||||
{
|
|
||||||
// Photo ID provided (using the Replace Photo function) - use that record
|
|
||||||
$photo = Photo::where('id', intval($request->get('photo_id')))->first();
|
|
||||||
$photo->raw_exif_data = null;
|
|
||||||
|
|
||||||
$queuedFileName = $queueStorage->uploadToAnalysisQueue($photoFile, $queueUid, $photo->storage_file_name);
|
$photo = new Photo();
|
||||||
$uploadedTempFile = new File($photoFile);
|
$photo->album_id = $album->id;
|
||||||
|
$photo->user_id = Auth::user()->id;
|
||||||
|
$photo->name = pathinfo($photoFile->getClientOriginalName(), PATHINFO_FILENAME);
|
||||||
|
$photo->file_name = $photoFile->getClientOriginalName();
|
||||||
|
$photo->storage_file_name = $savedFile->getFilename();
|
||||||
|
$photo->mime_type = $savedFile->getMimeType();
|
||||||
|
$photo->file_size = $savedFile->getSize();
|
||||||
|
$photo->is_analysed = false;
|
||||||
|
$photo->save();
|
||||||
|
|
||||||
$this->removeExistingActivityRecords($photo, 'photo.uploaded');
|
$isSuccessful = true;
|
||||||
$this->removeExistingActivityRecords($photo, 'photo.taken');
|
|
||||||
|
|
||||||
$photo->file_name = $photoFile->getClientOriginalName();
|
|
||||||
$photo->mime_type = $uploadedTempFile->getMimeType();
|
|
||||||
$photo->file_size = $uploadedTempFile->getSize();
|
|
||||||
$photo->storage_file_name = basename($queuedFileName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$queuedFileName = $queueStorage->uploadToAnalysisQueue($photoFile, $queueUid);
|
|
||||||
$uploadedTempFile = new File($photoFile);
|
|
||||||
|
|
||||||
$photo = new Photo();
|
|
||||||
$photo->album_id = $album->id;
|
|
||||||
$photo->user_id = Auth::user()->id;
|
|
||||||
$photo->name = pathinfo($photoFile->getClientOriginalName(), PATHINFO_FILENAME);
|
|
||||||
|
|
||||||
$photo->file_name = $photoFile->getClientOriginalName();
|
|
||||||
$photo->mime_type = $uploadedTempFile->getMimeType();
|
|
||||||
$photo->file_size = $uploadedTempFile->getSize();
|
|
||||||
$photo->storage_file_name = basename($queuedFileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
$photo->is_analysed = false;
|
|
||||||
$photo->save();
|
|
||||||
|
|
||||||
// Log an activity record for the user's feed
|
|
||||||
$this->createActivityRecord($photo, 'photo.uploaded');
|
|
||||||
|
|
||||||
// If queueing is enabled, store the photo in the queue now
|
|
||||||
if (UserConfig::isImageProcessingQueueEnabled())
|
|
||||||
{
|
|
||||||
$queueItem = new QueueItem([
|
|
||||||
'batch_reference' => $queueUid,
|
|
||||||
'action_type' => 'photo.analyse',
|
|
||||||
'album_id' => $photo->album_id,
|
|
||||||
'photo_id' => $photo->id,
|
|
||||||
'user_id' => $this->getUser()->id,
|
|
||||||
'queued_at' => new \DateTime()
|
|
||||||
]);
|
|
||||||
$queueItem->save();
|
|
||||||
|
|
||||||
$rabbitmq = new RabbitMQService();
|
|
||||||
$rabbitmq->queueItem($queueItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
$isSuccessful = true;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
@unlink($photoFile->getRealPath());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,7 +242,7 @@ class PhotoController extends Controller
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
return redirect(route('albums.analyse', [
|
return redirect(route('albums.analyse', [
|
||||||
'album' => $album->id,
|
'id' => $album->id,
|
||||||
'queue_token' => $queueUid
|
'queue_token' => $queueUid
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
@ -410,18 +255,12 @@ class PhotoController extends Controller
|
|||||||
// Load the linked album
|
// Load the linked album
|
||||||
$album = $this->loadAlbum($request->get('album_id'));
|
$album = $this->loadAlbum($request->get('album_id'));
|
||||||
|
|
||||||
if (is_null($request->files->get('archive')))
|
|
||||||
{
|
|
||||||
$request->session()->flash('error', trans('admin.upload_bulk_no_file'));
|
|
||||||
return redirect(route('albums.show', ['album' => $album->id]));
|
|
||||||
}
|
|
||||||
|
|
||||||
$archiveFile = UploadedFile::createFromBase($request->files->get('archive'));
|
$archiveFile = UploadedFile::createFromBase($request->files->get('archive'));
|
||||||
if ($archiveFile->getError() != UPLOAD_ERR_OK)
|
if ($archiveFile->getError() != UPLOAD_ERR_OK)
|
||||||
{
|
{
|
||||||
Log::error('Bulk image upload failed.', ['error' => $archiveFile->getError(), 'reason' => $archiveFile->getErrorMessage()]);
|
Log::error('Bulk image upload failed.', ['error' => $archiveFile->getError(), 'reason' => $archiveFile->getErrorMessage()]);
|
||||||
$request->session()->flash('error', $archiveFile->getErrorMessage());
|
$request->session()->flash('error', $archiveFile->getErrorMessage());
|
||||||
return redirect(route('albums.show', ['album' => $album->id]));
|
return redirect(route('albums.show', ['id' => $album->id]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the folder to hold the analysis results if not already present
|
// Create the folder to hold the analysis results if not already present
|
||||||
@ -431,104 +270,74 @@ class PhotoController extends Controller
|
|||||||
throw new \Exception('No queue_token value was provided!');
|
throw new \Exception('No queue_token value was provided!');
|
||||||
}
|
}
|
||||||
|
|
||||||
$temporaryFolder = sprintf('%s/%s', sys_get_temp_dir(), MiscHelper::randomString());
|
$queueFolder = FileHelper::getQueuePath($queueUid);
|
||||||
@mkdir($temporaryFolder);
|
|
||||||
|
|
||||||
try
|
$mimeType = strtolower($archiveFile->getMimeType());
|
||||||
|
switch ($mimeType)
|
||||||
{
|
{
|
||||||
$queueStorage = AnalysisQueueHelper::getStorageQueueSource();
|
case 'application/zip':
|
||||||
|
$zip = new \ZipArchive();
|
||||||
|
$zip->open($archiveFile->getPathname());
|
||||||
|
$zip->extractTo($queueFolder);
|
||||||
|
$zip->close();
|
||||||
|
@unlink($archiveFile->getPathname());
|
||||||
|
break;
|
||||||
|
|
||||||
$mimeType = strtolower($archiveFile->getMimeType());
|
default:
|
||||||
switch ($mimeType)
|
$request->session()->flash('error', sprintf('The file type "%s" is not supported for bulk uploads.', $mimeType));
|
||||||
{
|
return redirect(route('albums.show', ['id' => $album->id]));
|
||||||
case 'application/zip':
|
|
||||||
$zip = new \ZipArchive();
|
|
||||||
$zip->open($archiveFile->getPathname());
|
|
||||||
$zip->extractTo($temporaryFolder);
|
|
||||||
$zip->close();
|
|
||||||
@unlink($archiveFile->getPathname());
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$request->session()->flash('error', sprintf('The file type "%s" is not supported for bulk uploads.', $mimeType));
|
|
||||||
return redirect(route('albums.show', ['album' => $album->id]));
|
|
||||||
}
|
|
||||||
|
|
||||||
$di = new \RecursiveDirectoryIterator($temporaryFolder, \RecursiveDirectoryIterator::SKIP_DOTS);
|
|
||||||
$recursive = new \RecursiveIteratorIterator($di);
|
|
||||||
|
|
||||||
/** @var \SplFileInfo $fileInfo */
|
|
||||||
foreach ($recursive as $fileInfo)
|
|
||||||
{
|
|
||||||
if ($fileInfo->isDir())
|
|
||||||
{
|
|
||||||
if ($fileInfo->getFilename() == '__MACOSX' || substr($fileInfo->getFilename(), 0, 1) == '.')
|
|
||||||
{
|
|
||||||
@rmdir($fileInfo->getRealPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (substr($fileInfo->getFilename(), 0, 1) == '.')
|
|
||||||
{
|
|
||||||
// Temporary/hidden file - skip
|
|
||||||
@unlink($fileInfo->getRealPath());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = getimagesize($fileInfo->getRealPath());
|
|
||||||
if ($result === false)
|
|
||||||
{
|
|
||||||
// Not an image file - skip
|
|
||||||
@unlink($fileInfo->getRealPath());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$photoFile = new File($fileInfo->getRealPath());
|
|
||||||
$queuedFileName = $queueStorage->uploadToAnalysisQueue($photoFile, $queueUid);
|
|
||||||
|
|
||||||
$photo = new Photo();
|
|
||||||
$photo->album_id = $album->id;
|
|
||||||
$photo->user_id = Auth::user()->id;
|
|
||||||
$photo->name = pathinfo($photoFile->getFilename(), PATHINFO_FILENAME);
|
|
||||||
$photo->file_name = $photoFile->getFilename();
|
|
||||||
$photo->mime_type = $photoFile->getMimeType();
|
|
||||||
$photo->file_size = $photoFile->getSize();
|
|
||||||
$photo->is_analysed = false;
|
|
||||||
$photo->storage_file_name = basename($queuedFileName);
|
|
||||||
$photo->save();
|
|
||||||
|
|
||||||
// Log an activity record for the user's feed
|
|
||||||
$this->createActivityRecord($photo, 'photo.uploaded');
|
|
||||||
|
|
||||||
// If queueing is enabled, store the photo in the queue now
|
|
||||||
if (UserConfig::isImageProcessingQueueEnabled())
|
|
||||||
{
|
|
||||||
$queueItem = new QueueItem([
|
|
||||||
'batch_reference' => $queueUid,
|
|
||||||
'action_type' => 'photo.analyse',
|
|
||||||
'album_id' => $photo->album_id,
|
|
||||||
'photo_id' => $photo->id,
|
|
||||||
'user_id' => $this->getUser()->id,
|
|
||||||
'queued_at' => new \DateTime()
|
|
||||||
]);
|
|
||||||
$queueItem->save();
|
|
||||||
|
|
||||||
$rabbitmq = new RabbitMQService();
|
|
||||||
$rabbitmq->queueItem($queueItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
@unlink($fileInfo->getRealPath());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
|
$di = new \RecursiveDirectoryIterator($queueFolder, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||||
|
$recursive = new \RecursiveIteratorIterator($di);
|
||||||
|
|
||||||
|
/** @var \SplFileInfo $fileInfo */
|
||||||
|
foreach ($recursive as $fileInfo)
|
||||||
{
|
{
|
||||||
@rmdir($temporaryFolder);
|
if ($fileInfo->isDir())
|
||||||
|
{
|
||||||
|
if ($fileInfo->getFilename() == '__MACOSX' || substr($fileInfo->getFilename(), 0, 1) == '.')
|
||||||
|
{
|
||||||
|
@rmdir($fileInfo->getPathname());
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (substr($fileInfo->getFilename(), 0, 1) == '.')
|
||||||
|
{
|
||||||
|
// Temporary/hidden file - skip
|
||||||
|
@unlink($fileInfo->getPathname());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = getimagesize($fileInfo->getPathname());
|
||||||
|
if ($result === false)
|
||||||
|
{
|
||||||
|
// Not an image file - skip
|
||||||
|
@unlink($fileInfo->getPathname());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$photoFile = new File($fileInfo->getPathname());
|
||||||
|
|
||||||
|
/** @var File $savedFile */
|
||||||
|
$savedFile = FileHelper::saveExtractedFile($photoFile, $queueFolder);
|
||||||
|
|
||||||
|
$photo = new Photo();
|
||||||
|
$photo->album_id = $album->id;
|
||||||
|
$photo->user_id = Auth::user()->id;
|
||||||
|
$photo->name = pathinfo($photoFile->getFilename(), PATHINFO_FILENAME);
|
||||||
|
$photo->file_name = $photoFile->getFilename();
|
||||||
|
$photo->storage_file_name = $savedFile->getFilename();
|
||||||
|
$photo->mime_type = $savedFile->getMimeType();
|
||||||
|
$photo->file_size = $savedFile->getSize();
|
||||||
|
$photo->is_analysed = false;
|
||||||
|
$photo->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect(route('albums.analyse', [
|
return redirect(route('albums.analyse', [
|
||||||
'album' => $album->id,
|
'id' => $album->id,
|
||||||
'queue_token' => $queueUid
|
'queue_token' => $queueUid
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
@ -582,16 +391,9 @@ class PhotoController extends Controller
|
|||||||
$numberChanged = $this->updatePhotoDetails($request, $album);
|
$numberChanged = $this->updatePhotoDetails($request, $album);
|
||||||
}
|
}
|
||||||
|
|
||||||
$request->session()->flash(
|
$request->session()->flash('success', trans_choice('admin.bulk_photos_changed', $numberChanged, ['number' => $numberChanged]));
|
||||||
'success',
|
|
||||||
trans_choice(
|
|
||||||
UserConfig::isImageProcessingQueueEnabled() ? 'admin.bulk_photos_changed_queued' : 'admin.bulk_photos_changed',
|
|
||||||
$numberChanged,
|
|
||||||
['number' => $numberChanged]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return redirect(route('albums.show', array('album' => $albumId, 'page' => $request->get('page', 1))));
|
return redirect(route('albums.show', array('id' => $albumId, 'page' => $request->get('page', 1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function applyBulkActions(Request $request, Album $album)
|
private function applyBulkActions(Request $request, Album $album)
|
||||||
@ -631,161 +433,101 @@ class PhotoController extends Controller
|
|||||||
$action = $request->get('bulk-action');
|
$action = $request->get('bulk-action');
|
||||||
$numberChanged = 0;
|
$numberChanged = 0;
|
||||||
|
|
||||||
if (UserConfig::isImageProcessingQueueEnabled())
|
foreach ($photosToProcess as $photo)
|
||||||
{
|
{
|
||||||
$queueUid = MiscHelper::randomString();
|
$changed = false;
|
||||||
|
$photoService = new PhotoService($photo);
|
||||||
foreach ($photosToProcess as $photo)
|
$doNotSave = false;
|
||||||
|
switch (strtolower($action))
|
||||||
{
|
{
|
||||||
$queueItem = new QueueItem([
|
case 'change_album':
|
||||||
'batch_reference' => $queueUid,
|
if (Auth::user()->can('change-metadata', $photo))
|
||||||
'action_type' => sprintf('photo.bulk_action.%s', strtolower($action)),
|
|
||||||
'album_id' => $photo->album_id,
|
|
||||||
'photo_id' => $photo->id,
|
|
||||||
'user_id' => $this->getUser()->id,
|
|
||||||
'queued_at' => new \DateTime()
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (strtolower($action) == 'change_album')
|
|
||||||
{
|
|
||||||
$queueItem->new_album_id = intval($request->get('new-album-id'));
|
|
||||||
$newAlbumId = intval($request->get('new-album-id'));
|
|
||||||
if ($newAlbumId == $photo->album_id)
|
|
||||||
{
|
{
|
||||||
// Photo already belongs to this album, don't move
|
$newAlbumId = intval($request->get('new-album-id'));
|
||||||
continue;
|
if ($newAlbumId == $photo->album_id)
|
||||||
|
{
|
||||||
|
// Photo already belongs to this album, don't move
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$newAlbum = $this->loadAlbum($newAlbumId, 'upload-photos');
|
||||||
|
$photoService->changeAlbum($newAlbum);
|
||||||
|
$changed = true;
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
|
||||||
$queueItem->save();
|
case 'delete':
|
||||||
|
if (Auth::user()->can('delete', $photo))
|
||||||
|
{
|
||||||
|
$photoService->delete();
|
||||||
|
$doNotSave = true;
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
$rabbitmq = new RabbitMQService();
|
case 'flip_both':
|
||||||
$rabbitmq->queueItem($queueItem);
|
if (Auth::user()->can('manipulate', $photo))
|
||||||
|
{
|
||||||
|
$photoService->flip(true, true);
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
$numberChanged++;
|
case 'flip_horizontal':
|
||||||
|
if (Auth::user()->can('manipulate', $photo))
|
||||||
|
{
|
||||||
|
$photoService->flip(true, false);
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'flip_vertical':
|
||||||
|
if (Auth::user()->can('manipulate', $photo))
|
||||||
|
{
|
||||||
|
$photoService->flip(false, true);
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'refresh_thumbnails':
|
||||||
|
if (Auth::user()->can('change-metadata', $photo))
|
||||||
|
{
|
||||||
|
$photoService->regenerateThumbnails();
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'rotate_left':
|
||||||
|
if (Auth::user()->can('manipulate', $photo))
|
||||||
|
{
|
||||||
|
$photoService->rotate(90);
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'rotate_right':
|
||||||
|
if (Auth::user()->can('manipulate', $photo))
|
||||||
|
{
|
||||||
|
$photoService->rotate(270);
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
if (!$doNotSave)
|
||||||
{
|
|
||||||
foreach ($photosToProcess as $photo)
|
|
||||||
{
|
{
|
||||||
$changed = false;
|
$photo->save();
|
||||||
$photoService = new PhotoService($photo);
|
}
|
||||||
$doNotSave = false;
|
|
||||||
|
|
||||||
/* IF CHANGING THIS LOGIC OR ADDING EXTRA case OPTIONS, ALSO CHECK ProcessQueueCommand::processQueueItem AND ProcessQueueCommand::processPhotoBulkActionMessage */
|
if ($changed)
|
||||||
switch (strtolower($action))
|
{
|
||||||
{
|
$numberChanged++;
|
||||||
case 'change_album':
|
|
||||||
if (Auth::user()->can('change-metadata', $photo))
|
|
||||||
{
|
|
||||||
$newAlbumId = intval($request->get('new-album-id'));
|
|
||||||
if ($newAlbumId == $photo->album_id)
|
|
||||||
{
|
|
||||||
// Photo already belongs to this album, don't move
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
$newAlbum = $this->loadAlbum($newAlbumId, 'upload-photos');
|
|
||||||
$photoService->changeAlbum($newAlbum);
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'delete':
|
|
||||||
if (Auth::user()->can('delete', $photo))
|
|
||||||
{
|
|
||||||
$photoService->delete();
|
|
||||||
$doNotSave = true;
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'flip_both':
|
|
||||||
if (Auth::user()->can('manipulate', $photo))
|
|
||||||
{
|
|
||||||
$photoService->flip(true, true);
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'flip_horizontal':
|
|
||||||
if (Auth::user()->can('manipulate', $photo))
|
|
||||||
{
|
|
||||||
$photoService->flip(true, false);
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'flip_vertical':
|
|
||||||
if (Auth::user()->can('manipulate', $photo))
|
|
||||||
{
|
|
||||||
$photoService->flip(false, true);
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'refresh_thumbnails':
|
|
||||||
if (Auth::user()->can('change-metadata', $photo))
|
|
||||||
{
|
|
||||||
$photoService->regenerateThumbnails();
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'rotate_left':
|
|
||||||
if (Auth::user()->can('manipulate', $photo))
|
|
||||||
{
|
|
||||||
$photoService->rotate(90);
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'rotate_right':
|
|
||||||
if (Auth::user()->can('manipulate', $photo))
|
|
||||||
{
|
|
||||||
$photoService->rotate(270);
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$doNotSave)
|
|
||||||
{
|
|
||||||
$photo->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!in_array(strtolower($action), ['delete', 'refresh_thumbnails', 'change_album']))
|
|
||||||
{
|
|
||||||
// Log an activity record for the user's feed
|
|
||||||
$this->createActivityRecord($photo, 'photo.edited');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($changed)
|
|
||||||
{
|
|
||||||
$numberChanged++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $numberChanged;
|
return $numberChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createActivityRecord(Photo $photo, $type, $activityDateTime = null)
|
|
||||||
{
|
|
||||||
if (is_null($activityDateTime))
|
|
||||||
{
|
|
||||||
$activityDateTime = new \DateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
$userActivity = new UserActivity();
|
|
||||||
$userActivity->user_id = $this->getUser()->id;
|
|
||||||
$userActivity->activity_at = $activityDateTime;
|
|
||||||
$userActivity->type = $type;
|
|
||||||
$userActivity->photo_id = $photo->id;
|
|
||||||
$userActivity->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $id
|
* @param $id
|
||||||
* @return Album
|
* @return Album
|
||||||
@ -826,20 +568,6 @@ class PhotoController extends Controller
|
|||||||
return $photo;
|
return $photo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function removeExistingActivityRecords(Photo $photo, $type)
|
|
||||||
{
|
|
||||||
$existingFeedRecords = UserActivity::where([
|
|
||||||
'user_id' => $this->getUser()->id,
|
|
||||||
'photo_id' => $photo->id,
|
|
||||||
'type' => $type
|
|
||||||
])->get();
|
|
||||||
|
|
||||||
foreach ($existingFeedRecords as $existingFeedRecord)
|
|
||||||
{
|
|
||||||
$existingFeedRecord->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function updatePhotoDetails(Request $request, Album $album)
|
private function updatePhotoDetails(Request $request, Album $album)
|
||||||
{
|
{
|
||||||
$numberChanged = 0;
|
$numberChanged = 0;
|
||||||
@ -855,17 +583,6 @@ class PhotoController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$photo->fill($value);
|
$photo->fill($value);
|
||||||
|
|
||||||
// Update the photo labels
|
|
||||||
$labelString = trim($value['labels']);
|
|
||||||
$photo->labels()->detach();
|
|
||||||
|
|
||||||
if (strlen($labelString) > 0)
|
|
||||||
{
|
|
||||||
app(LabelController::class)->applyLabelsToPhoto($photo, $labelString);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save all changes
|
|
||||||
$photo->save();
|
$photo->save();
|
||||||
$numberChanged++;
|
$numberChanged++;
|
||||||
}
|
}
|
||||||
|
@ -1,355 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
|
||||||
|
|
||||||
use App\Configuration;
|
|
||||||
use App\ExternalService;
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Http\Requests\StoreServiceRequest;
|
|
||||||
use App\Services\DropboxService;
|
|
||||||
use App\Storage;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\App;
|
|
||||||
use Illuminate\Support\Facades\View;
|
|
||||||
|
|
||||||
class ServiceController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* List of fields that must be encrypted before being saved.
|
|
||||||
*
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
private $fieldsToEncrypt;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of fields that depend on the service_type being configured.
|
|
||||||
*
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
private $serviceTypeDependentFields;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('auth');
|
|
||||||
View::share('is_admin', true);
|
|
||||||
|
|
||||||
$this->serviceTypeDependentFields = ['app_id', 'app_secret'];
|
|
||||||
$this->fieldsToEncrypt = ['app_id', 'app_secret'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function authoriseDropbox(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-storage');
|
|
||||||
|
|
||||||
if (!$request->has('state') && !$request->has('code'))
|
|
||||||
{
|
|
||||||
// TODO flash an error
|
|
||||||
return redirect('storages.index');
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$storageID = decrypt($request->get('state'));
|
|
||||||
|
|
||||||
$storage = Storage::where('id', intval($storageID))->first();
|
|
||||||
if (is_null($storage))
|
|
||||||
{
|
|
||||||
// TODO flash an error
|
|
||||||
return redirect('storages.index');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($storage->externalService))
|
|
||||||
{
|
|
||||||
// TODO flash an error
|
|
||||||
return redirect('storages.index');
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($storage->externalService->service_type)
|
|
||||||
{
|
|
||||||
case ExternalService::DROPBOX:
|
|
||||||
$dropbox = new DropboxService();
|
|
||||||
$dropbox->handleAuthenticationResponse($request, $storage);
|
|
||||||
// TODO flash a success message
|
|
||||||
return redirect(route('storage.index'));
|
|
||||||
|
|
||||||
default:
|
|
||||||
// TODO flash an error
|
|
||||||
return redirect('storages.index');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (\Exception $ex)
|
|
||||||
{
|
|
||||||
// TODO flash an error
|
|
||||||
return redirect('storages.index');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for creating a new resource.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function create(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-services');
|
|
||||||
|
|
||||||
$serviceTypes = $this->serviceTypeList();
|
|
||||||
$selectedServiceType = old('service_type', $request->get('service_type'));
|
|
||||||
|
|
||||||
if (!array_key_exists($selectedServiceType, $serviceTypes))
|
|
||||||
{
|
|
||||||
$selectedServiceType = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$returnTo = old('return_to', $request->get('return_to'));
|
|
||||||
if (!array_key_exists($returnTo, $this->validReturnLocations()))
|
|
||||||
{
|
|
||||||
$returnTo = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('admin.create_service', [
|
|
||||||
'callbackUrls' => $this->callbackList(),
|
|
||||||
'returnTo' => $returnTo,
|
|
||||||
'selectedServiceType' => $selectedServiceType,
|
|
||||||
'service' => new ExternalService(),
|
|
||||||
'serviceTypes' => $serviceTypes
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete(Request $request, $id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-users');
|
|
||||||
|
|
||||||
$service = ExternalService::where('id', intval($id))->first();
|
|
||||||
if (is_null($service))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->isServiceInUse($service))
|
|
||||||
{
|
|
||||||
$request->session()->flash('warning', trans('admin.cannot_delete_service_in_use'));
|
|
||||||
return redirect(route('services.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('admin.delete_service', ['service' => $service]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the specified resource from storage.
|
|
||||||
*
|
|
||||||
* @param int $id
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function destroy(Request $request, $id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-services');
|
|
||||||
|
|
||||||
$service = ExternalService::where('id', intval($id))->first();
|
|
||||||
if (is_null($service))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->isServiceInUse($service))
|
|
||||||
{
|
|
||||||
$request->session()->flash('warning', trans('admin.cannot_delete_service_in_use'));
|
|
||||||
return redirect(route('services.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$service->delete();
|
|
||||||
$request->session()->flash('success', trans('admin.service_deletion_successful', [
|
|
||||||
'name' => $service->name
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
catch (\Exception $ex)
|
|
||||||
{
|
|
||||||
$request->session()->flash('error', trans('admin.service_deletion_failed', [
|
|
||||||
'error_message' => $ex->getMessage(),
|
|
||||||
'name' => $service->name
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect(route('services.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for editing the specified resource.
|
|
||||||
*
|
|
||||||
* @param int $id
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function edit(Request $request, $id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-services');
|
|
||||||
|
|
||||||
$service = ExternalService::where('id', intval($id))->first();
|
|
||||||
if (is_null($service))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt the fields that are stored as encrypted in the DB
|
|
||||||
foreach ($this->fieldsToEncrypt as $field)
|
|
||||||
{
|
|
||||||
if (!empty($service->$field))
|
|
||||||
{
|
|
||||||
$service->$field = decrypt($service->$field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('admin.edit_service', [
|
|
||||||
'callbackUrls' => $this->callbackList(),
|
|
||||||
'service' => $service,
|
|
||||||
'serviceTypes' => $this->serviceTypeList()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function index(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-services');
|
|
||||||
|
|
||||||
$services = ExternalService::orderBy('name')
|
|
||||||
->paginate(UserConfig::get('items_per_page'));
|
|
||||||
|
|
||||||
return Theme::render('admin.list_services', [
|
|
||||||
'error' => $request->session()->get('error'),
|
|
||||||
'services' => $services,
|
|
||||||
'success' => $request->session()->get('success'),
|
|
||||||
'warning' => $request->session()->get('warning')
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource in storage.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function store(StoreServiceRequest $request)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-services');
|
|
||||||
|
|
||||||
$service = new ExternalService($request->only(['name', 'service_type']));
|
|
||||||
|
|
||||||
foreach ($this->serviceTypeDependentFields as $field)
|
|
||||||
{
|
|
||||||
if ($request->has($field))
|
|
||||||
{
|
|
||||||
$service->$field = in_array($field, $this->fieldsToEncrypt)
|
|
||||||
? encrypt($request->get($field))
|
|
||||||
: $request->get($field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$service->save();
|
|
||||||
|
|
||||||
$returnToLocations = $this->validReturnLocations();
|
|
||||||
$returnTo = $request->get('return_to');
|
|
||||||
|
|
||||||
if (array_key_exists($returnTo, $returnToLocations))
|
|
||||||
{
|
|
||||||
return redirect($returnToLocations[$returnTo]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect(route('services.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource in storage.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param int $id
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function update(StoreServiceRequest $request, $id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-services');
|
|
||||||
|
|
||||||
$service = ExternalService::where('id', intval($id))->first();
|
|
||||||
if (is_null($service))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
$service->fill($request->only(['name', 'service_type']));
|
|
||||||
|
|
||||||
foreach ($this->serviceTypeDependentFields as $field)
|
|
||||||
{
|
|
||||||
if ($request->has($field))
|
|
||||||
{
|
|
||||||
$service->$field = in_array($field, $this->fieldsToEncrypt)
|
|
||||||
? encrypt($request->get($field))
|
|
||||||
: $request->get($field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$service->save();
|
|
||||||
|
|
||||||
return redirect(route('services.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function callbackList()
|
|
||||||
{
|
|
||||||
$dropboxService = new DropboxService();
|
|
||||||
|
|
||||||
return [
|
|
||||||
ExternalService::DROPBOX => $dropboxService->callbackUrl(),
|
|
||||||
ExternalService::FACEBOOK => route('login_callback.facebook'),
|
|
||||||
ExternalService::GOOGLE => route('login_callback.google'),
|
|
||||||
ExternalService::TWITTER => route('login_callback.twitter')
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function isServiceInUse(ExternalService $service)
|
|
||||||
{
|
|
||||||
switch ($service->service_type)
|
|
||||||
{
|
|
||||||
case ExternalService::FACEBOOK:
|
|
||||||
// Cannot delete Facebook service if it's set as the login provider
|
|
||||||
$facebookConfig = Configuration::where('key', 'facebook_external_service_id')->first();
|
|
||||||
return !is_null($facebookConfig) && intval($facebookConfig->value) == $service->id;
|
|
||||||
|
|
||||||
case ExternalService::GOOGLE:
|
|
||||||
// Cannot delete Google service if it's set as the login provider
|
|
||||||
$googleConfig = Configuration::where('key', 'google_external_service_id')->first();
|
|
||||||
return !is_null($googleConfig) && intval($googleConfig->value) == $service->id;
|
|
||||||
|
|
||||||
case ExternalService::DROPBOX:
|
|
||||||
return Storage::where('external_service_id', $service->id)->count() > 0;
|
|
||||||
|
|
||||||
case ExternalService::TWITTER:
|
|
||||||
// Cannot delete Twitter service if it's set as the login provider
|
|
||||||
$twitterConfig = Configuration::where('key', 'twitter_external_service_id')->first();
|
|
||||||
return !is_null($twitterConfig) && intval($twitterConfig->value) == $service->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function serviceTypeList()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
ExternalService::DROPBOX => trans(sprintf('services.%s', ExternalService::DROPBOX)),
|
|
||||||
ExternalService::FACEBOOK => trans(sprintf('services.%s', ExternalService::FACEBOOK)),
|
|
||||||
ExternalService::GOOGLE => trans(sprintf('services.%s', ExternalService::GOOGLE)),
|
|
||||||
ExternalService::TWITTER => trans(sprintf('services.%s', ExternalService::TWITTER))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function validReturnLocations()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'settings' => route('admin.settings')
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
|
||||||
|
|
||||||
use App\Configuration;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
|
|
||||||
class StatisticsController extends Controller
|
|
||||||
{
|
|
||||||
public function save(Request $request)
|
|
||||||
{
|
|
||||||
$isPublicStatsEnabled = strtolower($request->get('enable_public_statistics')) == 'on';
|
|
||||||
|
|
||||||
/** @var Configuration $config */
|
|
||||||
$config = UserConfig::getOrCreateModel('public_statistics');
|
|
||||||
$config->value = $isPublicStatsEnabled;
|
|
||||||
$config->save();
|
|
||||||
|
|
||||||
$request->session()->flash('success', trans('admin.statistics_prefs_saved_message'));
|
|
||||||
return redirect(route('statistics.index'));
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,14 +2,13 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\ExternalService;
|
|
||||||
use App\Facade\Theme;
|
use App\Facade\Theme;
|
||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Http\Requests;
|
|
||||||
use App\Services\DropboxService;
|
|
||||||
use App\Storage;
|
use App\Storage;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
use App\Http\Requests;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
|
|
||||||
@ -25,39 +24,7 @@ class StorageController extends Controller
|
|||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
View::share('is_admin', true);
|
View::share('is_admin', true);
|
||||||
|
|
||||||
$this->encryptedFields = ['password', 'access_key', 'secret_key', 'access_token'];
|
$this->encryptedFields = ['password', 'access_key', 'secret_key'];
|
||||||
}
|
|
||||||
|
|
||||||
public function authoriseService(Request $request, $id)
|
|
||||||
{
|
|
||||||
$this->authorizeAccessToAdminPanel('admin:manage-storage');
|
|
||||||
|
|
||||||
$storage = Storage::where('id', intval($id))->first();
|
|
||||||
if (is_null($storage))
|
|
||||||
{
|
|
||||||
return redirect(route('storages.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$externalServiceType = $this->getExternalServiceType($storage);
|
|
||||||
|
|
||||||
if (is_null($externalServiceType))
|
|
||||||
{
|
|
||||||
$request->session()->flash('error', trans('admin.storage_no_external_service_support'));
|
|
||||||
return redirect(route('storages.index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$serviceTypeName = trans(sprintf('services.%s', $externalServiceType));
|
|
||||||
|
|
||||||
switch ($externalServiceType)
|
|
||||||
{
|
|
||||||
case ExternalService::DROPBOX:
|
|
||||||
$dropbox = new DropboxService();
|
|
||||||
return redirect($dropbox->authoriseUrl($storage));
|
|
||||||
|
|
||||||
default:
|
|
||||||
$request->session()->flash('error', trans('admin.storage_external_service_no_authorisation', ['service_name' => $serviceTypeName]));
|
|
||||||
return redirect(route('storages.index'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,15 +56,11 @@ class StorageController extends Controller
|
|||||||
$this->authorizeAccessToAdminPanel('admin:manage-storage');
|
$this->authorizeAccessToAdminPanel('admin:manage-storage');
|
||||||
|
|
||||||
$filesystemDefaultLocation = sprintf('%s/storage/app/albums', dirname(dirname(dirname(dirname(__DIR__)))));
|
$filesystemDefaultLocation = sprintf('%s/storage/app/albums', dirname(dirname(dirname(dirname(__DIR__)))));
|
||||||
$storage = new Storage();
|
|
||||||
$storage->s3_signed_urls = true;
|
|
||||||
|
|
||||||
return Theme::render('admin.create_storage', [
|
return Theme::render('admin.create_storage', [
|
||||||
'album_sources' => UserConfig::albumSources(),
|
'album_sources' => UserConfig::albumSources(),
|
||||||
'dropbox_services' => ExternalService::getForService(ExternalService::DROPBOX),
|
|
||||||
'filesystem_default_location' => $filesystemDefaultLocation,
|
'filesystem_default_location' => $filesystemDefaultLocation,
|
||||||
'info' => $request->session()->get('info'),
|
'info' => $request->session()->get('info')
|
||||||
'storage' => $storage
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,14 +88,11 @@ class StorageController extends Controller
|
|||||||
'container_name',
|
'container_name',
|
||||||
'cdn_url',
|
'cdn_url',
|
||||||
'access_key',
|
'access_key',
|
||||||
'secret_key',
|
'secret_key'
|
||||||
'b2_bucket_type',
|
|
||||||
'external_service_id'
|
|
||||||
]));
|
]));
|
||||||
$storage->is_active = true;
|
$storage->is_active = true;
|
||||||
$storage->is_default = (strtolower($request->get('is_default')) == 'on');
|
$storage->is_default = (strtolower($request->get('is_default')) == 'on');
|
||||||
$storage->is_internal = false;
|
$storage->is_internal = false;
|
||||||
$storage->s3_signed_urls = (strtolower($request->get('s3_signed_urls')) == 'on');
|
|
||||||
|
|
||||||
if ($storage->source != 'LocalFilesystemSource' && isset($storage->location))
|
if ($storage->source != 'LocalFilesystemSource' && isset($storage->location))
|
||||||
{
|
{
|
||||||
@ -154,17 +114,6 @@ class StorageController extends Controller
|
|||||||
$this->unsetIsDefaultFromOthers($storage);
|
$this->unsetIsDefaultFromOthers($storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
$externalServiceType = $this->getExternalServiceType($storage);
|
|
||||||
|
|
||||||
if (!is_null($externalServiceType))
|
|
||||||
{
|
|
||||||
switch ($externalServiceType)
|
|
||||||
{
|
|
||||||
case ExternalService::DROPBOX:
|
|
||||||
return redirect(route('storage.authoriseService', ['storage' => $storage->id]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect(route('storage.index'));
|
return redirect(route('storage.index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,10 +186,12 @@ class StorageController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Theme::render('admin.edit_storage', [
|
if (!$request->session()->has('_old_input'))
|
||||||
'dropbox_services' => ExternalService::getForService(ExternalService::DROPBOX),
|
{
|
||||||
'storage' => $storage
|
$request->session()->flash('_old_input', $storage->toArray());
|
||||||
]);
|
}
|
||||||
|
|
||||||
|
return Theme::render('admin.edit_storage', ['storage' => $storage]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -271,13 +222,10 @@ class StorageController extends Controller
|
|||||||
'container_name',
|
'container_name',
|
||||||
'cdn_url',
|
'cdn_url',
|
||||||
'access_key',
|
'access_key',
|
||||||
'secret_key',
|
'secret_key'
|
||||||
'b2_bucket_type',
|
|
||||||
'external_service_id'
|
|
||||||
]));
|
]));
|
||||||
$storage->is_active = (strtolower($request->get('is_active')) == 'on');
|
$storage->is_active = (strtolower($request->get('is_active')) == 'on');
|
||||||
$storage->is_default = (strtolower($request->get('is_default')) == 'on');
|
$storage->is_default = (strtolower($request->get('is_default')) == 'on');
|
||||||
$storage->s3_signed_urls = (strtolower($request->get('s3_signed_urls')) == 'on');
|
|
||||||
|
|
||||||
if ($storage->is_default && !$storage->is_active)
|
if ($storage->is_default && !$storage->is_active)
|
||||||
{
|
{
|
||||||
@ -298,10 +246,6 @@ class StorageController extends Controller
|
|||||||
{
|
{
|
||||||
$this->unsetIsDefaultFromOthers($storage);
|
$this->unsetIsDefaultFromOthers($storage);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->setIsDefaultForFirstStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect(route('storage.index'));
|
return redirect(route('storage.index'));
|
||||||
}
|
}
|
||||||
@ -341,32 +285,6 @@ class StorageController extends Controller
|
|||||||
return redirect(route('storage.index'));
|
return redirect(route('storage.index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getExternalServiceType(Storage $storage)
|
|
||||||
{
|
|
||||||
if (!is_null($storage->externalService))
|
|
||||||
{
|
|
||||||
return $storage->externalService->service_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setIsDefaultForFirstStorage()
|
|
||||||
{
|
|
||||||
$count = Storage::where('is_default', true)->count();
|
|
||||||
|
|
||||||
if ($count == 0)
|
|
||||||
{
|
|
||||||
$storage = Storage::where('is_active', true)->first();
|
|
||||||
|
|
||||||
if (!is_null($storage))
|
|
||||||
{
|
|
||||||
$storage->is_default = true;
|
|
||||||
$storage->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function unsetIsDefaultFromOthers(Storage $storage)
|
private function unsetIsDefaultFromOthers(Storage $storage)
|
||||||
{
|
{
|
||||||
// If this storage is flagged as default, remove all others
|
// If this storage is flagged as default, remove all others
|
||||||
|
@ -5,10 +5,10 @@ namespace App\Http\Controllers\Admin;
|
|||||||
use App\Facade\Theme;
|
use App\Facade\Theme;
|
||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Group;
|
use App\Group;
|
||||||
use App\Helpers\PermissionsHelper;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Http\Requests;
|
|
||||||
use App\User;
|
use App\User;
|
||||||
|
|
||||||
|
use App\Http\Requests;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
@ -88,7 +88,6 @@ class UserController extends Controller
|
|||||||
$user->password = bcrypt($user->password);
|
$user->password = bcrypt($user->password);
|
||||||
$user->is_activated = true;
|
$user->is_activated = true;
|
||||||
$user->is_admin = (strtolower($request->get('is_admin')) == 'on');
|
$user->is_admin = (strtolower($request->get('is_admin')) == 'on');
|
||||||
$user->enable_profile_page = UserConfig::get('social_user_profiles');
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
return redirect(route('users.index'));
|
return redirect(route('users.index'));
|
||||||
@ -201,10 +200,6 @@ class UserController extends Controller
|
|||||||
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
// Rebuild the permissions cache
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$helper->rebuildCache();
|
|
||||||
|
|
||||||
return redirect(route('users.index'));
|
return redirect(route('users.index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Traits\ActivatesUsers;
|
|
||||||
use App\User;
|
use App\User;
|
||||||
use Illuminate\Foundation\Auth\RedirectsUsers;
|
use Illuminate\Foundation\Auth\RedirectsUsers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -11,7 +10,7 @@ use Illuminate\Support\Facades\App;
|
|||||||
|
|
||||||
class ActivateController extends Controller
|
class ActivateController extends Controller
|
||||||
{
|
{
|
||||||
use RedirectsUsers, ActivatesUsers;
|
use RedirectsUsers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where to redirect users after activation.
|
* Where to redirect users after activation.
|
||||||
@ -47,9 +46,6 @@ class ActivateController extends Controller
|
|||||||
|
|
||||||
$request->session()->flash('info', trans('auth.account_activated_message'));
|
$request->session()->flash('info', trans('auth.account_activated_message'));
|
||||||
|
|
||||||
$this->logActivatedActivity($user);
|
|
||||||
$this->sendUserActivatedEmails($user);
|
|
||||||
|
|
||||||
return redirect($this->redirectPath());
|
return redirect($this->redirectPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,20 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\ExternalService;
|
|
||||||
use App\Facade\Theme;
|
use App\Facade\Theme;
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Helpers\MiscHelper;
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\User;
|
|
||||||
use Illuminate\Contracts\Routing\UrlGenerator;
|
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Laravel\Socialite\One\TwitterProvider;
|
|
||||||
use Laravel\Socialite\Two\FacebookProvider;
|
|
||||||
use Laravel\Socialite\Two\GoogleProvider;
|
|
||||||
use League\OAuth1\Client\Server\Twitter as TwitterServer;
|
|
||||||
use Socialite;
|
|
||||||
|
|
||||||
class LoginController extends Controller
|
class LoginController extends Controller
|
||||||
{
|
{
|
||||||
@ -32,61 +22,21 @@ class LoginController extends Controller
|
|||||||
|
|
||||||
use AuthenticatesUsers;
|
use AuthenticatesUsers;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var UrlGenerator
|
|
||||||
*/
|
|
||||||
protected $generator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where to redirect users after login / registration.
|
* Where to redirect users after login / registration.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = '/me';
|
protected $redirectTo = '/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new controller instance.
|
* Create a new controller instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(UrlGenerator $generator)
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('guest', ['except' => 'logout']);
|
$this->middleware('guest', ['except' => 'logout']);
|
||||||
$this->generator = $generator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function logout(Request $request)
|
|
||||||
{
|
|
||||||
$this->guard()->logout();
|
|
||||||
|
|
||||||
$request->session()->invalidate();
|
|
||||||
|
|
||||||
return redirect()->back();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function attemptLogin(Request $request)
|
|
||||||
{
|
|
||||||
$isSuccessful = $this->guard()->attempt($this->credentials($request));
|
|
||||||
|
|
||||||
if ($isSuccessful)
|
|
||||||
{
|
|
||||||
/** @var User $user */
|
|
||||||
$user = $this->guard()->user();
|
|
||||||
|
|
||||||
// Update the social media ID if successful login and it was referred by the SSO provider
|
|
||||||
$loginData = $request->getSession()->get('ssoLoginData');
|
|
||||||
if (!is_null($loginData))
|
|
||||||
{
|
|
||||||
unset($loginData['name']);
|
|
||||||
unset($loginData['email']);
|
|
||||||
$user->fill($loginData);
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
$request->getSession()->remove('ssoLoginData');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $isSuccessful;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function credentials(Request $request)
|
protected function credentials(Request $request)
|
||||||
@ -106,285 +56,9 @@ class LoginController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function showLoginForm(Request $request)
|
public function showLoginForm(Request $request)
|
||||||
{
|
{
|
||||||
$previousUrl = MiscHelper::ensureHasTrailingSlash($this->generator->previous(false));
|
|
||||||
$homeUrl = MiscHelper::ensureHasTrailingSlash(route('home'));
|
|
||||||
|
|
||||||
if (UserConfig::get('social_user_feeds') && (empty($previousUrl) || $previousUrl == $homeUrl))
|
|
||||||
{
|
|
||||||
$previousUrl = route('userActivityFeed');
|
|
||||||
}
|
|
||||||
|
|
||||||
$request->getSession()->put('url.intended', $previousUrl);
|
|
||||||
|
|
||||||
return Theme::render('auth.v2_unified', [
|
return Theme::render('auth.v2_unified', [
|
||||||
'active_tab' => 'login',
|
'active_tab' => 'login',
|
||||||
'info' => $request->session()->get('info'),
|
'info' => $request->session()->get('info')
|
||||||
'is_sso' => false
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the application's login form (for a social media-linked account).
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function showLoginFormSso(Request $request)
|
|
||||||
{
|
|
||||||
// Social media login info
|
|
||||||
$loginData = $request->getSession()->get('ssoLoginData');
|
|
||||||
if (is_null($loginData))
|
|
||||||
{
|
|
||||||
// No SSO data in session, use the normal login screen
|
|
||||||
return redirect(route('login'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('auth.v2_unified', [
|
|
||||||
'active_tab' => 'login',
|
|
||||||
'info' => $request->session()->get('info'),
|
|
||||||
'is_sso' => true,
|
|
||||||
'login_data' => $loginData
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redirect the user to the Facebook authentication page.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function redirectToFacebook()
|
|
||||||
{
|
|
||||||
$socialite = $this->setSocialiteConfigForFacebook();
|
|
||||||
if (is_null($socialite))
|
|
||||||
{
|
|
||||||
return redirect(route('login'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $socialite->driver('facebook')->redirect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redirect the user to the Google authentication page.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function redirectToGoogle()
|
|
||||||
{
|
|
||||||
$socialite = $this->setSocialiteConfigForGoogle();
|
|
||||||
if (is_null($socialite))
|
|
||||||
{
|
|
||||||
return redirect(route('login'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $socialite->driver('google')->redirect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redirect the user to the Twitter authentication page.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function redirectToTwitter()
|
|
||||||
{
|
|
||||||
$socialite = $this->setSocialiteConfigForTwitter();
|
|
||||||
if (is_null($socialite))
|
|
||||||
{
|
|
||||||
return redirect(route('login'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $socialite->driver('twitter')->redirect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain the user information from Facebook.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function handleFacebookCallback(Request $request)
|
|
||||||
{
|
|
||||||
$socialite = $this->setSocialiteConfigForFacebook();
|
|
||||||
if (is_null($socialite))
|
|
||||||
{
|
|
||||||
return redirect(route('login'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$facebookUser = $socialite->driver('facebook')->user();
|
|
||||||
|
|
||||||
return $this->processSocialMediaLogin($request, 'facebook_id', $facebookUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain the user information from Google.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function handleGoogleCallback(Request $request)
|
|
||||||
{
|
|
||||||
$socialite = $this->setSocialiteConfigForGoogle();
|
|
||||||
if (is_null($socialite))
|
|
||||||
{
|
|
||||||
return redirect(route('login'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$googleUser = $socialite->driver('google')->user();
|
|
||||||
|
|
||||||
return $this->processSocialMediaLogin($request, 'google_id', $googleUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain the user information from Twitter.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function handleTwitterCallback(Request $request)
|
|
||||||
{
|
|
||||||
$socialite = $this->setSocialiteConfigForTwitter();
|
|
||||||
if (is_null($socialite))
|
|
||||||
{
|
|
||||||
return redirect(route('login'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$twitterUser = $socialite->driver('twitter')->user();
|
|
||||||
|
|
||||||
return $this->processSocialMediaLogin($request, 'twitter_id', $twitterUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getSocialMediaConfig($socialMediaEnabledField, $socialMediaExternalServiceIdField)
|
|
||||||
{
|
|
||||||
if (boolval(UserConfig::get($socialMediaEnabledField)))
|
|
||||||
{
|
|
||||||
$externalServiceID = intval(UserConfig::get($socialMediaExternalServiceIdField));
|
|
||||||
$externalService = ExternalService::where('id', $externalServiceID)->first();
|
|
||||||
|
|
||||||
return $externalService;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processSocialMediaLogin(Request $request, $socialMediaIdField, $socialMediaUser)
|
|
||||||
{
|
|
||||||
$userBySocialMediaId = User::where($socialMediaIdField, $socialMediaUser->getId())->first();
|
|
||||||
|
|
||||||
if (!is_null($userBySocialMediaId))
|
|
||||||
{
|
|
||||||
// We have an existing user for this Facebook ID - log them in
|
|
||||||
$this->guard()->login($userBySocialMediaId);
|
|
||||||
return redirect(route('home'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some providers (*cough*Twitter*cough*) don't give e-mail addresses without explicit permission/additional
|
|
||||||
// verification
|
|
||||||
if (!is_null($socialMediaUser->email))
|
|
||||||
{
|
|
||||||
$userByEmailAddress = User::where('email', $socialMediaUser->getEmail())->first();
|
|
||||||
|
|
||||||
if (!is_null($userByEmailAddress))
|
|
||||||
{
|
|
||||||
// We have an existing user with the e-mail address associated with the Facebook account
|
|
||||||
// Prompt for the password for that account
|
|
||||||
$request->getSession()->put('ssoLoginData', [
|
|
||||||
'name' => $socialMediaUser->getName(),
|
|
||||||
'email' => $socialMediaUser->getEmail(),
|
|
||||||
$socialMediaIdField => $socialMediaUser->getId(),
|
|
||||||
'is_activated' => true
|
|
||||||
]);
|
|
||||||
|
|
||||||
return redirect(route('auth.login_sso'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't have an existing user - prompt for registration
|
|
||||||
$request->getSession()->put('ssoRegisterData', [
|
|
||||||
'name' => $socialMediaUser->getName(),
|
|
||||||
'email' => $socialMediaUser->getEmail(),
|
|
||||||
$socialMediaIdField => $socialMediaUser->getId(),
|
|
||||||
'is_activated' => true
|
|
||||||
]);
|
|
||||||
|
|
||||||
return redirect(route('auth.register_sso'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setSocialiteConfigForFacebook()
|
|
||||||
{
|
|
||||||
$facebookConfig = $this->getSocialMediaConfig(
|
|
||||||
'social_facebook_login',
|
|
||||||
'facebook_external_service_id'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (is_null($facebookConfig))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$socialite = app()->make(\Laravel\Socialite\Contracts\Factory::class);
|
|
||||||
$socialite->extend(
|
|
||||||
'facebook',
|
|
||||||
function ($app) use ($socialite, $facebookConfig) {
|
|
||||||
$config = [
|
|
||||||
'client_id' => trim(decrypt($facebookConfig->app_id)),
|
|
||||||
'client_secret' => trim(decrypt($facebookConfig->app_secret)),
|
|
||||||
'redirect' => route('login_callback.facebook')
|
|
||||||
];
|
|
||||||
return $socialite->buildProvider(FacebookProvider::class, $config);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return $socialite;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setSocialiteConfigForGoogle()
|
|
||||||
{
|
|
||||||
$googleConfig = $this->getSocialMediaConfig(
|
|
||||||
'social_google_login',
|
|
||||||
'google_external_service_id'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (is_null($googleConfig))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$socialite = app()->make(\Laravel\Socialite\Contracts\Factory::class);
|
|
||||||
$socialite->extend(
|
|
||||||
'google',
|
|
||||||
function ($app) use ($socialite, $googleConfig) {
|
|
||||||
$config = [
|
|
||||||
'client_id' => trim(decrypt($googleConfig->app_id)),
|
|
||||||
'client_secret' => trim(decrypt($googleConfig->app_secret)),
|
|
||||||
'redirect' => route('login_callback.google')
|
|
||||||
];
|
|
||||||
return $socialite->buildProvider(GoogleProvider::class, $config);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return $socialite;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setSocialiteConfigForTwitter()
|
|
||||||
{
|
|
||||||
$twitterConfig = $this->getSocialMediaConfig(
|
|
||||||
'social_twitter_login',
|
|
||||||
'twitter_external_service_id'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (is_null($twitterConfig))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$socialite = app()->make(\Laravel\Socialite\Contracts\Factory::class);
|
|
||||||
$socialite->extend(
|
|
||||||
'twitter',
|
|
||||||
function ($app) use ($socialite, $twitterConfig) {
|
|
||||||
$config = [
|
|
||||||
'identifier' => trim(decrypt($twitterConfig->app_id)),
|
|
||||||
'secret' => trim(decrypt($twitterConfig->app_secret)),
|
|
||||||
'callback_uri' => route('login_callback.twitter')
|
|
||||||
];
|
|
||||||
return new TwitterProvider($app['request'], new TwitterServer($config));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return $socialite;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,12 @@ use App\Facade\Theme;
|
|||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Helpers\MiscHelper;
|
use App\Helpers\MiscHelper;
|
||||||
use App\Helpers\RecaptchaHelper;
|
use App\Helpers\RecaptchaHelper;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Mail\UserActivationRequired;
|
||||||
use App\Notifications\UserActivationRequired;
|
|
||||||
use App\Traits\ActivatesUsers;
|
|
||||||
use App\User;
|
use App\User;
|
||||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
class RegisterController extends Controller
|
class RegisterController extends Controller
|
||||||
@ -27,7 +27,7 @@ class RegisterController extends Controller
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use RegistersUsers, ActivatesUsers;
|
use RegistersUsers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where to redirect users after login / registration.
|
* Where to redirect users after login / registration.
|
||||||
@ -85,23 +85,25 @@ class RegisterController extends Controller
|
|||||||
*/
|
*/
|
||||||
protected function create(array $data)
|
protected function create(array $data)
|
||||||
{
|
{
|
||||||
if (!isset($data['is_activated']))
|
$activationData = [
|
||||||
{
|
'is_activated' => true
|
||||||
$data['is_activated'] = true;
|
];
|
||||||
|
|
||||||
if (UserConfig::get('require_email_verification'))
|
if (UserConfig::get('require_email_verification'))
|
||||||
{
|
{
|
||||||
$data['is_activated'] = false;
|
$activationData['is_activated'] = false;
|
||||||
$data['activation_token'] = MiscHelper::randomString();
|
$activationData['activation_token'] = MiscHelper::randomString();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$data['password'] = bcrypt($data['password']);
|
return User::create(array_merge(
|
||||||
$data['is_admin'] = false;
|
[
|
||||||
$data['enable_profile_page'] = UserConfig::get('social_user_profiles');
|
'name' => $data['name'],
|
||||||
unset($data['password_confirmation']);
|
'email' => $data['email'],
|
||||||
|
'password' => bcrypt($data['password']),
|
||||||
return User::create($data);
|
'is_admin' => false
|
||||||
|
],
|
||||||
|
$activationData
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(Request $request)
|
public function register(Request $request)
|
||||||
@ -113,29 +115,17 @@ class RegisterController extends Controller
|
|||||||
|
|
||||||
$this->validator($request)->validate();
|
$this->validator($request)->validate();
|
||||||
|
|
||||||
$userData = $request->all();
|
|
||||||
|
|
||||||
// Social media login info
|
|
||||||
$registerData = $request->getSession()->get('ssoRegisterData');
|
|
||||||
if (!is_null($registerData))
|
|
||||||
{
|
|
||||||
$userData = array_merge($registerData, $userData);
|
|
||||||
$request->getSession()->remove('ssoRegisterData');
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = $this->create($userData);
|
$user = $this->create($request->all());
|
||||||
|
|
||||||
if ($user->is_activated)
|
if ($user->is_activated)
|
||||||
{
|
{
|
||||||
$this->logActivatedActivity($user);
|
|
||||||
$this->sendUserActivatedEmails($user);
|
|
||||||
$this->guard()->login($user);
|
$this->guard()->login($user);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Send activation e-mail
|
// Send activation e-mail
|
||||||
$user->notify(new UserActivationRequired());
|
Mail::to($user)->send(new UserActivationRequired($user));
|
||||||
$request->session()->flash('info', trans('auth.activation_required_message'));
|
$request->session()->flash('info', trans('auth.activation_required_message'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +137,7 @@ class RegisterController extends Controller
|
|||||||
*
|
*
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function showRegistrationForm(Request $request)
|
public function showRegistrationForm()
|
||||||
{
|
{
|
||||||
if (!UserConfig::get('allow_self_registration'))
|
if (!UserConfig::get('allow_self_registration'))
|
||||||
{
|
{
|
||||||
@ -155,35 +145,7 @@ class RegisterController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Theme::render('auth.v2_unified', [
|
return Theme::render('auth.v2_unified', [
|
||||||
'active_tab' => 'register',
|
'active_tab' => 'register'
|
||||||
'is_sso' => false
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the application registration form (for a social media-linked account).
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function showRegistrationFormSso(Request $request)
|
|
||||||
{
|
|
||||||
if (!UserConfig::get('allow_self_registration'))
|
|
||||||
{
|
|
||||||
return redirect(route('home'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Social media login info
|
|
||||||
$registerData = $request->getSession()->get('ssoRegisterData');
|
|
||||||
if (is_null($registerData))
|
|
||||||
{
|
|
||||||
// No SSO data in session, use the normal registration screen
|
|
||||||
return redirect(route('register'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('auth.v2_unified', [
|
|
||||||
'active_tab' => 'register',
|
|
||||||
'is_sso' => true,
|
|
||||||
'register_data' => $registerData
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Gallery;
|
namespace App\Http\Controllers\Gallery;
|
||||||
|
|
||||||
|
use App\Album;
|
||||||
use App\AlbumRedirect;
|
use App\AlbumRedirect;
|
||||||
use App\Facade\Theme;
|
use App\Facade\Theme;
|
||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
|
use App\Helpers\ConfigHelper;
|
||||||
use App\Helpers\DbHelper;
|
use App\Helpers\DbHelper;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests;
|
||||||
use App\VisitorHit;
|
use App\VisitorHit;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
@ -31,7 +34,7 @@ class AlbumController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$album = DbHelper::getAlbumById($redirect->album_id);
|
$album = DbHelper::getAlbumById($redirect->album_id);
|
||||||
return redirect($album->url(), 301);
|
return redirect($album->url());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->authorizeForUser($this->getUser(), 'view', $album);
|
$this->authorizeForUser($this->getUser(), 'view', $album);
|
||||||
@ -40,7 +43,7 @@ class AlbumController extends Controller
|
|||||||
$requestedView = strtolower($request->get('view'));
|
$requestedView = strtolower($request->get('view'));
|
||||||
if (!in_array($requestedView, $validViews))
|
if (!in_array($requestedView, $validViews))
|
||||||
{
|
{
|
||||||
$requestedView = strtolower($album->default_view);
|
$requestedView = $album->default_view;
|
||||||
|
|
||||||
if (!in_array($requestedView, $validViews))
|
if (!in_array($requestedView, $validViews))
|
||||||
{
|
{
|
||||||
@ -68,28 +71,20 @@ class AlbumController extends Controller
|
|||||||
else if ($requestedView != 'slideshow')
|
else if ($requestedView != 'slideshow')
|
||||||
{
|
{
|
||||||
$photos = $album->photos()
|
$photos = $album->photos()
|
||||||
->orderBy(DB::raw('COALESCE(taken_at, created_at), name, id'))
|
->orderBy(DB::raw('COALESCE(taken_at, created_at)'))
|
||||||
->paginate(UserConfig::get('items_per_page'));
|
->paginate(UserConfig::get('items_per_page'));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// The slideshow view needs access to all photos, not paged
|
// The slideshow view needs access to all photos, not paged
|
||||||
$photos = $album->photos()
|
$photos = $album->photos()
|
||||||
->orderBy(DB::raw('COALESCE(taken_at, created_at), name, id'))
|
->orderBy(DB::raw('COALESCE(taken_at, created_at)'))
|
||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load child albums and their available children
|
|
||||||
$childAlbums = DbHelper::getChildAlbums($album);
|
|
||||||
foreach ($childAlbums as $childAlbum)
|
|
||||||
{
|
|
||||||
$childAlbum->children_count = DbHelper::getChildAlbumsCount($childAlbum);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render(sprintf('gallery.album_%s', $requestedView), [
|
return Theme::render(sprintf('gallery.album_%s', $requestedView), [
|
||||||
'album' => $album,
|
'album' => $album,
|
||||||
'allowed_views' => $validViews,
|
'allowed_views' => $validViews,
|
||||||
'child_albums' => $childAlbums,
|
|
||||||
'current_view' => $requestedView,
|
'current_view' => $requestedView,
|
||||||
'photos' => $photos
|
'photos' => $photos
|
||||||
]);
|
]);
|
||||||
|
@ -7,7 +7,6 @@ use App\Facade\Theme;
|
|||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Helpers\DbHelper;
|
use App\Helpers\DbHelper;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Label;
|
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
use App\VisitorHit;
|
use App\VisitorHit;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -19,13 +18,6 @@ class DefaultController extends Controller
|
|||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$albums = DbHelper::getAlbumsForCurrentUser(0);
|
$albums = DbHelper::getAlbumsForCurrentUser(0);
|
||||||
|
|
||||||
/** @var Album $album */
|
|
||||||
foreach ($albums as $album)
|
|
||||||
{
|
|
||||||
$album->children_count = DbHelper::getChildAlbumsCount($album);
|
|
||||||
}
|
|
||||||
|
|
||||||
$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)
|
||||||
@ -62,18 +54,6 @@ class DefaultController extends Controller
|
|||||||
// Albums the current user is allowed to access
|
// Albums the current user is allowed to access
|
||||||
$albumIDs = DbHelper::getAlbumIDsForCurrentUser();
|
$albumIDs = DbHelper::getAlbumIDsForCurrentUser();
|
||||||
|
|
||||||
// Add each label
|
|
||||||
$labels = Label::orderBy('name');
|
|
||||||
$labels->chunk(100, function($labelsChunk) use ($xml, $root)
|
|
||||||
{
|
|
||||||
/** @var Label $label */
|
|
||||||
foreach ($labelsChunk as $label)
|
|
||||||
{
|
|
||||||
$lastModifiedPhoto = $label->photos()->orderBy('updated_at', 'desc')->first();
|
|
||||||
$this->createSitemapNode($xml, $root, $label->url(), (is_null($lastModifiedPhoto) ? $label->updated_at : $lastModifiedPhoto->updated_at), '0.9');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add each album URL
|
// Add each album URL
|
||||||
$albums = Album::whereIn('id', $albumIDs)->orderBy('name');
|
$albums = Album::whereIn('id', $albumIDs)->orderBy('name');
|
||||||
$albums->chunk(100, function($albumsChunk) use ($xml, $root)
|
$albums->chunk(100, function($albumsChunk) use ($xml, $root)
|
||||||
@ -82,7 +62,7 @@ class DefaultController extends Controller
|
|||||||
foreach ($albumsChunk as $album)
|
foreach ($albumsChunk as $album)
|
||||||
{
|
{
|
||||||
$lastModifiedPhoto = Photo::where('album_id', $album->id)->orderBy('updated_at', 'desc')->first();
|
$lastModifiedPhoto = Photo::where('album_id', $album->id)->orderBy('updated_at', 'desc')->first();
|
||||||
$this->createSitemapNode($xml, $root, $album->url(), (is_null($lastModifiedPhoto) ? $album->updated_at : $lastModifiedPhoto->updated_at), '0.8');
|
$this->createSitemapNode($xml, $root, $album->url(), (is_null($lastModifiedPhoto) ? $album->updated_at : $lastModifiedPhoto->updated_at), '0.9');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,7 +84,7 @@ class DefaultController extends Controller
|
|||||||
$root,
|
$root,
|
||||||
$photo->url(),
|
$photo->url(),
|
||||||
$photo->updated_at,
|
$photo->updated_at,
|
||||||
'0.7',
|
'0.8',
|
||||||
$photo->thumbnailUrl('fullsize', false),
|
$photo->thumbnailUrl('fullsize', false),
|
||||||
join(' - ', $photoMeta)
|
join(' - ', $photoMeta)
|
||||||
);
|
);
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Gallery;
|
|
||||||
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\User;
|
|
||||||
use App\UserFollower;
|
|
||||||
|
|
||||||
class ExploreController extends Controller
|
|
||||||
{
|
|
||||||
public function users()
|
|
||||||
{
|
|
||||||
if (!UserConfig::get('social_user_profiles'))
|
|
||||||
{
|
|
||||||
return redirect(route('home'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$users = User::where([
|
|
||||||
'is_activated' => true,
|
|
||||||
'enable_profile_page' => true
|
|
||||||
])
|
|
||||||
->orderBy('name')
|
|
||||||
->paginate(UserConfig::get('items_per_page'));
|
|
||||||
|
|
||||||
$usersFollowing = UserFollower::where('user_id', $this->getUser()->id)
|
|
||||||
->select('following_user_id')
|
|
||||||
->get()
|
|
||||||
->map(function($f)
|
|
||||||
{
|
|
||||||
return $f->following_user_id;
|
|
||||||
})
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
return Theme::render('gallery.explore_users', [
|
|
||||||
'users' => $users,
|
|
||||||
'users_following' => $usersFollowing
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Gallery;
|
|
||||||
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Helpers\DbHelper;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Label;
|
|
||||||
use Illuminate\Support\Facades\App;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
|
|
||||||
class LabelController extends Controller
|
|
||||||
{
|
|
||||||
public function index(Request $request)
|
|
||||||
{
|
|
||||||
$labels = Label::orderBy('name')->get();
|
|
||||||
|
|
||||||
/** @var Label $label */
|
|
||||||
foreach ($labels as $label)
|
|
||||||
{
|
|
||||||
$label->photos_count = $label->photoCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('gallery.labels', ['labels' => $labels]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function show(Request $request, $labelAlias)
|
|
||||||
{
|
|
||||||
$label = Label::where('url_alias', $labelAlias)->first();
|
|
||||||
if (is_null($label))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
$validViews = UserConfig::allowedAlbumViews();
|
|
||||||
$requestedView = strtolower($request->get('view'));
|
|
||||||
if (!in_array($requestedView, $validViews))
|
|
||||||
{
|
|
||||||
$requestedView = $validViews[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
$allowedAlbumIDs = DbHelper::getAlbumIDsForCurrentUser();
|
|
||||||
|
|
||||||
if ($label->photos()->count() == 0)
|
|
||||||
{
|
|
||||||
$requestedView = 'empty';
|
|
||||||
$photos = [];
|
|
||||||
}
|
|
||||||
else if ($requestedView != 'slideshow')
|
|
||||||
{
|
|
||||||
$photos = $label->photos()
|
|
||||||
->whereIn('album_id', $allowedAlbumIDs)
|
|
||||||
->orderBy(DB::raw('COALESCE(photos.taken_at, photos.created_at)'))
|
|
||||||
->paginate(UserConfig::get('items_per_page'));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The slideshow view needs access to all photos, not paged
|
|
||||||
$photos = $label->photos()
|
|
||||||
->whereIn('album_id', $allowedAlbumIDs)
|
|
||||||
->orderBy(DB::raw('COALESCE(photos.taken_at, photos.created_at)'))
|
|
||||||
->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($photos) == 0)
|
|
||||||
{
|
|
||||||
$requestedView = 'empty';
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render(sprintf('gallery.label_%s', $requestedView), [
|
|
||||||
'allowed_views' => $validViews,
|
|
||||||
'current_view' => $requestedView,
|
|
||||||
'label' => $label,
|
|
||||||
'photos' => $photos
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,411 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Gallery;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Helpers\DbHelper;
|
|
||||||
use App\Helpers\PermissionsHelper;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Notifications\ModeratePhotoComment;
|
|
||||||
use App\Notifications\PhotoCommentApproved;
|
|
||||||
use App\Notifications\PhotoCommentApprovedUser;
|
|
||||||
use App\Notifications\PhotoCommentRepliedTo;
|
|
||||||
use App\Photo;
|
|
||||||
use App\PhotoComment;
|
|
||||||
use App\User;
|
|
||||||
use App\UserActivity;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\App;
|
|
||||||
use Illuminate\Validation\ValidationException;
|
|
||||||
|
|
||||||
class PhotoCommentController extends Controller
|
|
||||||
{
|
|
||||||
public function moderate(Request $request, $albumUrlAlias, $photoFilename, $commentID)
|
|
||||||
{
|
|
||||||
$album = null;
|
|
||||||
|
|
||||||
/** @var Photo $photo */
|
|
||||||
$photo = null;
|
|
||||||
|
|
||||||
/** @var PhotoComment $comment */
|
|
||||||
$comment = null;
|
|
||||||
|
|
||||||
if (!$this->loadAlbumPhotoComment($albumUrlAlias, $photoFilename, $commentID, $album, $photo, $comment))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!User::currentOrAnonymous()->can('moderate-comments', $photo))
|
|
||||||
{
|
|
||||||
App::abort(403);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$comment->isModerated())
|
|
||||||
{
|
|
||||||
if ($request->has('approve'))
|
|
||||||
{
|
|
||||||
$comment->approved_at = new \DateTime();
|
|
||||||
$comment->approved_user_id = $this->getUser()->id;
|
|
||||||
$comment->save();
|
|
||||||
|
|
||||||
$this->createUserActivityRecord($comment);
|
|
||||||
$this->notifyAlbumOwnerAndPoster($album, $photo, $comment);
|
|
||||||
$request->getSession()->flash('success', trans('gallery.photo_comment_approved_successfully'));
|
|
||||||
}
|
|
||||||
else if ($request->has('reject'))
|
|
||||||
{
|
|
||||||
$comment->rejected_at = new \DateTime();
|
|
||||||
$comment->rejected_user_id = $this->getUser()->id;
|
|
||||||
$comment->save();
|
|
||||||
|
|
||||||
$request->getSession()->flash('success', trans('gallery.photo_comment_rejected_successfully'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect($photo->url());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reply(Request $request, $albumUrlAlias, $photoFilename, $commentID)
|
|
||||||
{
|
|
||||||
$album = null;
|
|
||||||
|
|
||||||
/** @var Photo $photo */
|
|
||||||
$photo = null;
|
|
||||||
|
|
||||||
/** @var PhotoComment $comment */
|
|
||||||
$comment = null;
|
|
||||||
|
|
||||||
if (!$this->loadAlbumPhotoComment($albumUrlAlias, $photoFilename, $commentID, $album, $photo, $comment))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!User::currentOrAnonymous()->can('post-comment', $photo))
|
|
||||||
{
|
|
||||||
App::abort(403);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('partials.photo_comments_reply_form', [
|
|
||||||
'photo' => $photo,
|
|
||||||
'reply_comment' => $comment
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function store(Request $request, $albumUrlAlias, $photoFilename)
|
|
||||||
{
|
|
||||||
$album = null;
|
|
||||||
|
|
||||||
/** @var Photo $photo */
|
|
||||||
$photo = null;
|
|
||||||
|
|
||||||
/** @var PhotoComment $comment */
|
|
||||||
$comment = null;
|
|
||||||
|
|
||||||
if (!$this->loadAlbumPhotoComment($albumUrlAlias, $photoFilename, 0, $album, $photo, $comment))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!User::currentOrAnonymous()->can('post-comment', $photo))
|
|
||||||
{
|
|
||||||
App::abort(403);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate and link the parent comment, if provided
|
|
||||||
// We do this here so if the validation fails, we still have the parent comment available in the catch block
|
|
||||||
$parentComment = null;
|
|
||||||
if ($request->has('parent_comment_id'))
|
|
||||||
{
|
|
||||||
$parentComment = $photo->comments()->where('id', intval($request->get('parent_comment_id')))->first();
|
|
||||||
|
|
||||||
if (is_null($parentComment))
|
|
||||||
{
|
|
||||||
return redirect($photo->url());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$this->validate($request, [
|
|
||||||
'name' => 'required|max:255',
|
|
||||||
'email' => 'sometimes|max:255|email',
|
|
||||||
'comment' => 'required'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$commentText = $this->stripDisallowedHtmlTags($request->get('comment'));
|
|
||||||
|
|
||||||
$comment = new PhotoComment();
|
|
||||||
$comment->photo_id = $photo->id;
|
|
||||||
$comment->fill($request->only(['name', 'email']));
|
|
||||||
$comment->comment = $commentText;
|
|
||||||
|
|
||||||
if (!is_null($parentComment))
|
|
||||||
{
|
|
||||||
$comment->parent_comment_id = $parentComment->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the created user ID if we're logged in
|
|
||||||
$user = $this->getUser();
|
|
||||||
if (!is_null($user) && !$user->isAnonymous())
|
|
||||||
{
|
|
||||||
$comment->created_user_id = $user->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-approve the comment if we're allowed to moderate comments
|
|
||||||
$isAutoApproved = false;
|
|
||||||
if (User::currentOrAnonymous()->can('moderate-comments', $photo))
|
|
||||||
{
|
|
||||||
$comment->approved_at = new \DateTime();
|
|
||||||
$comment->approved_user_id = $user->id;
|
|
||||||
$isAutoApproved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-approve the comment if settings allow
|
|
||||||
if ($user->isAnonymous() && !UserConfig::get('moderate_anonymous_users'))
|
|
||||||
{
|
|
||||||
$comment->approved_at = new \DateTime();
|
|
||||||
$comment->approved_user_id = null; // we don't have a user ID to set!
|
|
||||||
$isAutoApproved = true;
|
|
||||||
}
|
|
||||||
else if (!$user->isAnonymous() && !UserConfig::get('moderate_known_users'))
|
|
||||||
{
|
|
||||||
$comment->approved_at = new \DateTime();
|
|
||||||
$comment->approved_user_id = $user->id;
|
|
||||||
$isAutoApproved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$comment->save();
|
|
||||||
|
|
||||||
// Send notification e-mails to moderators or album owner
|
|
||||||
if (!$isAutoApproved)
|
|
||||||
{
|
|
||||||
$this->notifyAlbumModerators($album, $photo, $comment);
|
|
||||||
$request->getSession()->flash('success', trans('gallery.photo_comment_posted_successfully_pending_moderation'));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Log an activity record for the user's feed
|
|
||||||
$this->createUserActivityRecord($comment);
|
|
||||||
$this->notifyAlbumOwnerAndPoster($album, $photo, $comment);
|
|
||||||
$request->getSession()->flash('success', trans('gallery.photo_comment_posted_successfully'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($request->isXmlHttpRequest())
|
|
||||||
{
|
|
||||||
return response()->json(['redirect_url' => $photo->url()]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return redirect($photo->url());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ValidationException $e)
|
|
||||||
{
|
|
||||||
if (!is_null($parentComment))
|
|
||||||
{
|
|
||||||
return redirect()
|
|
||||||
->to($photo->replyToCommentFormUrl($parentComment->id))
|
|
||||||
->withErrors($e->errors())
|
|
||||||
->withInput($request->all());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return redirect()
|
|
||||||
->back()
|
|
||||||
->withErrors($e->errors())
|
|
||||||
->withInput($request->all());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createUserActivityRecord(PhotoComment $comment)
|
|
||||||
{
|
|
||||||
if (!is_null($comment->created_user_id))
|
|
||||||
{
|
|
||||||
$userActivity = new UserActivity();
|
|
||||||
$userActivity->user_id = $comment->created_user_id;
|
|
||||||
$userActivity->activity_at = $comment->created_at;
|
|
||||||
|
|
||||||
if (is_null($comment->parent_comment_id))
|
|
||||||
{
|
|
||||||
$userActivity->type = 'photo.commented';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$userActivity->type = 'photo.comment_replied';
|
|
||||||
}
|
|
||||||
|
|
||||||
$userActivity->photo_id = $comment->photo_id;
|
|
||||||
$userActivity->photo_comment_id = $comment->id;
|
|
||||||
$userActivity->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function loadAlbumPhotoComment($albumUrlAlias, $photoFilename, $commentID, &$album, &$photo, &$comment)
|
|
||||||
{
|
|
||||||
$album = DbHelper::getAlbumByPath($albumUrlAlias);
|
|
||||||
if (is_null($album))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->authorizeForUser($this->getUser(), 'view', $album);
|
|
||||||
|
|
||||||
$photo = PhotoController::loadPhotoByAlbumAndFilename($album, $photoFilename);
|
|
||||||
|
|
||||||
if (!UserConfig::get('allow_photo_comments'))
|
|
||||||
{
|
|
||||||
// Not allowed to post comments
|
|
||||||
App::abort(404);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intval($commentID > 0))
|
|
||||||
{
|
|
||||||
$comment = $photo->comments()->where('id', $commentID)->first();
|
|
||||||
if (is_null($comment))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a given comment by its ID.
|
|
||||||
* @param $id
|
|
||||||
* @return PhotoComment
|
|
||||||
*/
|
|
||||||
private function loadCommentByID($id)
|
|
||||||
{
|
|
||||||
$comment = PhotoComment::where('id', intval($id))->first();
|
|
||||||
if (is_null($comment))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends an e-mail notification to an album's moderators that a comment is available to moderate.
|
|
||||||
* @param Album $album
|
|
||||||
* @param Photo $photo
|
|
||||||
* @param PhotoComment $comment
|
|
||||||
*/
|
|
||||||
private function notifyAlbumModerators(Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
// Get all users from the cache
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$moderators = $helper->usersWhoCan_Album($album, 'moderate-comments');
|
|
||||||
|
|
||||||
/** @var User $moderator */
|
|
||||||
foreach ($moderators as $moderator)
|
|
||||||
{
|
|
||||||
$moderator->notify(new ModeratePhotoComment($album, $photo, $comment));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends an e-mail notification to an album's owned that a comment has been posted/approved.
|
|
||||||
* @param Album $album
|
|
||||||
* @param Photo $photo
|
|
||||||
* @param PhotoComment $comment
|
|
||||||
*/
|
|
||||||
private function notifyAlbumOwnerAndPoster(Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
/** @var User $owner */
|
|
||||||
$owner = $album->user;
|
|
||||||
|
|
||||||
$owner->notify(new PhotoCommentApproved($album, $photo, $comment));
|
|
||||||
|
|
||||||
// Also send a notification to the comment poster
|
|
||||||
$poster = new User();
|
|
||||||
$poster->name = $comment->authorDisplayName();
|
|
||||||
$poster->email = $comment->authorEmail();
|
|
||||||
|
|
||||||
$poster->notify(new PhotoCommentApprovedUser($album, $photo, $comment));
|
|
||||||
|
|
||||||
// Send notification to the parent comment owner (if this is a reply)
|
|
||||||
if (!is_null($comment->parent_comment_id))
|
|
||||||
{
|
|
||||||
$parentComment = $this->loadCommentByID($comment->parent_comment_id);
|
|
||||||
|
|
||||||
if (is_null($parentComment))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$parentPoster = new User();
|
|
||||||
$parentPoster->name = $parentComment->authorDisplayName();
|
|
||||||
$parentPoster->email = $parentComment->authorEmail();
|
|
||||||
|
|
||||||
$parentPoster->notify(new PhotoCommentRepliedTo($album, $photo, $comment));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function stripDisallowedHtmlTags($commentText)
|
|
||||||
{
|
|
||||||
$allowedHtmlTags = explode(',', UserConfig::get('photo_comments_allowed_html'));
|
|
||||||
$allowedHtmlTagsCleaned = [];
|
|
||||||
|
|
||||||
foreach ($allowedHtmlTags as $tag)
|
|
||||||
{
|
|
||||||
$allowedHtmlTagsCleaned[] = trim($tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match any starting HTML tags
|
|
||||||
$regexMatchString = '/<(?!\/)([a-z]+)(?:\s.*)*>/Us';
|
|
||||||
|
|
||||||
$htmlTagMatches = [];
|
|
||||||
preg_match_all($regexMatchString, $commentText, $htmlTagMatches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
|
|
||||||
|
|
||||||
for ($index = 0; $index < count($htmlTagMatches); $index++)
|
|
||||||
{
|
|
||||||
$htmlTagMatch = $htmlTagMatches[$index];
|
|
||||||
|
|
||||||
$htmlTag = $htmlTagMatch[1][0]; // e.g. "p" for <p>
|
|
||||||
if (in_array($htmlTag, $allowedHtmlTagsCleaned))
|
|
||||||
{
|
|
||||||
// This tag is allowed - carry on
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This tag is not allowed - remove it from the string */
|
|
||||||
|
|
||||||
// Find the closing tag
|
|
||||||
$disallowedStringOffset = $htmlTagMatch[0][1];
|
|
||||||
$endingTagMatches = [];
|
|
||||||
preg_match(sprintf('/(<%1$s.*>)(.+)<\/%1$s>/Us', $htmlTag), $commentText, $endingTagMatches, 0, $disallowedStringOffset);
|
|
||||||
|
|
||||||
// Replace the matched string with the inner string
|
|
||||||
$commentText = substr_replace($commentText, $endingTagMatches[2], $disallowedStringOffset, strlen($endingTagMatches[0]));
|
|
||||||
|
|
||||||
// Adjust the offsets for strings after the one we're processing, so the offsets match up with the string correctly
|
|
||||||
for ($index2 = $index + 1; $index2 < count($htmlTagMatches); $index2++)
|
|
||||||
{
|
|
||||||
// If this string appears entirely BEFORE the next one starts, we need to subtract the entire length.
|
|
||||||
// Otherwise, we only need to substract the length of the start tag, as the next one starts within it.
|
|
||||||
$differenceAfterReplacement = strlen($endingTagMatches[1]);
|
|
||||||
|
|
||||||
if ($htmlTagMatch[0][1] + strlen($endingTagMatches[0]) < $htmlTagMatches[$index2][0][1])
|
|
||||||
{
|
|
||||||
$differenceAfterReplacement = strlen($endingTagMatches[0]) - strlen($endingTagMatches[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$htmlTagMatches[$index2][0][1] -= $differenceAfterReplacement;
|
|
||||||
$htmlTagMatches[$index2][1][1] -= $differenceAfterReplacement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $commentText;
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,15 +6,17 @@ use App\Album;
|
|||||||
use App\Facade\Theme;
|
use App\Facade\Theme;
|
||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Helpers\DbHelper;
|
use App\Helpers\DbHelper;
|
||||||
|
use App\Helpers\MiscHelper;
|
||||||
|
use app\Http\Controllers\Admin\AlbumController;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Middleware\VerifyCsrfToken;
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
use App\VisitorHit;
|
use App\VisitorHit;
|
||||||
use GuzzleHttp\Psr7\Stream;
|
use Guzzle\Http\Mimetypes;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use function GuzzleHttp\Psr7\mimetype_from_extension;
|
|
||||||
|
|
||||||
class PhotoController extends Controller
|
class PhotoController extends Controller
|
||||||
{
|
{
|
||||||
@ -69,9 +71,8 @@ class PhotoController extends Controller
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var Stream $photoStream */
|
|
||||||
$photoStream = $album->getAlbumSource()->fetchPhotoContent($photo, $thumbnail);
|
$photoStream = $album->getAlbumSource()->fetchPhotoContent($photo, $thumbnail);
|
||||||
$mimeType = mimetype_from_extension(pathinfo($photo->storage_file_name, PATHINFO_EXTENSION));
|
$mimeType = Mimetypes::getInstance()->fromFilename($photo->storage_file_name);
|
||||||
|
|
||||||
return response()->stream(
|
return response()->stream(
|
||||||
function() use ($photoStream)
|
function() use ($photoStream)
|
||||||
@ -80,7 +81,7 @@ class PhotoController extends Controller
|
|||||||
},
|
},
|
||||||
200,
|
200,
|
||||||
[
|
[
|
||||||
'Content-Length' => strlen($photoStream->getContents()),
|
'Content-Length' => $photoStream->getContentLength(),
|
||||||
'Content-Type' => $mimeType
|
'Content-Type' => $mimeType
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@ -101,42 +102,11 @@ class PhotoController extends Controller
|
|||||||
|
|
||||||
$isOriginalAllowed = Gate::forUser($this->getUser())->allows('photo.download_original', $photo);
|
$isOriginalAllowed = Gate::forUser($this->getUser())->allows('photo.download_original', $photo);
|
||||||
|
|
||||||
// Load the Next/Previous buttons
|
$returnAlbumUrl = $album->url();
|
||||||
$thisPhotoDate = is_null($photo->taken_at) ? $photo->created_at : $photo->taken_at;
|
$referer = $request->headers->get('Referer');
|
||||||
|
if (strlen($referer) > 0 && MiscHelper::isSafeUrl($referer))
|
||||||
// I don't like the idea of using a totally raw SQL query, but it's the only sure-fire way to number the rows
|
|
||||||
// so we can get the previous/next photos accurately - and we don't have to load all data for the photo objects
|
|
||||||
$previousPhoto = null;
|
|
||||||
$nextPhoto = null;
|
|
||||||
|
|
||||||
$allAlbumPhotos = DB::select(
|
|
||||||
DB::raw(
|
|
||||||
'SELECT p.id, (@row_number:=@row_number + 1) AS row_number
|
|
||||||
FROM photos p, (SELECT @row_number:=0) AS t
|
|
||||||
WHERE p.album_id = :album_id
|
|
||||||
ORDER BY COALESCE(p.taken_at, p.created_at), p.name, p.id;'
|
|
||||||
),
|
|
||||||
[
|
|
||||||
'album_id' => $album->id
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
for ($i = 0; $i < count($allAlbumPhotos); $i++)
|
|
||||||
{
|
{
|
||||||
if ($allAlbumPhotos[$i]->id === $photo->id)
|
$returnAlbumUrl = $referer;
|
||||||
{
|
|
||||||
if ($i > 0)
|
|
||||||
{
|
|
||||||
$previousPhoto = Photo::where('id', $allAlbumPhotos[$i - 1]->id)->first();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($i + 1 < count($allAlbumPhotos))
|
|
||||||
{
|
|
||||||
$nextPhoto = Photo::where('id', $allAlbumPhotos[$i + 1]->id)->first();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the visit to the photo
|
// Record the visit to the photo
|
||||||
@ -154,33 +124,8 @@ class PhotoController extends Controller
|
|||||||
return Theme::render('gallery.photo', [
|
return Theme::render('gallery.photo', [
|
||||||
'album' => $album,
|
'album' => $album,
|
||||||
'is_original_allowed' => $isOriginalAllowed,
|
'is_original_allowed' => $isOriginalAllowed,
|
||||||
'next_photo' => $nextPhoto,
|
|
||||||
'photo' => $photo,
|
'photo' => $photo,
|
||||||
'previous_photo' => $previousPhoto,
|
'return_album_url' => $returnAlbumUrl
|
||||||
'success' => $request->getSession()->get('success')
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function showExifData(Request $request, $albumUrlAlias, $photoFilename)
|
|
||||||
{
|
|
||||||
$album = DbHelper::getAlbumByPath($albumUrlAlias);
|
|
||||||
if (is_null($album))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->authorizeForUser($this->getUser(), 'view', $album);
|
|
||||||
|
|
||||||
$photo = PhotoController::loadPhotoByAlbumAndFilename($album, $photoFilename);
|
|
||||||
$this->authorizeForUser($this->getUser(), 'changeMetadata', $photo);
|
|
||||||
|
|
||||||
$exifData = print_r(unserialize(base64_decode($photo->raw_exif_data)), true);
|
|
||||||
|
|
||||||
return Theme::render('gallery.photo_exif', [
|
|
||||||
'album' => $album,
|
|
||||||
'exif_data' => $exifData,
|
|
||||||
'photo' => $photo
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,313 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Gallery;
|
|
||||||
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Helpers\DbHelper;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Label;
|
|
||||||
use App\Photo;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class StatisticsController extends Controller
|
|
||||||
{
|
|
||||||
public function albumSizeByPhotosChart(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
|
|
||||||
|
|
||||||
$stats = DB::table('photos')
|
|
||||||
->whereIn('photos.album_id', DbHelper::getAlbumIDsForCurrentUser())
|
|
||||||
->join('albums', 'albums.id', '=', 'photos.album_id')
|
|
||||||
->groupBy('albums.name')
|
|
||||||
->select('albums.name', DB::raw('count(photos.id) as photo_count'))
|
|
||||||
->orderBy('photo_count', 'desc')
|
|
||||||
->limit(10)
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$labels = [];
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($stats as $stat)
|
|
||||||
{
|
|
||||||
$labels[] = $stat->name;
|
|
||||||
$data[] = $stat->photo_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'labels' => $labels,
|
|
||||||
'backgrounds' => $this->rotateColoursForData($data),
|
|
||||||
'data' => $data
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function albumSizeByPhotoSizeChart(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
|
|
||||||
|
|
||||||
$stats = DB::table('photos')
|
|
||||||
->whereIn('photos.album_id', DbHelper::getAlbumIDsForCurrentUser())
|
|
||||||
->join('albums', 'albums.id', '=', 'photos.album_id')
|
|
||||||
->groupBy('albums.name')
|
|
||||||
->select('albums.name', DB::raw('sum(photos.file_size) as photo_size'))
|
|
||||||
->orderBy('photo_size', 'desc')
|
|
||||||
->limit(10)
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$labels = [];
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($stats as $stat)
|
|
||||||
{
|
|
||||||
$labels[] = $stat->name;
|
|
||||||
$data[] = ceil($stat->photo_size / 1024 / 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'labels' => $labels,
|
|
||||||
'backgrounds' => $this->rotateColoursForData($data),
|
|
||||||
'data' => $data
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function camerasChart(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
|
|
||||||
|
|
||||||
$stats = DB::table('photos')
|
|
||||||
->where([
|
|
||||||
['camera_make', '!=', ''],
|
|
||||||
['camera_model', '!=', '']
|
|
||||||
])
|
|
||||||
->whereIn('album_id', DbHelper::getAlbumIDsForCurrentUser())
|
|
||||||
->groupBy('camera_make', 'camera_model')
|
|
||||||
->select('camera_make', 'camera_model', DB::raw('count(*) as photo_count'))
|
|
||||||
->orderBy('photo_count', 'desc')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$labels = [];
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($stats as $stat)
|
|
||||||
{
|
|
||||||
// Remove the model from the make if it starts with it
|
|
||||||
// E.g. CANON - CANON EOS 1200D becomes just CANON EOS 1200D
|
|
||||||
if (substr($stat->camera_model, 0, strlen($stat->camera_make)) == $stat->camera_make)
|
|
||||||
{
|
|
||||||
$stat->camera_make = trim(substr($stat->camera_make, strlen($stat->camera_make)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$labels[] = sprintf('%s %s', $stat->camera_make, $stat->camera_model);
|
|
||||||
$data[] = $stat->photo_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'labels' => $labels,
|
|
||||||
'backgrounds' => $this->rotateColoursForData($data),
|
|
||||||
'data' => $data
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fileSizeChart(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
|
|
||||||
|
|
||||||
$labels = [
|
|
||||||
trans('gallery.statistics.file_sizes_legend.small'),
|
|
||||||
trans('gallery.statistics.file_sizes_legend.medium'),
|
|
||||||
trans('gallery.statistics.file_sizes_legend.large'),
|
|
||||||
trans('gallery.statistics.file_sizes_legend.huge')
|
|
||||||
];
|
|
||||||
$data = [0, 0, 0, 0];
|
|
||||||
|
|
||||||
$stats = DB::table('photos')->whereIn('album_id', DbHelper::getAlbumIDsForCurrentUser())->orderBy('id');
|
|
||||||
$stats->chunk(100, function($photos) use (&$data)
|
|
||||||
{
|
|
||||||
foreach ($photos as $photo)
|
|
||||||
{
|
|
||||||
if ($photo->file_size < (1 * 1024 * 1024))
|
|
||||||
{
|
|
||||||
$data[0]++;
|
|
||||||
}
|
|
||||||
else if ($photo->file_size < (3 * 1024 * 1024))
|
|
||||||
{
|
|
||||||
$data[1]++;
|
|
||||||
}
|
|
||||||
else if ($photo->file_size < (5 * 1024 * 1024))
|
|
||||||
{
|
|
||||||
$data[2]++;
|
|
||||||
}
|
|
||||||
else if ($photo->file_size >= (5 * 1024 * 1024))
|
|
||||||
{
|
|
||||||
$data[3]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'labels' => $labels,
|
|
||||||
'backgrounds' => $this->rotateColoursForData($data),
|
|
||||||
'data' => $data
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
|
|
||||||
|
|
||||||
// Numbers for at-a-glance
|
|
||||||
$albumIDs = DbHelper::getAlbumIDsForCurrentUser();
|
|
||||||
|
|
||||||
$albumCount = count($albumIDs);
|
|
||||||
$labelCount = Label::all()->count();
|
|
||||||
$photoCount = Photo::whereIn('album_id', $albumIDs)->count();
|
|
||||||
|
|
||||||
return Theme::render('gallery.statistics', [
|
|
||||||
'album_count' => $albumCount,
|
|
||||||
'label_count' => $labelCount,
|
|
||||||
'photo_count' => $photoCount
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function photosCombined(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
|
|
||||||
|
|
||||||
$labels = [];
|
|
||||||
$data = [
|
|
||||||
['label' => trans('gallery.statistics.photos_combined.taken'), 'values' => []],
|
|
||||||
['label' => trans('gallery.statistics.photos_combined.uploaded'), 'values' => []]
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($this->lastXMonthsDates(18) as $date)
|
|
||||||
{
|
|
||||||
$fromDate = sprintf('%04d-%02d-01 00:00:00', $date[0], $date[1]);
|
|
||||||
$toDate = sprintf('%04d-%02d-%02d 23:59:59', $date[0], $date[1], cal_days_in_month(CAL_GREGORIAN, $date[1], $date[0]));
|
|
||||||
|
|
||||||
$photoCountTaken = Photo::whereBetween('taken_at', array($fromDate, $toDate))->count();
|
|
||||||
$photoCountUploaded = Photo::whereBetween('created_at', array($fromDate, $toDate))->count();
|
|
||||||
|
|
||||||
$labels[] = date('M Y', strtotime($fromDate));
|
|
||||||
$data[0]['values'][] = $photoCountTaken;
|
|
||||||
$data[1]['values'][] = $photoCountUploaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data[0]['values'] = array_reverse($data[0]['values']);
|
|
||||||
$data[1]['values'] = array_reverse($data[1]['values']);
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'labels' => array_reverse($labels),
|
|
||||||
'data' => $data
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function photosTaken12Months(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
|
|
||||||
|
|
||||||
$labels = [];
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->lastXMonthsDates() as $date)
|
|
||||||
{
|
|
||||||
$fromDate = sprintf('%04d-%02d-01 00:00:00', $date[0], $date[1]);
|
|
||||||
$toDate = sprintf('%04d-%02d-%02d 23:59:59', $date[0], $date[1], cal_days_in_month(CAL_GREGORIAN, $date[1], $date[0]));
|
|
||||||
|
|
||||||
$photoCount = Photo::whereBetween('taken_at', array($fromDate, $toDate))->count();
|
|
||||||
|
|
||||||
$labels[] = date('M Y', strtotime($fromDate));
|
|
||||||
$data[] = $photoCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'labels' => array_reverse($labels),
|
|
||||||
'data' => array_reverse($data)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function photosUploaded12Months(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
|
|
||||||
|
|
||||||
$labels = [];
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->lastXMonthsDates() as $date)
|
|
||||||
{
|
|
||||||
$fromDate = sprintf('%04d-%02d-01 00:00:00', $date[0], $date[1]);
|
|
||||||
$toDate = sprintf('%04d-%02d-%02d 23:59:59', $date[0], $date[1], cal_days_in_month(CAL_GREGORIAN, $date[1], $date[0]));
|
|
||||||
|
|
||||||
$photoCount = Photo::whereBetween('created_at', array($fromDate, $toDate))->count();
|
|
||||||
|
|
||||||
$labels[] = date('M Y', strtotime($fromDate));
|
|
||||||
$data[] = $photoCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'labels' => array_reverse($labels),
|
|
||||||
'data' => array_reverse($data)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function lastXMonthsDates($x = 12)
|
|
||||||
{
|
|
||||||
$year = intval(date('Y'));
|
|
||||||
$month = intval(date('m'));
|
|
||||||
|
|
||||||
$datesNeeded = [];
|
|
||||||
|
|
||||||
while (count($datesNeeded) < $x)
|
|
||||||
{
|
|
||||||
$datesNeeded[] = [$year, $month];
|
|
||||||
|
|
||||||
$month--;
|
|
||||||
if ($month == 0)
|
|
||||||
{
|
|
||||||
$month = 12;
|
|
||||||
$year--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $datesNeeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function rotateColoursForData(array $data = [])
|
|
||||||
{
|
|
||||||
$colours = [
|
|
||||||
'#d54d36',
|
|
||||||
'#59c669',
|
|
||||||
'#aa5ccf',
|
|
||||||
'#85b83a',
|
|
||||||
'#5f6cd9',
|
|
||||||
'#bbb248',
|
|
||||||
'#ca49a1',
|
|
||||||
'#479341',
|
|
||||||
'#d94b70',
|
|
||||||
'#52b395',
|
|
||||||
'#7b589e',
|
|
||||||
'#da8f32',
|
|
||||||
'#6e8bd0',
|
|
||||||
'#8a722c',
|
|
||||||
'#46aed7',
|
|
||||||
'#aa5839',
|
|
||||||
'#d48cca',
|
|
||||||
'#64803f',
|
|
||||||
'#a5506d',
|
|
||||||
'#e19774'
|
|
||||||
];
|
|
||||||
$result = [];
|
|
||||||
$lastIndex = 0;
|
|
||||||
|
|
||||||
for ($i = 0; $i < count($data); $i++)
|
|
||||||
{
|
|
||||||
$result[] = $colours[$lastIndex];
|
|
||||||
$lastIndex++;
|
|
||||||
if ($lastIndex >= count($colours))
|
|
||||||
{
|
|
||||||
$lastIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,531 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Gallery;
|
|
||||||
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Helpers\DbHelper;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Http\Requests\SaveUserSettingsRequest;
|
|
||||||
use App\Notifications\UserChangeEmailRequired;
|
|
||||||
use App\User;
|
|
||||||
use App\UserActivity;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\App;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
|
|
||||||
class UserController extends Controller
|
|
||||||
{
|
|
||||||
public function activityFeed()
|
|
||||||
{
|
|
||||||
if (!UserConfig::get('social_user_feeds'))
|
|
||||||
{
|
|
||||||
return redirect(route('home'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('gallery.user_activity_feed', [
|
|
||||||
'user' => $this->getUser()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function activityFeedJson()
|
|
||||||
{
|
|
||||||
if (!UserConfig::get('social_user_feeds'))
|
|
||||||
{
|
|
||||||
return response()->json(['message' => 'Activity feeds not enabled']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = $this->getUser();
|
|
||||||
|
|
||||||
$result = [];
|
|
||||||
$activities = UserActivity::with('photo')
|
|
||||||
->with('photoComment')
|
|
||||||
->with('user')
|
|
||||||
->join('user_followers', 'user_followers.following_user_id', '=', 'user_activity.user_id')
|
|
||||||
->where([
|
|
||||||
'user_followers.user_id' => $user->id
|
|
||||||
])
|
|
||||||
->orderBy('activity_at', 'desc')
|
|
||||||
->limit(100) // TODO: make this configurable
|
|
||||||
->select('user_activity.*')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
/** @var UserActivity $activity */
|
|
||||||
foreach ($activities as $activity)
|
|
||||||
{
|
|
||||||
$userName = $activity->user->name;
|
|
||||||
$userProfileUrl = $activity->user->profileUrl();
|
|
||||||
$userAvatar = Theme::gravatarUrl($activity->user->email, 32);
|
|
||||||
|
|
||||||
$newItem = [
|
|
||||||
'activity_at' => date(UserConfig::get('date_format'), strtotime($activity->activity_at)),
|
|
||||||
'avatar' => $userAvatar,
|
|
||||||
'description' => trans(sprintf('gallery.user_feed_type.%s', $activity->type))
|
|
||||||
];
|
|
||||||
|
|
||||||
$params = [];
|
|
||||||
$params['user_name'] = $userName;
|
|
||||||
$params['user_url'] = $userProfileUrl;
|
|
||||||
|
|
||||||
if (!is_null($activity->photo))
|
|
||||||
{
|
|
||||||
// Check the user has access
|
|
||||||
if (!$this->getUser()->can('view', $activity->photo))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$params['photo_name'] = $activity->photo->name;
|
|
||||||
$params['photo_url'] = $activity->photo->url();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_null($activity->album))
|
|
||||||
{
|
|
||||||
// Check the user has access
|
|
||||||
if (!$this->getUser()->can('view', $activity->album))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$params['album_name'] = $activity->album->name;
|
|
||||||
$params['album_url'] = $activity->album->url();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other activity-specific parameters
|
|
||||||
switch (strtolower($activity->type))
|
|
||||||
{
|
|
||||||
case 'user.created':
|
|
||||||
$params['app_name'] = UserConfig::get('app_name');
|
|
||||||
$params['app_url'] = route('home');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$newItem['params'] = $params;
|
|
||||||
|
|
||||||
$result[] = $newItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json($result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function confirmEmailChangeState(Request $request)
|
|
||||||
{
|
|
||||||
$user = $this->getUser();
|
|
||||||
if (!$user->is_email_change_in_progress)
|
|
||||||
{
|
|
||||||
return redirect(route('userSettings'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the e-mail address
|
|
||||||
$user->email = $user->new_email_address;
|
|
||||||
|
|
||||||
// Reset the e-mail change state
|
|
||||||
$user->is_email_change_in_progress = false;
|
|
||||||
$user->new_email_address = null;
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
$request->session()->flash('success', trans('auth.change_email_success_message'));
|
|
||||||
return redirect(route('userSettings'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function followUser($idOrAlias)
|
|
||||||
{
|
|
||||||
$user = $this->loadUserProfilePage($idOrAlias);
|
|
||||||
|
|
||||||
$isFollowing = $this->getUser()->following()->where('following_user_id', $user->id)->count() > 0;
|
|
||||||
if (!$isFollowing)
|
|
||||||
{
|
|
||||||
$this->getUser()->following()->attach(
|
|
||||||
$user->id,
|
|
||||||
[
|
|
||||||
'created_at' => new \DateTime(),
|
|
||||||
'updated_at' => new \DateTime()
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function resetEmailChangeState(Request $request)
|
|
||||||
{
|
|
||||||
$user = $this->getUser();
|
|
||||||
if (!$user->is_email_change_in_progress)
|
|
||||||
{
|
|
||||||
return redirect(route('userSettings'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $request->all();
|
|
||||||
|
|
||||||
if (isset($data['resend_email']))
|
|
||||||
{
|
|
||||||
$this->sendEmailChangeConfirmationEmail($user, $user->new_email_address);
|
|
||||||
$request->session()->flash('info', trans('auth.change_email_required_message'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($data['cancel_change']))
|
|
||||||
{
|
|
||||||
$user->is_email_change_in_progress = false;
|
|
||||||
$user->new_email_address = null;
|
|
||||||
$user->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect(route('userSettings'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function saveSettings(SaveUserSettingsRequest $request)
|
|
||||||
{
|
|
||||||
$data = $request->only(['name', 'email', 'profile_alias', 'enable_profile_page']);
|
|
||||||
$user = $this->getUser();
|
|
||||||
|
|
||||||
if (
|
|
||||||
UserConfig::get('require_email_verification') &&
|
|
||||||
isset($data['email']) &&
|
|
||||||
$data['email'] != $user->email &&
|
|
||||||
!$user->is_email_change_in_progress
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Can't update the e-mail directly until the new e-mail address has been verified.
|
|
||||||
// TODO - send e-mail and handle response, flag e-mail as being "change in-progress"
|
|
||||||
// Send activation e-mail
|
|
||||||
|
|
||||||
$this->sendEmailChangeConfirmationEmail($user, $data['email']);
|
|
||||||
$request->session()->flash('info', trans('auth.change_email_required_message'));
|
|
||||||
|
|
||||||
// Flag the user as a change e-mail in progress
|
|
||||||
$user->new_email_address = $data['email'];
|
|
||||||
$user->is_email_change_in_progress = true;
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
unset($data['email']);
|
|
||||||
$request->session()->flash('info', trans('auth.change_email_required_message'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't allow e-mail address to be changed if a change is in progress
|
|
||||||
if ($user->is_email_change_in_progress)
|
|
||||||
{
|
|
||||||
unset($data['email']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$user->fill($data);
|
|
||||||
$user->enable_profile_page = (isset($data['enable_profile_page']) && strtolower($data['enable_profile_page']) == 'on');
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
$request->session()->flash('success', trans('gallery.user_settings.settings_saved'));
|
|
||||||
|
|
||||||
return redirect(route('userSettings'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function settings(Request $request)
|
|
||||||
{
|
|
||||||
return Theme::render('gallery.user_settings', [
|
|
||||||
'info' => $request->session()->get('info'),
|
|
||||||
'success' => $request->session()->get('success'),
|
|
||||||
'user' => $this->getUser()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function show(Request $request, $idOrAlias)
|
|
||||||
{
|
|
||||||
$user = $this->loadUserProfilePage($idOrAlias);
|
|
||||||
|
|
||||||
$albums = $this->getAlbumsForUser($user);
|
|
||||||
$albumIDs = $this->getAlbumIDsForUser($user);
|
|
||||||
$cameras = $this->getCamerasUsedInAlbums($albumIDs);
|
|
||||||
$activity = $this->getActivityDatesInAlbums($albumIDs);
|
|
||||||
|
|
||||||
$daysInMonth = $this->getDaysInMonths();
|
|
||||||
|
|
||||||
// Only logged-in users can follow other users (and if it's not their own page!)
|
|
||||||
$canFollow = !$this->getUser()->isAnonymous() && $this->getUser()->id != $user->id;
|
|
||||||
$isFollowing = false;
|
|
||||||
|
|
||||||
if ($canFollow)
|
|
||||||
{
|
|
||||||
// Is the current user following this user?
|
|
||||||
$isFollowing = $this->getUser()->following()->where('following_user_id', $user->id)->count() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme::render('gallery.user_profile', [
|
|
||||||
'active_tab' => $request->get('tab'),
|
|
||||||
'activity_taken' => $this->constructActivityGrid($activity['taken']),
|
|
||||||
'activity_uploaded' => $this->constructActivityGrid($activity['uploaded']),
|
|
||||||
'albums' => $albums,
|
|
||||||
'cameras' => $cameras,
|
|
||||||
'can_follow' => $canFollow,
|
|
||||||
'is_following' => $isFollowing,
|
|
||||||
'month_days' => $daysInMonth,
|
|
||||||
'user' => $user
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function showFeedJson(Request $request, $idOrAlias)
|
|
||||||
{
|
|
||||||
$user = $this->loadUserProfilePage($idOrAlias);
|
|
||||||
|
|
||||||
$result = [];
|
|
||||||
$activities = UserActivity::with('photo')
|
|
||||||
->with('photoComment')
|
|
||||||
->with('album')
|
|
||||||
->where([
|
|
||||||
'user_id' => $user->id
|
|
||||||
])
|
|
||||||
->orderBy('activity_at', 'desc')
|
|
||||||
->limit(100) // TODO: make this configurable
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$userName = $user->name;
|
|
||||||
$userProfileUrl = $user->profileUrl();
|
|
||||||
$userAvatar = Theme::gravatarUrl($user->email, 32);
|
|
||||||
|
|
||||||
/** @var UserActivity $activity */
|
|
||||||
foreach ($activities as $activity)
|
|
||||||
{
|
|
||||||
$newItem = [
|
|
||||||
'activity_at' => date(UserConfig::get('date_format'), strtotime($activity->activity_at)),
|
|
||||||
'avatar' => $userAvatar,
|
|
||||||
'description' => trans(sprintf('gallery.user_feed_type.%s', $activity->type))
|
|
||||||
];
|
|
||||||
|
|
||||||
$params = [];
|
|
||||||
$params['user_name'] = $userName;
|
|
||||||
$params['user_url'] = $userProfileUrl;
|
|
||||||
|
|
||||||
if (!is_null($activity->photo))
|
|
||||||
{
|
|
||||||
// Check the user has access
|
|
||||||
if (!$this->getUser()->can('view', $activity->photo))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$params['photo_name'] = $activity->photo->name;
|
|
||||||
$params['photo_url'] = $activity->photo->url();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_null($activity->album))
|
|
||||||
{
|
|
||||||
// Check the user has access
|
|
||||||
if (!$this->getUser()->can('view', $activity->album))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$params['album_name'] = $activity->album->name;
|
|
||||||
$params['album_url'] = $activity->album->url();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other activity-specific parameters
|
|
||||||
switch (strtolower($activity->type))
|
|
||||||
{
|
|
||||||
case 'user.created':
|
|
||||||
$params['app_name'] = UserConfig::get('app_name');
|
|
||||||
$params['app_url'] = route('home');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$newItem['params'] = $params;
|
|
||||||
|
|
||||||
$result[] = $newItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json($result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unFollowUser($idOrAlias)
|
|
||||||
{
|
|
||||||
$user = $this->loadUserProfilePage($idOrAlias);
|
|
||||||
|
|
||||||
$isFollowing = $this->getUser()->following()->where('following_user_id', $user->id)->count() > 0;
|
|
||||||
if ($isFollowing)
|
|
||||||
{
|
|
||||||
$this->getUser()->following()->detach($user->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function constructActivityGrid(Collection $collection)
|
|
||||||
{
|
|
||||||
$results = [];
|
|
||||||
|
|
||||||
$lastYearFrom = new \DateTime();
|
|
||||||
$lastYearFrom->sub(new \DateInterval('P1Y'));
|
|
||||||
$lastYearFrom->add(new \DateInterval('P1D'));
|
|
||||||
|
|
||||||
$today = new \DateTime();
|
|
||||||
$current = clone $lastYearFrom;
|
|
||||||
|
|
||||||
while ($current < $today)
|
|
||||||
{
|
|
||||||
$year = intval($current->format('Y'));
|
|
||||||
$month = intval($current->format('m'));
|
|
||||||
$date = intval($current->format('d'));
|
|
||||||
|
|
||||||
if (!isset($results[$year]))
|
|
||||||
{
|
|
||||||
$results[$year] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($results[$year][$month]))
|
|
||||||
{
|
|
||||||
$results[$year][$month] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($results[$year][$month][$date]))
|
|
||||||
{
|
|
||||||
$results[$year][$month][$date] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$current->add(new \DateInterval('P1D'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now update the totals from the collection
|
|
||||||
foreach ($collection as $photoInfo)
|
|
||||||
{
|
|
||||||
$date = \DateTime::createFromFormat('Y-m-d', $photoInfo->the_date);
|
|
||||||
|
|
||||||
$year = intval($date->format('Y'));
|
|
||||||
$month = intval($date->format('m'));
|
|
||||||
$date = intval($date->format('d'));
|
|
||||||
|
|
||||||
$results[$year][$month][$date] = $photoInfo->photos_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace the month names
|
|
||||||
foreach ($results as $year => &$months)
|
|
||||||
{
|
|
||||||
foreach ($months as $month => $dates)
|
|
||||||
{
|
|
||||||
$monthDate = \DateTime::createFromFormat('m', $month);
|
|
||||||
$months[$monthDate->format('M')] = $dates;
|
|
||||||
unset($months[$month]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getActivityDatesInAlbums(array $albumIDs)
|
|
||||||
{
|
|
||||||
$createdAt = DB::table('photos')
|
|
||||||
->whereIn('album_id', $albumIDs)
|
|
||||||
->whereRaw(DB::raw('DATE(created_at) > DATE(DATE_SUB(NOW(), INTERVAL 1 year))'))
|
|
||||||
->select([
|
|
||||||
DB::raw('DATE(created_at) AS the_date'),
|
|
||||||
DB::raw('COUNT(photos.id) AS photos_count')
|
|
||||||
])
|
|
||||||
->groupBy(DB::raw('DATE(created_at)'))
|
|
||||||
->orderBy(DB::raw('DATE(created_at)'))
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$takenAt = DB::table('photos')
|
|
||||||
->whereIn('album_id', $albumIDs)
|
|
||||||
->whereRaw(DB::raw('DATE(taken_at) > DATE(DATE_SUB(NOW(), INTERVAL 1 year))'))
|
|
||||||
->select([
|
|
||||||
DB::raw('DATE(taken_at) AS the_date'),
|
|
||||||
DB::raw('COUNT(photos.id) AS photos_count')
|
|
||||||
])
|
|
||||||
->groupBy(DB::raw('DATE(taken_at)'))
|
|
||||||
->orderBy(DB::raw('DATE(taken_at)'))
|
|
||||||
->get();
|
|
||||||
|
|
||||||
return ['uploaded' => $createdAt, 'taken' => $takenAt];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getAlbumsForUser(User $user)
|
|
||||||
{
|
|
||||||
return DbHelper::getAlbumsForCurrentUser_NonPaged()
|
|
||||||
->where('user_id', $user->id)
|
|
||||||
->paginate(UserConfig::get('items_per_page'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getAlbumIDsForUser(User $user)
|
|
||||||
{
|
|
||||||
$results = [];
|
|
||||||
|
|
||||||
$albums = DbHelper::getAlbumsForCurrentUser_NonPaged()
|
|
||||||
->where('user_id', $user->id)
|
|
||||||
->select('albums.id')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($albums as $album)
|
|
||||||
{
|
|
||||||
$results[] = intval($album->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getCamerasUsedInAlbums(array $albumIDs)
|
|
||||||
{
|
|
||||||
return DB::table('photos')
|
|
||||||
->whereIn('album_id', $albumIDs)
|
|
||||||
->where([
|
|
||||||
['camera_make', '!=', ''],
|
|
||||||
['camera_model', '!=', '']
|
|
||||||
])
|
|
||||||
->groupBy('camera_make', 'camera_model', 'camera_software')
|
|
||||||
->select('camera_make', 'camera_model', 'camera_software', DB::raw('count(*) as photo_count'))
|
|
||||||
->orderBy('photo_count', 'desc')
|
|
||||||
->orderBy('camera_make')
|
|
||||||
->orderBy('camera_model')
|
|
||||||
->orderBy('camera_software')
|
|
||||||
->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getDaysInMonths()
|
|
||||||
{
|
|
||||||
$results = [];
|
|
||||||
|
|
||||||
$lastYearFrom = new \DateTime();
|
|
||||||
$lastYearFrom->sub(new \DateInterval('P1Y'));
|
|
||||||
$lastYearFrom->sub(new \DateInterval(sprintf('P%dD', $lastYearFrom->format('d') - 1)));
|
|
||||||
|
|
||||||
$today = new \DateTime();
|
|
||||||
$current = clone $lastYearFrom;
|
|
||||||
|
|
||||||
while ($current < $today)
|
|
||||||
{
|
|
||||||
$year = intval($current->format('Y'));
|
|
||||||
$month = intval($current->format('m'));
|
|
||||||
|
|
||||||
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
|
|
||||||
$results[$year][$current->format('M')] = $daysInMonth;
|
|
||||||
|
|
||||||
$current->add(new \DateInterval('P1M'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $idOrAlias
|
|
||||||
* @return User
|
|
||||||
*/
|
|
||||||
private function loadUserProfilePage($idOrAlias)
|
|
||||||
{
|
|
||||||
// If a user has a profile alias set, their profile page cannot be accessed by the ID
|
|
||||||
$user = User::where(DB::raw('COALESCE(NULLIF(profile_alias, \'\'), id)'), strtolower($idOrAlias))->first();
|
|
||||||
|
|
||||||
if (is_null($user))
|
|
||||||
{
|
|
||||||
App::abort(404);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->authorizeForUser($this->getUser(), 'view', $user);
|
|
||||||
|
|
||||||
return $user;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sendEmailChangeConfirmationEmail(User $user, $newEmailAddress)
|
|
||||||
{
|
|
||||||
$oldEmailAddress = $user->email;
|
|
||||||
$user->email = $newEmailAddress;
|
|
||||||
|
|
||||||
$user->notify(new UserChangeEmailRequired());
|
|
||||||
$user->email = $oldEmailAddress;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\AlbumDefaultAnonymousPermission;
|
|
||||||
use App\Configuration;
|
use App\Configuration;
|
||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Helpers\MiscHelper;
|
use App\Helpers\MiscHelper;
|
||||||
use App\Http\Requests\StoreUserRequest;
|
use App\Http\Requests\StoreUserRequest;
|
||||||
use App\Permission;
|
|
||||||
use App\Storage;
|
use App\Storage;
|
||||||
use App\User;
|
use App\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -19,10 +17,10 @@ class InstallController extends Controller
|
|||||||
public function administrator(StoreUserRequest $request)
|
public function administrator(StoreUserRequest $request)
|
||||||
{
|
{
|
||||||
// Validate we're at the required stage
|
// Validate we're at the required stage
|
||||||
$stage = 2;
|
$stage = 3;
|
||||||
if (intval($request->session()->get('install_stage')) < $stage)
|
if (intval($request->session()->get('install_stage')) < $stage)
|
||||||
{
|
{
|
||||||
return redirect(route('install.database'));
|
return redirect(route('install.check'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we already have an admin account, this step can be skipped
|
// If we already have an admin account, this step can be skipped
|
||||||
@ -41,7 +39,6 @@ class InstallController extends Controller
|
|||||||
$user->password = bcrypt($request->get('password'));
|
$user->password = bcrypt($request->get('password'));
|
||||||
$user->is_admin = true;
|
$user->is_admin = true;
|
||||||
$user->is_activated = true;
|
$user->is_activated = true;
|
||||||
$user->enable_profile_page = true;
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
return $this->completeSetup();
|
return $this->completeSetup();
|
||||||
@ -52,10 +49,71 @@ class InstallController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function database(Request $request)
|
public function check(Request $request)
|
||||||
{
|
{
|
||||||
// This is the first installation step therefore it doesn't need to verify the stage
|
// This is the first installation step therefore it doesn't need to verify the stage
|
||||||
|
|
||||||
|
if ($request->getMethod() == 'POST')
|
||||||
|
{
|
||||||
|
$request->session()->set('install_stage', 2);
|
||||||
|
return redirect(route('install.database'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$canContinue = true;
|
||||||
|
$runtimeMinimum = '5.6.4'; // this minimum is imposed by Laravel 5.3
|
||||||
|
$runtimeVersion = phpversion();
|
||||||
|
$phpIsValid = version_compare($runtimeVersion, $runtimeMinimum) >= 0;
|
||||||
|
|
||||||
|
if (!$phpIsValid)
|
||||||
|
{
|
||||||
|
$canContinue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$requiredModules = [
|
||||||
|
'curl' => 'installer.php_modules.curl',
|
||||||
|
'pdo_mysql' => 'installer.php_modules.mysql',
|
||||||
|
'gd' => 'installer.php_modules.gd'
|
||||||
|
];
|
||||||
|
$availableModules = [];
|
||||||
|
|
||||||
|
foreach ($requiredModules as $key => $langString)
|
||||||
|
{
|
||||||
|
$availableModules[$key] = extension_loaded($key);
|
||||||
|
if (!$availableModules[$key])
|
||||||
|
{
|
||||||
|
$canContinue = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$uploadLimit = MiscHelper::convertToBytes(ini_get('upload_max_filesize'));
|
||||||
|
$postMaxSize = MiscHelper::convertToBytes(ini_get('post_max_size'));
|
||||||
|
|
||||||
|
$recommendedMinimum = 4 * 1024 * 1024;
|
||||||
|
|
||||||
|
return view('install.check', [
|
||||||
|
'available_modules' => $availableModules,
|
||||||
|
'can_continue' => $canContinue,
|
||||||
|
'php_is_valid' => $phpIsValid,
|
||||||
|
'php_version_current' => $runtimeVersion,
|
||||||
|
'php_version_required' => $runtimeMinimum,
|
||||||
|
'post_max_size' => ($postMaxSize / 1024 / 1024),
|
||||||
|
'post_max_size_warning' => $postMaxSize < $recommendedMinimum,
|
||||||
|
'recommended_minimum_upload' => ($recommendedMinimum / 1024 / 1024),
|
||||||
|
'upload_limit' => ($uploadLimit / 1024 / 1024),
|
||||||
|
'upload_limit_warning' => $uploadLimit < $recommendedMinimum,
|
||||||
|
'required_modules' => $requiredModules
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function database(Request $request)
|
||||||
|
{
|
||||||
|
// Validate we're at the required stage
|
||||||
|
$stage = 2;
|
||||||
|
if (intval($request->session()->get('install_stage')) < $stage)
|
||||||
|
{
|
||||||
|
return redirect(route('install.check'));
|
||||||
|
}
|
||||||
|
|
||||||
if ($request->method() == 'POST')
|
if ($request->method() == 'POST')
|
||||||
{
|
{
|
||||||
$baseDirectory = dirname(dirname(dirname(__DIR__)));
|
$baseDirectory = dirname(dirname(dirname(__DIR__)));
|
||||||
@ -92,16 +150,12 @@ class InstallController extends Controller
|
|||||||
|
|
||||||
Artisan::call('cache:clear');
|
Artisan::call('cache:clear');
|
||||||
Artisan::call('migrate', ['--force' => true]);
|
Artisan::call('migrate', ['--force' => true]);
|
||||||
Artisan::call('db:seed', ['--force' => true]);
|
|
||||||
|
|
||||||
$versionNumber = UserConfig::getOrCreateModel('app_version');
|
$versionNumber = UserConfig::getOrCreateModel('app_version');
|
||||||
$versionNumber->value = config('app.version');
|
$versionNumber->value = config('app.version');
|
||||||
$versionNumber->save();
|
$versionNumber->save();
|
||||||
|
|
||||||
// Default settings
|
$request->session()->set('install_stage', 3);
|
||||||
$this->setConfigurationForNewSystems();
|
|
||||||
|
|
||||||
$request->session()->put('install_stage', 2);
|
|
||||||
return redirect(route('install.administrator'));
|
return redirect(route('install.administrator'));
|
||||||
}
|
}
|
||||||
catch (\Exception $ex)
|
catch (\Exception $ex)
|
||||||
@ -155,47 +209,4 @@ class InstallController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setDefaultAnonymousPermission($section, $permission)
|
|
||||||
{
|
|
||||||
$permission = Permission::where([
|
|
||||||
['section', $section],
|
|
||||||
['description', $permission]
|
|
||||||
])->first();
|
|
||||||
|
|
||||||
if (is_null($permission))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$adap = new AlbumDefaultAnonymousPermission();
|
|
||||||
$adap->permission_id = $permission->id;
|
|
||||||
$adap->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setConfigurationForNewSystems()
|
|
||||||
{
|
|
||||||
/** @var Configuration $socialFeeds */
|
|
||||||
$socialFeeds = UserConfig::getOrCreateModel('social_user_feeds');
|
|
||||||
$socialFeeds->value = true;
|
|
||||||
$socialFeeds->save();
|
|
||||||
|
|
||||||
/** @var Configuration $socialProfiles */
|
|
||||||
$socialProfiles = UserConfig::getOrCreateModel('social_user_profiles');
|
|
||||||
$socialProfiles->value = true;
|
|
||||||
$socialProfiles->save();
|
|
||||||
|
|
||||||
/** @var Configuration $photoComments */
|
|
||||||
$photoComments = UserConfig::getOrCreateModel('allow_photo_comments');
|
|
||||||
$photoComments->value = true;
|
|
||||||
$photoComments->save();
|
|
||||||
|
|
||||||
$defaultPermissions = ['album.list', 'album.view', 'album.post-comment'];
|
|
||||||
foreach ($defaultPermissions as $defaultPermission)
|
|
||||||
{
|
|
||||||
$permissionParts = explode('.', $defaultPermission);
|
|
||||||
|
|
||||||
$this->setDefaultAnonymousPermission($permissionParts[0], $permissionParts[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -5,7 +5,6 @@ namespace App\Http\Middleware;
|
|||||||
use App\DataMigration;
|
use App\DataMigration;
|
||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Helpers\MiscHelper;
|
use App\Helpers\MiscHelper;
|
||||||
use App\Helpers\PermissionsHelper;
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -14,6 +13,9 @@ use Illuminate\Support\Facades\Log;
|
|||||||
|
|
||||||
class AppInstallation
|
class AppInstallation
|
||||||
{
|
{
|
||||||
|
private $baseDirectory;
|
||||||
|
private $environmentFilePath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The application instance.
|
* The application instance.
|
||||||
*
|
*
|
||||||
@ -30,6 +32,8 @@ class AppInstallation
|
|||||||
public function __construct(Application $app)
|
public function __construct(Application $app)
|
||||||
{
|
{
|
||||||
$this->app = $app;
|
$this->app = $app;
|
||||||
|
$this->baseDirectory = dirname(dirname(dirname(__DIR__)));
|
||||||
|
$this->environmentFilePath = sprintf('%s/.env', $this->baseDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(Request $request, Closure $next)
|
public function handle(Request $request, Closure $next)
|
||||||
@ -46,14 +50,6 @@ class AppInstallation
|
|||||||
// See if the successful flag has been written to the .env file
|
// See if the successful flag has been written to the .env file
|
||||||
$isAppInstalled = MiscHelper::getEnvironmentSetting('APP_INSTALLED');
|
$isAppInstalled = MiscHelper::getEnvironmentSetting('APP_INSTALLED');
|
||||||
|
|
||||||
// See if the vendors are out-of-date
|
|
||||||
if ($this->isVendorUpdateRequired())
|
|
||||||
{
|
|
||||||
return $isAppInstalled
|
|
||||||
? redirect('/update')
|
|
||||||
: redirect('/install');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($request->is('install/*'))
|
if ($request->is('install/*'))
|
||||||
{
|
{
|
||||||
// Already in the installer
|
// Already in the installer
|
||||||
@ -69,40 +65,26 @@ class AppInstallation
|
|||||||
if ($isAppInstalled)
|
if ($isAppInstalled)
|
||||||
{
|
{
|
||||||
// See if an update is necessary
|
// See if an update is necessary
|
||||||
if ($this->updateDatabaseIfRequired())
|
$this->updateDatabaseIfRequired();
|
||||||
{
|
|
||||||
return redirect($request->fullUrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
// App is configured, continue on
|
// App is configured, continue on
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect(route('install.database'));
|
return redirect(route('install.check'));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateAppKey()
|
private function generateAppKey()
|
||||||
{
|
{
|
||||||
// Generate an application key and store to the .env file
|
// Generate an application key and store to the .env file
|
||||||
if (!file_exists(MiscHelper::getEnvironmentFilePath()))
|
if (!file_exists($this->environmentFilePath))
|
||||||
{
|
{
|
||||||
$key = MiscHelper::randomString(32);
|
$key = MiscHelper::randomString(32);
|
||||||
file_put_contents(MiscHelper::getEnvironmentFilePath(), sprintf('APP_KEY=%s', $key) . PHP_EOL);
|
file_put_contents($this->environmentFilePath, sprintf('APP_KEY=%s', $key) . PHP_EOL);
|
||||||
app('config')->set(['app' => ['key' => $key]]);
|
app('config')->set(['app' => ['key' => $key]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function isVendorUpdateRequired()
|
|
||||||
{
|
|
||||||
$vendorsVersionFilename = $this->app->basePath('vendor/version.txt');
|
|
||||||
if (!file_exists($vendorsVersionFilename))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return trim(file_get_contents($vendorsVersionFilename)) != trim(config('app.version'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function updateDatabaseIfRequired()
|
private function updateDatabaseIfRequired()
|
||||||
{
|
{
|
||||||
$versionNumber = UserConfig::getOrCreateModel('app_version');
|
$versionNumber = UserConfig::getOrCreateModel('app_version');
|
||||||
@ -112,9 +94,7 @@ class AppInstallation
|
|||||||
{
|
{
|
||||||
Log::info('Upgrading database', ['new_version' => $appVersionNumber]);
|
Log::info('Upgrading database', ['new_version' => $appVersionNumber]);
|
||||||
|
|
||||||
Artisan::call('config:cache');
|
|
||||||
Artisan::call('cache:clear');
|
Artisan::call('cache:clear');
|
||||||
Artisan::call('view:clear');
|
|
||||||
Artisan::call('migrate', ['--force' => true]);
|
Artisan::call('migrate', ['--force' => true]);
|
||||||
Artisan::call('db:seed', ['--force' => true]);
|
Artisan::call('db:seed', ['--force' => true]);
|
||||||
|
|
||||||
@ -151,14 +131,6 @@ class AppInstallation
|
|||||||
// Save the new version number
|
// Save the new version number
|
||||||
$versionNumber->value = $appVersionNumber;
|
$versionNumber->value = $appVersionNumber;
|
||||||
$versionNumber->save();
|
$versionNumber->save();
|
||||||
|
|
||||||
// Rebuild the permissions cache
|
|
||||||
$helper = new PermissionsHelper();
|
|
||||||
$helper->rebuildCache();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,7 +19,6 @@ class CheckMaxPostSizeExceeded
|
|||||||
protected $exclude = [
|
protected $exclude = [
|
||||||
'/admin/photos/analyse/*',
|
'/admin/photos/analyse/*',
|
||||||
'/admin/photos/flip/*',
|
'/admin/photos/flip/*',
|
||||||
'/admin/photos/reanalyse/*',
|
|
||||||
'/admin/photos/regenerate-thumbnails/*',
|
'/admin/photos/regenerate-thumbnails/*',
|
||||||
'/admin/photos/rotate/*'
|
'/admin/photos/rotate/*'
|
||||||
];
|
];
|
||||||
|
@ -6,7 +6,6 @@ use App\Album;
|
|||||||
use App\Facade\Theme;
|
use App\Facade\Theme;
|
||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\Helpers\DbHelper;
|
use App\Helpers\DbHelper;
|
||||||
use App\Label;
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Contracts\Encryption\DecryptException;
|
use Illuminate\Contracts\Encryption\DecryptException;
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
@ -23,10 +22,73 @@ class GlobalConfiguration
|
|||||||
*/
|
*/
|
||||||
protected $app;
|
protected $app;
|
||||||
|
|
||||||
public static function updateMailConfig()
|
/**
|
||||||
|
* Create a new middleware instance.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Foundation\Application $app
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Application $app)
|
||||||
|
{
|
||||||
|
$this->app = $app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Request $request, Closure $next)
|
||||||
|
{
|
||||||
|
// We can always add the version number
|
||||||
|
$this->addVersionNumberToView();
|
||||||
|
|
||||||
|
// If running the installer, chances are our database isn't running yet
|
||||||
|
if ($request->is('install/*'))
|
||||||
|
{
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When running migrations, CLI tasks or the installer, don't need to add things to the view
|
||||||
|
if (php_sapi_name() != 'cli')
|
||||||
|
{
|
||||||
|
$this->addThemeInfoToView();
|
||||||
|
$this->addAlbumsToView();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default mail configuration as per user's requirements
|
||||||
|
$this->updateMailConfig();
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addAlbumsToView()
|
||||||
|
{
|
||||||
|
$albums = DbHelper::getAlbumsForCurrentUser_NonPaged()->get();
|
||||||
|
View::share('albums', $albums);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addThemeInfoToView()
|
||||||
|
{
|
||||||
|
$themeInfo = Theme::info();
|
||||||
|
|
||||||
|
// Add each theme info element to the view - prefixing with theme_
|
||||||
|
// e.g. $themeInfo['name'] becomes $theme_name in the view
|
||||||
|
foreach ($themeInfo as $key => $value)
|
||||||
|
{
|
||||||
|
View::share('theme_' . $key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also add a theme_url key
|
||||||
|
View::share('theme_url', sprintf('themes/%s', Theme::current()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addVersionNumberToView()
|
||||||
|
{
|
||||||
|
$version = config('app.version');
|
||||||
|
View::share('app_version', $version);
|
||||||
|
View::share('app_version_url', $version);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateMailConfig()
|
||||||
{
|
{
|
||||||
/** @var Mailer $mailer */
|
/** @var Mailer $mailer */
|
||||||
$mailer = app('mailer');
|
$mailer = $this->app->mailer;
|
||||||
$swiftMailer = $mailer->getSwiftMailer();
|
$swiftMailer = $mailer->getSwiftMailer();
|
||||||
|
|
||||||
/** @var \Swift_SmtpTransport $transport */
|
/** @var \Swift_SmtpTransport $transport */
|
||||||
@ -60,152 +122,4 @@ class GlobalConfiguration
|
|||||||
|
|
||||||
$mailer->alwaysFrom(UserConfig::get('sender_address'), UserConfig::get('sender_name'));
|
$mailer->alwaysFrom(UserConfig::get('sender_address'), UserConfig::get('sender_name'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new middleware instance.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Foundation\Application $app
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(Application $app)
|
|
||||||
{
|
|
||||||
$this->app = $app;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(Request $request, Closure $next)
|
|
||||||
{
|
|
||||||
// We can always add the version number
|
|
||||||
$this->addVersionNumberToView();
|
|
||||||
|
|
||||||
// If running the installer, chances are our database isn't running yet
|
|
||||||
if ($request->is('install/*'))
|
|
||||||
{
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When running migrations, CLI tasks or the installer, don't need to add things to the view
|
|
||||||
if (php_sapi_name() != 'cli')
|
|
||||||
{
|
|
||||||
$this->addThemeInfoToView();
|
|
||||||
$this->addAlbumsToView();
|
|
||||||
$this->addLabelsToView();
|
|
||||||
$this->addFlashMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the default mail configuration as per user's requirements
|
|
||||||
$this->updateMailConfig();
|
|
||||||
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addAlbumsToView()
|
|
||||||
{
|
|
||||||
$albums = DbHelper::getAlbumsForCurrentUser_NonPaged()->get();
|
|
||||||
View::share('g_albums', $albums);
|
|
||||||
|
|
||||||
if (UserConfig::get('albums_menu_parents_only'))
|
|
||||||
{
|
|
||||||
// Only show top-level albums in the nav bar
|
|
||||||
$navbarAlbums = $albums->filter(function($value, $key)
|
|
||||||
{
|
|
||||||
return is_null($value->parent_album_id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If not just showing top-level albums, we can show all
|
|
||||||
$navbarAlbums = $albums;
|
|
||||||
}
|
|
||||||
|
|
||||||
$navbarAlbumsToDisplay = UserConfig::get('albums_menu_number_items');
|
|
||||||
View::share('g_albums_menu', $navbarAlbums->take($navbarAlbumsToDisplay));
|
|
||||||
View::share('g_more_albums', $navbarAlbums->count() - $navbarAlbumsToDisplay);
|
|
||||||
|
|
||||||
$albumsToUpload = DbHelper::getAlbumsForCurrentUser_NonPaged('upload-photos')->get();
|
|
||||||
View::share('g_albums_upload', $albumsToUpload);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addFlashMessages()
|
|
||||||
{
|
|
||||||
/** @var Request $request */
|
|
||||||
$request = app('request');
|
|
||||||
if ($request->session()->has('error'))
|
|
||||||
{
|
|
||||||
View::share('error', $request->session()->get('error'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($request->session()->has('success'))
|
|
||||||
{
|
|
||||||
View::share('success', $request->session()->get('success'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addLabelsToView()
|
|
||||||
{
|
|
||||||
$NUMBER_TO_SHOW_IN_NAVBAR = 5;
|
|
||||||
$labelCount = Label::count();
|
|
||||||
$labels = Label::all();
|
|
||||||
$labelsToAdd = [];
|
|
||||||
|
|
||||||
/** @var Label $label */
|
|
||||||
foreach ($labels as $label)
|
|
||||||
{
|
|
||||||
$label->photos_count = $label->photoCount();
|
|
||||||
$labelsToAdd[] = $label;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort my photo count, then name
|
|
||||||
usort($labelsToAdd, function(Label $a, Label $b)
|
|
||||||
{
|
|
||||||
if ($a->photos_count == $b->photos_count)
|
|
||||||
{
|
|
||||||
if ($a->name == $b->name)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else if ($a->name < $b->name)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if ($a->name > $b->name)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ($a->photos_count < $b->photos_count)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if ($a->photos_count > $b->photos_count)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$labelsToAdd = array_slice(array_reverse($labelsToAdd), 0, $NUMBER_TO_SHOW_IN_NAVBAR);
|
|
||||||
|
|
||||||
View::share('g_labels', $labelsToAdd);
|
|
||||||
View::share('g_more_labels', $labelCount - $NUMBER_TO_SHOW_IN_NAVBAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addThemeInfoToView()
|
|
||||||
{
|
|
||||||
$themeInfo = Theme::info();
|
|
||||||
|
|
||||||
// Add each theme info element to the view - prefixing with theme_
|
|
||||||
// e.g. $themeInfo['name'] becomes $theme_name in the view
|
|
||||||
foreach ($themeInfo as $key => $value)
|
|
||||||
{
|
|
||||||
View::share('theme_' . $key, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also add a theme_url key
|
|
||||||
View::share('theme_url', sprintf('themes/%s', Theme::current()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addVersionNumberToView()
|
|
||||||
{
|
|
||||||
$version = config('app.version');
|
|
||||||
View::share('app_version', $version);
|
|
||||||
View::share('app_version_url', $version);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -24,11 +24,10 @@ class SaveSettingsRequest extends FormRequest
|
|||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'albums_menu_number_items' => 'required|integer|min:1',
|
|
||||||
'app_name' => 'required|max:255',
|
'app_name' => 'required|max:255',
|
||||||
'date_format' => 'required',
|
'date_format' => 'required',
|
||||||
'smtp_server' => 'required',
|
'smtp_server' => 'required',
|
||||||
'smtp_port' => 'required|integer'
|
'smtp_port' => 'required:integer'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
|
|
||||||
class SaveUserSettingsRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function authorize()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'required|max:255',
|
|
||||||
'email' => 'required|email|max:255|unique:users,email,' . Auth::user()->id,
|
|
||||||
'profile_alias' => 'sometimes|max:255|unique:users,profile_alias,' . Auth::user()->id
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,7 +28,7 @@ class StoreAlbumRequest extends FormRequest
|
|||||||
case 'POST':
|
case 'POST':
|
||||||
return [
|
return [
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'name' => 'required|album_path_unique|max:255',
|
'name' => 'required|unique:albums|max:255',
|
||||||
'storage_id' => 'required|sometimes'
|
'storage_id' => 'required|sometimes'
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ class StoreAlbumRequest extends FormRequest
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'description' => 'sometimes',
|
'description' => 'sometimes',
|
||||||
'name' => 'required|sometimes|max:255|album_path_unique:' . $albumId,
|
'name' => 'required|sometimes|max:255|unique:albums,name,' . $albumId,
|
||||||
'storage_id' => 'required|sometimes'
|
'storage_id' => 'required|sometimes'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class StoreLabelRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function authorize()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
return ['name' => 'required:max:255|unique:labels,name'];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests;
|
|
||||||
|
|
||||||
use App\ExternalService;
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class StoreServiceRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function authorize()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
switch ($this->method())
|
|
||||||
{
|
|
||||||
case 'POST':
|
|
||||||
$result = [
|
|
||||||
'name' => 'required|unique:external_services|max:255',
|
|
||||||
'service_type' => 'required|max:255',
|
|
||||||
];
|
|
||||||
|
|
||||||
switch ($this->get('service_type'))
|
|
||||||
{
|
|
||||||
case ExternalService::DROPBOX:
|
|
||||||
case ExternalService::FACEBOOK:
|
|
||||||
case ExternalService::GOOGLE:
|
|
||||||
case ExternalService::TWITTER:
|
|
||||||
// Standard OAuth services
|
|
||||||
$result['app_id'] = 'sometimes|required';
|
|
||||||
$result['app_secret'] = 'sometimes|required';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'PATCH':
|
|
||||||
case 'PUT':
|
|
||||||
$serviceId = intval($this->segment(3));
|
|
||||||
$service = ExternalService::find($serviceId);
|
|
||||||
$result = [
|
|
||||||
'name' => 'required|max:255|unique:external_services,name,' . $serviceId
|
|
||||||
];
|
|
||||||
|
|
||||||
switch ($service->service_type)
|
|
||||||
{
|
|
||||||
case ExternalService::DROPBOX:
|
|
||||||
case ExternalService::FACEBOOK:
|
|
||||||
case ExternalService::GOOGLE:
|
|
||||||
case ExternalService::TWITTER:
|
|
||||||
// Standard OAuth services
|
|
||||||
$result['app_id'] = 'sometimes|required';
|
|
||||||
$result['app_secret'] = 'sometimes|required';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -65,16 +65,6 @@ class StoreStorageRequest extends FormRequest
|
|||||||
$result['service_region'] = 'sometimes|required';
|
$result['service_region'] = 'sometimes|required';
|
||||||
$result['container_name'] = 'sometimes|required';
|
$result['container_name'] = 'sometimes|required';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'BackblazeB2Source':
|
|
||||||
$result['access_key'] = 'sometimes|required';
|
|
||||||
$result['secret_key'] = 'sometimes|required';
|
|
||||||
$result['container_name'] = 'sometimes|required';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'DropboxSource':
|
|
||||||
$result['external_service_id'] = 'sometimes|required';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -113,16 +103,6 @@ class StoreStorageRequest extends FormRequest
|
|||||||
$result['service_region'] = 'sometimes|required';
|
$result['service_region'] = 'sometimes|required';
|
||||||
$result['container_name'] = 'sometimes|required';
|
$result['container_name'] = 'sometimes|required';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'BackblazeB2Source':
|
|
||||||
$result['access_key'] = 'sometimes|required';
|
|
||||||
$result['secret_key'] = 'sometimes|required';
|
|
||||||
$result['container_name'] = 'sometimes|required';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'DropboxSource':
|
|
||||||
$result['external_service_id'] = 'sometimes|required';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use App\Helpers\DbHelper;
|
|
||||||
use App\Helpers\MiscHelper;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class Label extends Model
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $fillable = [
|
|
||||||
'name', 'url_alias'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function generateAlias()
|
|
||||||
{
|
|
||||||
$this->url_alias = preg_replace('/[^a-z0-9\-]/', '-', strtolower($this->name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function photoCount()
|
|
||||||
{
|
|
||||||
return $this->photos()->whereIn('album_id', DbHelper::getAlbumIDsForCurrentUser())->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function photos()
|
|
||||||
{
|
|
||||||
return $this->belongsToMany(Photo::class, 'photo_labels');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function thumbnailUrl($thumbnailName)
|
|
||||||
{
|
|
||||||
$photo = $this->photos()
|
|
||||||
->whereIn('album_id', DbHelper::getAlbumIDsForCurrentUser())
|
|
||||||
->inRandomOrder()
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if (!is_null($photo))
|
|
||||||
{
|
|
||||||
return $photo->album->getAlbumSource()->getUrlToPhoto($photo, $thumbnailName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotate standard images
|
|
||||||
$images = [
|
|
||||||
asset('themes/base/images/empty-album-1.jpg'),
|
|
||||||
asset('themes/base/images/empty-album-2.jpg'),
|
|
||||||
asset('themes/base/images/empty-album-3.jpg')
|
|
||||||
];
|
|
||||||
return $images[rand(0, count($images) - 1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function url()
|
|
||||||
{
|
|
||||||
return route('viewLabel', $this->url_alias);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\EmailLog;
|
|
||||||
use Illuminate\Mail\Mailable;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\HtmlString;
|
|
||||||
|
|
||||||
abstract class MailableBase extends Mailable
|
|
||||||
{
|
|
||||||
public function buildEmailLog()
|
|
||||||
{
|
|
||||||
// Build the e-mail
|
|
||||||
$this->build();
|
|
||||||
|
|
||||||
// Get the current user for the ID
|
|
||||||
$currentUser = Auth::user();
|
|
||||||
|
|
||||||
// Build the body so we can use it as a string
|
|
||||||
$bodies = $this->buildView();
|
|
||||||
|
|
||||||
/** @var HtmlString $html */
|
|
||||||
$html = $bodies['html'];
|
|
||||||
|
|
||||||
/** @var HtmlString $text */
|
|
||||||
$text = $bodies['text'];
|
|
||||||
|
|
||||||
return new EmailLog([
|
|
||||||
'sender_user_id' => !is_null($currentUser) ? $currentUser->id : null,
|
|
||||||
'sender_name' => $this->from[0]['name'],
|
|
||||||
'sender_address' => $this->from[0]['address'],
|
|
||||||
'to_addresses' => json_encode($this->to),
|
|
||||||
'cc_addresses' => json_encode($this->cc),
|
|
||||||
'bcc_addresses' => json_encode($this->bcc),
|
|
||||||
'subject' => $this->subject,
|
|
||||||
'body_plain' => $text->toHtml(),
|
|
||||||
'body_html' => $html->toHtml()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Photo;
|
|
||||||
use App\PhotoComment;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class ModeratePhotoComment extends MailableBase
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
private $album;
|
|
||||||
private $comment;
|
|
||||||
private $photo;
|
|
||||||
private $user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(User $user, Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
$this->user = $user;
|
|
||||||
$this->album = $album;
|
|
||||||
$this->photo = $photo;
|
|
||||||
$this->comment = $comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the message.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function build()
|
|
||||||
{
|
|
||||||
$subject = trans('email.moderate_photo_comment_subject', ['album_name' => $this->album->name]);
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->subject($subject)
|
|
||||||
->markdown(Theme::viewName('email.moderate_photo_comment'))
|
|
||||||
->with([
|
|
||||||
'album' => $this->album,
|
|
||||||
'comment' => $this->comment,
|
|
||||||
'photo' => $this->photo,
|
|
||||||
'subject' => $subject,
|
|
||||||
'user' => $this->user
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Photo;
|
|
||||||
use App\PhotoComment;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* E-mail notification to the owner of an album that is sent when a new comment has been approved in their album.
|
|
||||||
* @package App\Mail
|
|
||||||
*/
|
|
||||||
class PhotoCommentApproved extends MailableBase
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
private $album;
|
|
||||||
private $comment;
|
|
||||||
private $photo;
|
|
||||||
private $user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(User $user, Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
$this->user = $user;
|
|
||||||
$this->album = $album;
|
|
||||||
$this->photo = $photo;
|
|
||||||
$this->comment = $comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the message.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function build()
|
|
||||||
{
|
|
||||||
$subject = trans('email.photo_comment_approved_subject', ['album_name' => $this->album->name]);
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->subject($subject)
|
|
||||||
->markdown(Theme::viewName('email.photo_comment_approved'))
|
|
||||||
->with([
|
|
||||||
'album' => $this->album,
|
|
||||||
'comment' => $this->comment,
|
|
||||||
'photo' => $this->photo,
|
|
||||||
'subject' => $subject,
|
|
||||||
'user' => $this->user
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Photo;
|
|
||||||
use App\PhotoComment;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* E-mail notification to the poster of a comment that is sent when it has been approved.
|
|
||||||
* @package App\Mail
|
|
||||||
*/
|
|
||||||
class PhotoCommentApprovedUser extends MailableBase
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
private $album;
|
|
||||||
private $comment;
|
|
||||||
private $photo;
|
|
||||||
private $user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(User $user, Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
$this->user = $user;
|
|
||||||
$this->album = $album;
|
|
||||||
$this->photo = $photo;
|
|
||||||
$this->comment = $comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the message.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function build()
|
|
||||||
{
|
|
||||||
$subject = trans('email.photo_comment_approved_user_subject', ['album_name' => $this->album->name]);
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->subject($subject)
|
|
||||||
->markdown(Theme::viewName('email.photo_comment_approved_user'))
|
|
||||||
->with([
|
|
||||||
'album' => $this->album,
|
|
||||||
'comment' => $this->comment,
|
|
||||||
'photo' => $this->photo,
|
|
||||||
'subject' => $subject,
|
|
||||||
'user' => $this->user
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Photo;
|
|
||||||
use App\PhotoComment;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* E-mail notification to the poster of a comment that is sent when it has been approved.
|
|
||||||
* @package App\Mail
|
|
||||||
*/
|
|
||||||
class PhotoCommentRepliedTo extends MailableBase
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
private $album;
|
|
||||||
private $comment;
|
|
||||||
private $photo;
|
|
||||||
private $user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(User $user, Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
$this->user = $user;
|
|
||||||
$this->album = $album;
|
|
||||||
$this->photo = $photo;
|
|
||||||
$this->comment = $comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the message.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function build()
|
|
||||||
{
|
|
||||||
$subject = trans('email.photo_comment_replied_to_subject', ['album_name' => $this->album->name]);
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->subject($subject)
|
|
||||||
->markdown(Theme::viewName('email.photo_comment_replied_to'))
|
|
||||||
->with([
|
|
||||||
'album' => $this->album,
|
|
||||||
'comment' => $this->comment,
|
|
||||||
'photo' => $this->photo,
|
|
||||||
'subject' => $subject,
|
|
||||||
'user' => $this->user
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Mail\Mailable;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class ResetMyPassword extends MailableBase
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
private $token;
|
|
||||||
private $user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(User $user, $token)
|
|
||||||
{
|
|
||||||
$this->user = $user;
|
|
||||||
$this->token = $token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the message.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function build()
|
|
||||||
{
|
|
||||||
$subject = trans('email.reset_my_password_subject', ['app_name' => UserConfig::get('app_name')]);
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->subject($subject)
|
|
||||||
->markdown(Theme::viewName('email.reset_my_password'))
|
|
||||||
->with([
|
|
||||||
'subject' => $subject,
|
|
||||||
'token' => $this->token,
|
|
||||||
'user' => $this->user
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,9 +8,6 @@ use Illuminate\Bus\Queueable;
|
|||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
/**
|
|
||||||
* NOTE: This does not need converting to a notification. It should always be sent immediately and not queued.
|
|
||||||
*/
|
|
||||||
class TestMailConfig extends Mailable
|
class TestMailConfig extends Mailable
|
||||||
{
|
{
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
@ -40,7 +37,7 @@ class TestMailConfig extends Mailable
|
|||||||
|
|
||||||
return $this
|
return $this
|
||||||
->subject($subject)
|
->subject($subject)
|
||||||
->markdown(Theme::viewName('email.test_email'))
|
->view(Theme::viewName('email.test_email'))
|
||||||
->with(['subject' => $subject]);
|
->with(['subject' => $subject]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@ use App\Facade\Theme;
|
|||||||
use App\Facade\UserConfig;
|
use App\Facade\UserConfig;
|
||||||
use App\User;
|
use App\User;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
|
||||||
class UserActivationRequired extends MailableBase
|
class UserActivationRequired extends Mailable
|
||||||
{
|
{
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
@ -35,7 +37,7 @@ class UserActivationRequired extends MailableBase
|
|||||||
|
|
||||||
return $this
|
return $this
|
||||||
->subject($subject)
|
->subject($subject)
|
||||||
->markdown(Theme::viewName('email.user_activation_required'))
|
->view(Theme::viewName('email.user_activation_required'))
|
||||||
->with([
|
->with([
|
||||||
'subject' => $subject,
|
'subject' => $subject,
|
||||||
'user' => $this->user
|
'user' => $this->user
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class UserChangeEmailRequired extends MailableBase
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
private $user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(User $user)
|
|
||||||
{
|
|
||||||
$this->user = $user;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the message.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function build()
|
|
||||||
{
|
|
||||||
$subject = trans('email.change_email_required_subject', ['app_name' => UserConfig::get('app_name')]);
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->subject($subject)
|
|
||||||
->markdown(Theme::viewName('email.user_change_email_required'))
|
|
||||||
->with([
|
|
||||||
'subject' => $subject,
|
|
||||||
'user' => $this->user
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use App\Facade\Theme;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class UserSelfActivated extends MailableBase
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
private $createdUser;
|
|
||||||
private $user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(User $user, User $createdUser)
|
|
||||||
{
|
|
||||||
$this->user = $user;
|
|
||||||
$this->createdUser = $createdUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the message.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function build()
|
|
||||||
{
|
|
||||||
$subject = trans('email.user_self_activated_subject', ['app_name' => UserConfig::get('app_name')]);
|
|
||||||
|
|
||||||
return $this
|
|
||||||
->subject($subject)
|
|
||||||
->markdown(Theme::viewName('email.user_self_activated'))
|
|
||||||
->with([
|
|
||||||
'subject' => $subject,
|
|
||||||
'user' => $this->user,
|
|
||||||
'created_user' => $this->createdUser
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\ModelObservers;
|
|
||||||
|
|
||||||
use App\Label;
|
|
||||||
|
|
||||||
class LabelObserver
|
|
||||||
{
|
|
||||||
public function creating(Label $label)
|
|
||||||
{
|
|
||||||
$label->generateAlias();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\EmailLog;
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use Illuminate\Mail\Mailable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables a notification to use a Mailable to write to the database.
|
|
||||||
*/
|
|
||||||
trait DatabaseEmailNotification
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the notification's delivery channels.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function via($notifiable)
|
|
||||||
{
|
|
||||||
$drivers = [];
|
|
||||||
|
|
||||||
if (UserConfig::get('queue_emails'))
|
|
||||||
{
|
|
||||||
$drivers[] = QueueEmailDatabaseChannel::class;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$drivers[] = 'mail';
|
|
||||||
$drivers[] = SentEmailDatabaseChannel::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $drivers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the EmailLog entry to write to the database.
|
|
||||||
* @param $notifiable
|
|
||||||
* @return EmailLog
|
|
||||||
*/
|
|
||||||
public function toEmailDatabase($notifiable)
|
|
||||||
{
|
|
||||||
return $this->toMail($notifiable)->buildEmailLog();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function setPropertiesOnMailable(Mailable $mailable, $notifiable)
|
|
||||||
{
|
|
||||||
// Set to and from properties accordingly
|
|
||||||
$mailable->from(UserConfig::get('sender_address'), UserConfig::get('sender_name'));
|
|
||||||
$mailable->to($notifiable->email, $notifiable->name);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\EmailLog;
|
|
||||||
|
|
||||||
class EmailDatabaseWriterChannelBase
|
|
||||||
{
|
|
||||||
protected function writeToTable(EmailLog $logEntry, $shouldQueue = false)
|
|
||||||
{
|
|
||||||
if ($shouldQueue)
|
|
||||||
{
|
|
||||||
$logEntry->queued_at = new \DateTime();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$logEntry->sent_at = new \DateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
$logEntry->save();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Mail\MailableBase;
|
|
||||||
use App\Mail\ModeratePhotoComment as ModeratePhotoCommentMailable;
|
|
||||||
use App\Photo;
|
|
||||||
use App\PhotoComment;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class ModeratePhotoComment extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
use DatabaseEmailNotification;
|
|
||||||
|
|
||||||
private $album;
|
|
||||||
private $comment;
|
|
||||||
private $photo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new notification instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
$this->album = $album;
|
|
||||||
$this->photo = $photo;
|
|
||||||
$this->comment = $comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mail representation of the notification.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @return \Illuminate\Notifications\Messages\MailMessage|MailableBase
|
|
||||||
*/
|
|
||||||
public function toMail($notifiable)
|
|
||||||
{
|
|
||||||
$mailable = new ModeratePhotoCommentMailable($notifiable, $this->album, $this->photo, $this->comment);
|
|
||||||
|
|
||||||
$this->setPropertiesOnMailable($mailable, $notifiable);
|
|
||||||
|
|
||||||
return $mailable;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Mail\MailableBase;
|
|
||||||
use App\Mail\PhotoCommentApproved as PhotoCommentApprovedMailable;
|
|
||||||
use App\Photo;
|
|
||||||
use App\PhotoComment;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class PhotoCommentApproved extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
use DatabaseEmailNotification;
|
|
||||||
|
|
||||||
private $album;
|
|
||||||
private $comment;
|
|
||||||
private $photo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new notification instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
$this->album = $album;
|
|
||||||
$this->photo = $photo;
|
|
||||||
$this->comment = $comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mail representation of the notification.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @return \Illuminate\Notifications\Messages\MailMessage|MailableBase
|
|
||||||
*/
|
|
||||||
public function toMail($notifiable)
|
|
||||||
{
|
|
||||||
$mailable = new PhotoCommentApprovedMailable($notifiable, $this->album, $this->photo, $this->comment);
|
|
||||||
|
|
||||||
$this->setPropertiesOnMailable($mailable, $notifiable);
|
|
||||||
|
|
||||||
return $mailable;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Mail\MailableBase;
|
|
||||||
use App\Mail\PhotoCommentApprovedUser as PhotoCommentApprovedUserMailable;
|
|
||||||
use App\Photo;
|
|
||||||
use App\PhotoComment;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class PhotoCommentApprovedUser extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
use DatabaseEmailNotification;
|
|
||||||
|
|
||||||
private $album;
|
|
||||||
private $comment;
|
|
||||||
private $photo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new notification instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
$this->album = $album;
|
|
||||||
$this->photo = $photo;
|
|
||||||
$this->comment = $comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mail representation of the notification.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @return \Illuminate\Notifications\Messages\MailMessage|MailableBase
|
|
||||||
*/
|
|
||||||
public function toMail($notifiable)
|
|
||||||
{
|
|
||||||
$mailable = new PhotoCommentApprovedUserMailable($notifiable, $this->album, $this->photo, $this->comment);
|
|
||||||
|
|
||||||
$this->setPropertiesOnMailable($mailable, $notifiable);
|
|
||||||
|
|
||||||
return $mailable;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\Album;
|
|
||||||
use App\Mail\MailableBase;
|
|
||||||
use App\Mail\PhotoCommentRepliedTo as PhotoCommentRepliedToMailable;
|
|
||||||
use App\Photo;
|
|
||||||
use App\PhotoComment;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class PhotoCommentRepliedTo extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
use DatabaseEmailNotification;
|
|
||||||
|
|
||||||
private $album;
|
|
||||||
private $comment;
|
|
||||||
private $photo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new notification instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(Album $album, Photo $photo, PhotoComment $comment)
|
|
||||||
{
|
|
||||||
$this->album = $album;
|
|
||||||
$this->photo = $photo;
|
|
||||||
$this->comment = $comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mail representation of the notification.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @return \Illuminate\Notifications\Messages\MailMessage|MailableBase
|
|
||||||
*/
|
|
||||||
public function toMail($notifiable)
|
|
||||||
{
|
|
||||||
$mailable = new PhotoCommentRepliedToMailable($notifiable, $this->album, $this->photo, $this->comment);
|
|
||||||
|
|
||||||
$this->setPropertiesOnMailable($mailable, $notifiable);
|
|
||||||
|
|
||||||
return $mailable;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\EmailLog;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class QueueEmailDatabaseChannel extends EmailDatabaseWriterChannelBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Send the given notification.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @param \Illuminate\Notifications\Notification $notification
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function send($notifiable, Notification $notification)
|
|
||||||
{
|
|
||||||
/** @var EmailLog $logEntry */
|
|
||||||
$logEntry = $notification->toEmailDatabase($notifiable);
|
|
||||||
|
|
||||||
$this->writeToTable($logEntry, true);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\Mail\MailableBase;
|
|
||||||
use App\Mail\ResetMyPassword;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class ResetPassword extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
use DatabaseEmailNotification;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The password reset token.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $token;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a notification instance.
|
|
||||||
*
|
|
||||||
* @param string $token
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct($token)
|
|
||||||
{
|
|
||||||
$this->token = $token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mail representation of the notification.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @return \Illuminate\Notifications\Messages\MailMessage|MailableBase
|
|
||||||
*/
|
|
||||||
public function toMail($notifiable)
|
|
||||||
{
|
|
||||||
$mailable = new ResetMyPassword($notifiable, $this->token);
|
|
||||||
|
|
||||||
$this->setPropertiesOnMailable($mailable, $notifiable);
|
|
||||||
|
|
||||||
return $mailable;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\EmailLog;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class SentEmailDatabaseChannel extends EmailDatabaseWriterChannelBase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Send the given notification.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @param \Illuminate\Notifications\Notification $notification
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function send($notifiable, Notification $notification)
|
|
||||||
{
|
|
||||||
/** @var EmailLog $logEntry */
|
|
||||||
$logEntry = $notification->toEmailDatabase($notifiable);
|
|
||||||
|
|
||||||
$this->writeToTable($logEntry, false);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\Mail\MailableBase;
|
|
||||||
use App\Mail\UserActivationRequired as UserActivationRequiredMailable;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class UserActivationRequired extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
use DatabaseEmailNotification;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new notification instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mail representation of the notification.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @return \Illuminate\Notifications\Messages\MailMessage|MailableBase
|
|
||||||
*/
|
|
||||||
public function toMail($notifiable)
|
|
||||||
{
|
|
||||||
$mailable = new UserActivationRequiredMailable($notifiable);
|
|
||||||
|
|
||||||
$this->setPropertiesOnMailable($mailable, $notifiable);
|
|
||||||
|
|
||||||
return $mailable;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\Mail\MailableBase;
|
|
||||||
use App\Mail\UserChangeEmailRequired as UserChangeEmailRequiredMailable;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class UserChangeEmailRequired extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
use DatabaseEmailNotification;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new notification instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mail representation of the notification.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @return \Illuminate\Notifications\Messages\MailMessage|MailableBase
|
|
||||||
*/
|
|
||||||
public function toMail($notifiable)
|
|
||||||
{
|
|
||||||
$mailable = new UserChangeEmailRequiredMailable($notifiable);
|
|
||||||
|
|
||||||
$this->setPropertiesOnMailable($mailable, $notifiable);
|
|
||||||
|
|
||||||
return $mailable;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\Mail\MailableBase;
|
|
||||||
use App\Mail\UserSelfActivated as UserSelfActivatedMailable;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class UserSelfActivated extends Notification
|
|
||||||
{
|
|
||||||
use Queueable;
|
|
||||||
use DatabaseEmailNotification;
|
|
||||||
|
|
||||||
private $createdUser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new notification instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(User $createdUser)
|
|
||||||
{
|
|
||||||
$this->createdUser = $createdUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the mail representation of the notification.
|
|
||||||
*
|
|
||||||
* @param mixed $notifiable
|
|
||||||
* @return \Illuminate\Notifications\Messages\MailMessage|MailableBase
|
|
||||||
*/
|
|
||||||
public function toMail($notifiable)
|
|
||||||
{
|
|
||||||
$mailable = new UserSelfActivatedMailable($notifiable, $this->createdUser);
|
|
||||||
|
|
||||||
$this->setPropertiesOnMailable($mailable, $notifiable);
|
|
||||||
|
|
||||||
return $mailable;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use App\AlbumSources\IAlbumSource;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
@ -32,10 +31,6 @@ class Photo extends Model
|
|||||||
'width',
|
'width',
|
||||||
'height',
|
'height',
|
||||||
'is_analysed',
|
'is_analysed',
|
||||||
'raw_exif_data',
|
|
||||||
'aperture_fnumber',
|
|
||||||
'iso_number',
|
|
||||||
'shutter_speed',
|
|
||||||
'created_at',
|
'created_at',
|
||||||
'updated_at'
|
'updated_at'
|
||||||
];
|
];
|
||||||
@ -53,72 +48,11 @@ class Photo extends Model
|
|||||||
return $this->belongsTo(Album::class);
|
return $this->belongsTo(Album::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function comments()
|
|
||||||
{
|
|
||||||
return $this->hasMany(PhotoComment::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function exifUrl()
|
|
||||||
{
|
|
||||||
return route('viewExifData', [
|
|
||||||
'albumUrlAlias' => $this->album->url_path,
|
|
||||||
'photoFilename' => $this->storage_file_name
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function labelIDs()
|
|
||||||
{
|
|
||||||
$labelIDs = [];
|
|
||||||
|
|
||||||
foreach ($this->labels()->orderBy('name')->get() as $label)
|
|
||||||
{
|
|
||||||
$labelIDs[] = $label->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode(',', $labelIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function labels()
|
|
||||||
{
|
|
||||||
return $this->belongsToMany(Label::class, 'photo_labels');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function moderateCommentUrl($commentID = -1)
|
|
||||||
{
|
|
||||||
return route('moderatePhotoComment', [
|
|
||||||
'albumUrlAlias' => $this->album->url_path,
|
|
||||||
'photoFilename' => $this->storage_file_name,
|
|
||||||
'commentID' => $commentID
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function postCommentUrl()
|
|
||||||
{
|
|
||||||
return route('postPhotoComment', [
|
|
||||||
'albumUrlAlias' => $this->album->url_path,
|
|
||||||
'photoFilename' => $this->storage_file_name
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function replyToCommentFormUrl($commentID = -1)
|
|
||||||
{
|
|
||||||
return route('replyPhotoComment', [
|
|
||||||
'albumUrlAlias' => $this->album->url_path,
|
|
||||||
'photoFilename' => $this->storage_file_name,
|
|
||||||
'commentID' => $commentID
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function thumbnailUrl($thumbnailName = null, $cacheBust = true)
|
public function thumbnailUrl($thumbnailName = null, $cacheBust = true)
|
||||||
{
|
{
|
||||||
/** @var IAlbumSource $source */
|
$url = $this->album->getAlbumSource()->getUrlToPhoto($this, $thumbnailName);
|
||||||
$source = $this->album->getAlbumSource();
|
|
||||||
$sourceConfiguration = $source->getConfiguration();
|
|
||||||
|
|
||||||
$url = $source->getUrlToPhoto($this, $thumbnailName);
|
if ($cacheBust)
|
||||||
|
|
||||||
// Cache busting doesn't work with S3 signed URLs
|
|
||||||
if ($cacheBust && !$sourceConfiguration->s3_signed_urls)
|
|
||||||
{
|
{
|
||||||
// Append the timestamp of the last update to avoid browser caching
|
// Append the timestamp of the last update to avoid browser caching
|
||||||
$theDate = is_null($this->updated_at) ? $this->created_at : $this->updated_at;
|
$theDate = is_null($this->updated_at) ? $this->created_at : $this->updated_at;
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class PhotoComment extends Model
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $fillable = [
|
|
||||||
'name',
|
|
||||||
'email',
|
|
||||||
'comment'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function authorDisplayName()
|
|
||||||
{
|
|
||||||
return is_null($this->createdBy) ? $this->name : $this->createdBy->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function authorEmail()
|
|
||||||
{
|
|
||||||
return is_null($this->createdBy) ? $this->email : $this->createdBy->email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function children()
|
|
||||||
{
|
|
||||||
return $this->hasMany(PhotoComment::class, 'parent_comment_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createdBy()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class, 'created_user_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function depth()
|
|
||||||
{
|
|
||||||
$depth = 0;
|
|
||||||
$current = $this;
|
|
||||||
|
|
||||||
while (!is_null($current->parent))
|
|
||||||
{
|
|
||||||
$current = $current->parent;
|
|
||||||
$depth++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isApproved()
|
|
||||||
{
|
|
||||||
return (!is_null($this->approved_at) && is_null($this->rejected_at));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isModerated()
|
|
||||||
{
|
|
||||||
return (!is_null($this->approved_at) || !is_null($this->rejected_at));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isRejected()
|
|
||||||
{
|
|
||||||
return (!is_null($this->rejected_at) && is_null($this->approved_at));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function parent()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(PhotoComment::class, 'parent_comment_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function photo()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(Photo::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function textAsHtml()
|
|
||||||
{
|
|
||||||
$start = '<p>';
|
|
||||||
$end = '</p>';
|
|
||||||
$isHtml = (
|
|
||||||
strlen($this->comment) > (strlen($start) + strlen($end)) && // text contains both our start + end string
|
|
||||||
strtolower(substr($this->comment, 0, strlen($start))) == strtolower($start) && // text starts with our start string
|
|
||||||
strtolower(substr($this->comment, strlen($this->comment) - strlen($end))) == strtolower($end) // text ends with our end string
|
|
||||||
);
|
|
||||||
|
|
||||||
return $isHtml ? $this->comment : sprintf('<p>%s</p>', $this->comment);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,9 +3,7 @@
|
|||||||
namespace App\Policies;
|
namespace App\Policies;
|
||||||
|
|
||||||
use App\Album;
|
use App\Album;
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\Group;
|
use App\Group;
|
||||||
use App\Helpers\PermissionsHelper;
|
|
||||||
use App\Permission;
|
use App\Permission;
|
||||||
use App\User;
|
use App\User;
|
||||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||||
@ -47,7 +45,13 @@ class AlbumPolicy
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->userHasPermission($user, $album, 'change-photo-metadata');
|
// Get the edit permission
|
||||||
|
$permission = Permission::where([
|
||||||
|
'section' => 'album',
|
||||||
|
'description' => 'change-photo-metadata'
|
||||||
|
])->first();
|
||||||
|
|
||||||
|
return $this->userHasPermission($user, $album, $permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(User $user, Album $album)
|
public function delete(User $user, Album $album)
|
||||||
@ -58,7 +62,13 @@ class AlbumPolicy
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->userHasPermission($user, $album, 'delete');
|
// Get the edit permission
|
||||||
|
$permission = Permission::where([
|
||||||
|
'section' => 'album',
|
||||||
|
'description' => 'delete'
|
||||||
|
])->first();
|
||||||
|
|
||||||
|
return $this->userHasPermission($user, $album, $permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deletePhotos(User $user, Album $album)
|
public function deletePhotos(User $user, Album $album)
|
||||||
@ -69,7 +79,13 @@ class AlbumPolicy
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->userHasPermission($user, $album, 'delete-photos');
|
// 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)
|
public function edit(User $user, Album $album)
|
||||||
@ -80,7 +96,13 @@ class AlbumPolicy
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->userHasPermission($user, $album, 'edit');
|
// Get the edit permission
|
||||||
|
$permission = Permission::where([
|
||||||
|
'section' => 'album',
|
||||||
|
'description' => 'edit'
|
||||||
|
])->first();
|
||||||
|
|
||||||
|
return $this->userHasPermission($user, $album, $permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function manipulatePhotos(User $user, Album $album)
|
public function manipulatePhotos(User $user, Album $album)
|
||||||
@ -91,35 +113,13 @@ class AlbumPolicy
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->userHasPermission($user, $album, 'manipulate-photos');
|
// Get the edit permission
|
||||||
}
|
$permission = Permission::where([
|
||||||
|
'section' => 'album',
|
||||||
|
'description' => 'manipulate-photos'
|
||||||
|
])->first();
|
||||||
|
|
||||||
public function moderateComments(User $user, Album $album)
|
return $this->userHasPermission($user, $album, $permission);
|
||||||
{
|
|
||||||
if ($user->id == $album->user_id)
|
|
||||||
{
|
|
||||||
// The album's owner and can do everything
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->userHasPermission($user, $album, 'moderate-comments');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function postComment(User $user, Album $album)
|
|
||||||
{
|
|
||||||
if ($user->id == $album->user_id)
|
|
||||||
{
|
|
||||||
// The album's owner and can do everything
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't allow comments to be posted if anonymous user, and anonymous comments disabled
|
|
||||||
if ($user->isAnonymous() && !UserConfig::get('allow_photo_comments_anonymous'))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->userHasPermission($user, $album, 'post-comment');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uploadPhotos(User $user, Album $album)
|
public function uploadPhotos(User $user, Album $album)
|
||||||
@ -130,7 +130,13 @@ class AlbumPolicy
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->userHasPermission($user, $album, 'upload-photos');
|
// 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)
|
public function view(User $user, Album $album)
|
||||||
@ -141,12 +147,56 @@ class AlbumPolicy
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->userHasPermission($user, $album, 'view');
|
// Get the edit permission
|
||||||
|
$permission = Permission::where([
|
||||||
|
'section' => 'album',
|
||||||
|
'description' => 'view'
|
||||||
|
])->first();
|
||||||
|
|
||||||
|
return $this->userHasPermission($user, $album, $permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function userHasPermission(User $user, Album $album, $permission)
|
private function userHasPermission(User $user, Album $album, Permission $permission)
|
||||||
{
|
{
|
||||||
$helper = new PermissionsHelper();
|
if ($user->isAnonymous())
|
||||||
return $helper->userCan_Album($album, $user, $permission);
|
{
|
||||||
|
$query = Album::query()->join('album_anonymous_permissions', 'album_anonymous_permissions.album_id', '=', 'albums.id')
|
||||||
|
->join('permissions', 'permissions.id', '=', 'album_anonymous_permissions.permission_id')
|
||||||
|
->where([
|
||||||
|
['albums.id', $album->id],
|
||||||
|
['permissions.id', $permission->id]
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $query->count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any of the user's groups are granted the permission
|
||||||
|
/** @var Group $group */
|
||||||
|
foreach ($user->groups as $group)
|
||||||
|
{
|
||||||
|
$groupPermission = $album->groupPermissions()->where([
|
||||||
|
'group_id' => $group->id,
|
||||||
|
'permission_id' => $permission->id
|
||||||
|
])->first();
|
||||||
|
|
||||||
|
if (!is_null($groupPermission))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user is directly granted the permission
|
||||||
|
$userPermission = $album->userPermissions()->where([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'permission_id' => $permission->id
|
||||||
|
])->first();
|
||||||
|
|
||||||
|
if (!is_null($userPermission))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nope, no permission
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,37 +61,4 @@ class PhotoPolicy
|
|||||||
|
|
||||||
return $user->can('manipulate-photos', $photo->album);
|
return $user->can('manipulate-photos', $photo->album);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function moderateComments(User $user, Photo $photo)
|
|
||||||
{
|
|
||||||
if ($user->id == $photo->user_id)
|
|
||||||
{
|
|
||||||
// The photo's owner can do everything
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user->can('moderate-comments', $photo->album);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function postComment(User $user, Photo $photo)
|
|
||||||
{
|
|
||||||
if ($user->id == $photo->user_id)
|
|
||||||
{
|
|
||||||
// The photo's owner can do everything
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user->can('post-comment', $photo->album);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function view(User $user, Photo $photo)
|
|
||||||
{
|
|
||||||
if ($user->id == $photo->user_id)
|
|
||||||
{
|
|
||||||
// The photo's owner can do everything
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user->can('view', $photo->album);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Policies;
|
|
||||||
|
|
||||||
use App\Facade\UserConfig;
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
|
||||||
|
|
||||||
class UserPolicy
|
|
||||||
{
|
|
||||||
use HandlesAuthorization;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new policy instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
public function before($user, $ability)
|
|
||||||
{
|
|
||||||
if (!UserConfig::get('social_user_profiles'))
|
|
||||||
{
|
|
||||||
// Social profiles not enabled
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function view(User $user, User $userBeingAccessed)
|
|
||||||
{
|
|
||||||
return $userBeingAccessed->enable_profile_page;
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,9 +8,7 @@ use App\Helpers\ImageHelper;
|
|||||||
use App\Helpers\MiscHelper;
|
use App\Helpers\MiscHelper;
|
||||||
use App\Helpers\ThemeHelper;
|
use App\Helpers\ThemeHelper;
|
||||||
use App\Helpers\ValidationHelper;
|
use App\Helpers\ValidationHelper;
|
||||||
use App\Label;
|
|
||||||
use App\ModelObservers\AlbumObserver;
|
use App\ModelObservers\AlbumObserver;
|
||||||
use App\ModelObservers\LabelObserver;
|
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Mail\Mailer;
|
use Illuminate\Mail\Mailer;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
@ -38,10 +36,6 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
return $themeHelper;
|
return $themeHelper;
|
||||||
});
|
});
|
||||||
$this->app->singleton('misc', function ($app)
|
|
||||||
{
|
|
||||||
return new MiscHelper();
|
|
||||||
});
|
|
||||||
$this->app->singleton('user_config', function ($app)
|
$this->app->singleton('user_config', function ($app)
|
||||||
{
|
{
|
||||||
return new ConfigHelper();
|
return new ConfigHelper();
|
||||||
@ -50,11 +44,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'));
|
||||||
Validator::extend('album_path_unique', (ValidationHelper::class . '@albumPathUnique'));
|
|
||||||
|
|
||||||
// Model observers
|
// Model observers
|
||||||
Album::observe(AlbumObserver::class);
|
Album::observe(AlbumObserver::class);
|
||||||
Label::observe(LabelObserver::class);
|
|
||||||
|
|
||||||
// Configure our default pager
|
// Configure our default pager
|
||||||
if (MiscHelper::isAppInstalled())
|
if (MiscHelper::isAppInstalled())
|
||||||
|
@ -9,10 +9,10 @@ use App\Permission;
|
|||||||
use App\Photo;
|
use App\Photo;
|
||||||
use App\Policies\AlbumPolicy;
|
use App\Policies\AlbumPolicy;
|
||||||
use App\Policies\PhotoPolicy;
|
use App\Policies\PhotoPolicy;
|
||||||
use App\Policies\UserPolicy;
|
|
||||||
use App\User;
|
use App\User;
|
||||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
use function GuzzleHttp\Psr7\mimetype_from_extension;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||||
|
|
||||||
class AuthServiceProvider extends ServiceProvider
|
class AuthServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@ -28,8 +28,7 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
protected $policies = [
|
protected $policies = [
|
||||||
Album::class => AlbumPolicy::class,
|
Album::class => AlbumPolicy::class,
|
||||||
Photo::class => PhotoPolicy::class,
|
Photo::class => PhotoPolicy::class
|
||||||
User::class => UserPolicy::class
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,22 +52,10 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
return $this->userHasAdminPermission($user, 'manage-albums');
|
return $this->userHasAdminPermission($user, 'manage-albums');
|
||||||
});
|
});
|
||||||
Gate::define('admin:manage-comments', function ($user)
|
|
||||||
{
|
|
||||||
return $this->userHasAdminPermission($user, 'manage-comments');
|
|
||||||
});
|
|
||||||
Gate::define('admin:manage-groups', function ($user)
|
Gate::define('admin:manage-groups', function ($user)
|
||||||
{
|
{
|
||||||
return $this->userHasAdminPermission($user, 'manage-groups');
|
return $this->userHasAdminPermission($user, 'manage-groups');
|
||||||
});
|
});
|
||||||
Gate::define('admin:manage-labels', function ($user)
|
|
||||||
{
|
|
||||||
return $this->userHasAdminPermission($user, 'manage-labels');
|
|
||||||
});
|
|
||||||
Gate::define('admin:manage-services', function ($user)
|
|
||||||
{
|
|
||||||
return $this->userHasAdminPermission($user, 'manage-services');
|
|
||||||
});
|
|
||||||
Gate::define('admin:manage-storage', function ($user)
|
Gate::define('admin:manage-storage', function ($user)
|
||||||
{
|
{
|
||||||
return $this->userHasAdminPermission($user, 'manage-storage');
|
return $this->userHasAdminPermission($user, 'manage-storage');
|
||||||
@ -87,20 +74,6 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
return ($user->id == $photo->user_id);
|
return ($user->id == $photo->user_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
Gate::define('photo.quick_upload', function($user)
|
|
||||||
{
|
|
||||||
$can = true;
|
|
||||||
$can &= $this->userHasAdminPermission($user, 'access');
|
|
||||||
$can &= $this->userHasAdminPermission($user, 'manage-albums');
|
|
||||||
|
|
||||||
return $can;
|
|
||||||
});
|
|
||||||
|
|
||||||
Gate::define('statistics.public-access', function ($user)
|
|
||||||
{
|
|
||||||
return UserConfig::get('public_statistics') || !$user->isAnonymous();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function userHasAdminPermission(User $user, $permissionDescription)
|
private function userHasAdminPermission(User $user, $permissionDescription)
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class QueueItem extends Model
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $fillable = [
|
|
||||||
'batch_reference',
|
|
||||||
'action_type',
|
|
||||||
'album_id',
|
|
||||||
'photo_id',
|
|
||||||
'queued_at',
|
|
||||||
'user_id'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function album()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(Album::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function photo()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(Photo::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function user()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,400 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
use App\Exceptions\BackblazeRetryException;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
class BackblazeB2Service
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The individual URL for the account to use to access the API
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $accountApiUrl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ID of the account in Backblaze B2.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $accountID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The base URL for public access to the account's files.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $downloadUrl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Authorisation header for authenticating to the API.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $authHeader;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Authorisation token for accessing the API post-authentication.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $authToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ID of the bucket.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $bucketId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of the bucket.
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
private $bucketType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration related to the Backblaze B2 service.
|
|
||||||
* @var \Illuminate\Config\Repository|mixed
|
|
||||||
*/
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current file upload token.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $uploadAuthToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current upload URL.
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $uploadUrl;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->config = config('services.backblaze_b2');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function authorizeAccount($force = false)
|
|
||||||
{
|
|
||||||
if (empty($this->authToken) || $force)
|
|
||||||
{
|
|
||||||
$result = $this->sendRequest($this->config['auth_url']);
|
|
||||||
|
|
||||||
if (!isset($result->authorizationToken))
|
|
||||||
{
|
|
||||||
throw new \Exception('Authorisation to Backblaze failed. Is the API key correct?');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->authToken = $result->authorizationToken;
|
|
||||||
$this->accountApiUrl = $result->apiUrl;
|
|
||||||
$this->accountID = $result->accountId;
|
|
||||||
$this->downloadUrl = $result->downloadUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteFile($fileID, $fileName)
|
|
||||||
{
|
|
||||||
$this->sendRequest(
|
|
||||||
sprintf('%s/b2api/v2/b2_delete_file_version', $this->accountApiUrl),
|
|
||||||
'POST',
|
|
||||||
[
|
|
||||||
'fileId' => $fileID,
|
|
||||||
'fileName' => $fileName
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function downloadFile($fileID)
|
|
||||||
{
|
|
||||||
return $this->sendRequest(
|
|
||||||
sprintf('%s/b2api/v2/b2_download_file_by_id?fileId=%s', $this->accountApiUrl, urlencode($fileID)),
|
|
||||||
'GET',
|
|
||||||
null,
|
|
||||||
[
|
|
||||||
'http_headers' => [
|
|
||||||
sprintf('Authorization: %s', $this->authToken)
|
|
||||||
],
|
|
||||||
'response_body_is_json' => false
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBucketType()
|
|
||||||
{
|
|
||||||
return $this->bucketType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDownloadAuthToken()
|
|
||||||
{
|
|
||||||
$result = $this->sendRequest(
|
|
||||||
sprintf('%s/b2api/v2/b2_get_download_authorization', $this->accountApiUrl),
|
|
||||||
'POST',
|
|
||||||
[
|
|
||||||
'bucketId' => $this->bucketId,
|
|
||||||
'validDurationInSeconds' => intval($this->config['download_token_lifetime']),
|
|
||||||
'fileNamePrefix' => ''
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
return $result->authorizationToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDownloadUrl()
|
|
||||||
{
|
|
||||||
return $this->downloadUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setBucketName($bucketName)
|
|
||||||
{
|
|
||||||
$bucketDetails = $this->getBucketDetailsFromName($bucketName);
|
|
||||||
|
|
||||||
$this->bucketId = $bucketDetails->bucketId;
|
|
||||||
$this->bucketType = $bucketDetails->bucketType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setCredentials($applicationKeyID, $applicationKey)
|
|
||||||
{
|
|
||||||
$this->authHeader = sprintf('%s:%s', $applicationKeyID, $applicationKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uploadFile($pathToFileToUpload, $pathToStorage)
|
|
||||||
{
|
|
||||||
// Get a URL to upload our file to
|
|
||||||
list($uploadUrl, $authorizationToken) = $this->getUploadUrl();
|
|
||||||
|
|
||||||
if (empty($uploadUrl) || empty($authorizationToken))
|
|
||||||
{
|
|
||||||
throw new \Exception('No upload URL/authorization token returned from Backblaze B2.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$exponentialBackoff = 1;
|
|
||||||
$numberOfRetries = 5; // this effectively gives us 31 seconds of retries (1+2+4+8+16)
|
|
||||||
$numberOfTimesTried = 0;
|
|
||||||
|
|
||||||
while ($numberOfTimesTried < $numberOfRetries)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return $this->uploadFileReal($pathToFileToUpload, $pathToStorage, $uploadUrl, $authorizationToken);
|
|
||||||
}
|
|
||||||
catch (BackblazeRetryException $ex)
|
|
||||||
{
|
|
||||||
sleep($exponentialBackoff);
|
|
||||||
|
|
||||||
// Get a new upload token
|
|
||||||
$this->uploadAuthToken = null;
|
|
||||||
$this->uploadUrl = null;
|
|
||||||
|
|
||||||
list($uploadUrl, $authorizationToken) = $this->getUploadUrl();
|
|
||||||
|
|
||||||
// Keep backing off
|
|
||||||
$exponentialBackoff *= $exponentialBackoff;
|
|
||||||
$numberOfTimesTried++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function uploadFileReal($pathToFileToUpload, $pathToStorage, $uploadUrl, $authorizationToken)
|
|
||||||
{
|
|
||||||
$fileSize = filesize($pathToFileToUpload);
|
|
||||||
$handle = fopen($pathToFileToUpload, 'r');
|
|
||||||
$fileContents = fread($handle, $fileSize);
|
|
||||||
fclose($handle);
|
|
||||||
$fileContentsSha1 = sha1_file($pathToFileToUpload);
|
|
||||||
|
|
||||||
$httpHeaders = [
|
|
||||||
sprintf('Authorization: %s', $authorizationToken),
|
|
||||||
'Content-Type: b2/x-auto',
|
|
||||||
sprintf('X-Bz-Content-Sha1: %s', $fileContentsSha1),
|
|
||||||
sprintf('X-Bz-File-Name: %s', urlencode($pathToStorage))
|
|
||||||
];
|
|
||||||
|
|
||||||
$result = $this->sendRequestReal(
|
|
||||||
$uploadUrl,
|
|
||||||
'POST',
|
|
||||||
$fileContents,
|
|
||||||
[
|
|
||||||
'http_headers' => $httpHeaders,
|
|
||||||
'post_body_is_json' => false
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
return $result->fileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getBucketDetailsFromName($bucketName)
|
|
||||||
{
|
|
||||||
$result = $this->sendRequest(
|
|
||||||
sprintf('%s/b2api/v2/b2_list_buckets', $this->accountApiUrl),
|
|
||||||
'POST',
|
|
||||||
[
|
|
||||||
'accountId' => $this->accountID,
|
|
||||||
'bucketName' => $bucketName
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isset($result->buckets) && is_array($result->buckets) && count($result->buckets) >= 1)
|
|
||||||
{
|
|
||||||
return $result->buckets[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \Exception(sprintf('The bucket \'%s\' was not found or your API key does not have access.', $bucketName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getUploadUrl($alwaysGetNewToken = false)
|
|
||||||
{
|
|
||||||
if (is_null($this->uploadAuthToken) || $alwaysGetNewToken)
|
|
||||||
{
|
|
||||||
$result = $this->sendRequest(
|
|
||||||
sprintf('%s/b2api/v2/b2_get_upload_url', $this->accountApiUrl),
|
|
||||||
'POST',
|
|
||||||
['bucketId' => $this->bucketId]
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->uploadAuthToken = $result->authorizationToken;
|
|
||||||
$this->uploadUrl = $result->uploadUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [$this->uploadUrl, $this->uploadAuthToken];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getBasicHttpClient($url, $method = 'GET', array $httpHeaders = [])
|
|
||||||
{
|
|
||||||
$httpHeaders = array_merge(
|
|
||||||
[
|
|
||||||
'Accept: application/json'
|
|
||||||
],
|
|
||||||
$httpHeaders
|
|
||||||
);
|
|
||||||
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
|
|
||||||
switch (strtoupper($method))
|
|
||||||
{
|
|
||||||
case 'GET':
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'POST':
|
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sendRequest($url, $method = 'GET', $postData = null, array $postOptions = [])
|
|
||||||
{
|
|
||||||
$exponentialBackoff = 1;
|
|
||||||
$numberOfRetries = 5; // this effectively gives us 31 seconds of retries (1+2+4+8+16)
|
|
||||||
$numberOfTimesTried = 0;
|
|
||||||
|
|
||||||
while ($numberOfTimesTried < $numberOfRetries)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return $this->sendRequestReal($url, $method, $postData, $postOptions);
|
|
||||||
}
|
|
||||||
catch (BackblazeRetryException $ex)
|
|
||||||
{
|
|
||||||
// Clear the upload token if requested
|
|
||||||
if (isset($postOptions['clear_upload_token_on_retry']) && $postOptions['clear_upload_token_on_retry'])
|
|
||||||
{
|
|
||||||
$this->uploadAuthToken = null;
|
|
||||||
$this->uploadUrl = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep backing off
|
|
||||||
sleep($exponentialBackoff);
|
|
||||||
$exponentialBackoff *= $exponentialBackoff;
|
|
||||||
$numberOfTimesTried++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sendRequestReal($url, $method = 'GET', $postData = null, array $postOptions = [])
|
|
||||||
{
|
|
||||||
$postOptions = array_merge(
|
|
||||||
[
|
|
||||||
'authorization_token' => null,
|
|
||||||
'http_headers' => [],
|
|
||||||
'post_body_is_json' => true,
|
|
||||||
'response_body_is_json' => true
|
|
||||||
],
|
|
||||||
$postOptions
|
|
||||||
);
|
|
||||||
$httpHeaders = $postOptions['http_headers'];
|
|
||||||
|
|
||||||
// Some methods may need to override the authorization token used
|
|
||||||
if (empty($postOptions['authorization_token']))
|
|
||||||
{
|
|
||||||
// No override - work out which auth token to use
|
|
||||||
if (is_null($this->authToken))
|
|
||||||
{
|
|
||||||
// No auth token yet, use username/password
|
|
||||||
$httpHeaders[] = sprintf('Authorization: Basic %s', base64_encode($this->authHeader));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Use the auth token we have
|
|
||||||
$httpHeaders[] = sprintf('Authorization: %s', $this->authToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Override - use the auth token specified
|
|
||||||
$httpHeaders[] = sprintf('Authorization: %s', $postOptions['authorization_token']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$ch = $this->getBasicHttpClient($url, $method, $httpHeaders);
|
|
||||||
|
|
||||||
if (!is_null($postData))
|
|
||||||
{
|
|
||||||
if ($postOptions['post_body_is_json'])
|
|
||||||
{
|
|
||||||
$postData = json_encode($postData);
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info(sprintf('%s: %s', strtoupper($method), $url));
|
|
||||||
Log::debug('HTTP headers:', $httpHeaders);
|
|
||||||
|
|
||||||
// Only log a post body if we have one and it's in JSON format (i.e. not a file upload)
|
|
||||||
if (!is_null($postData) && $postOptions['post_body_is_json'])
|
|
||||||
{
|
|
||||||
Log::debug($postData);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = curl_exec($ch);
|
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
|
|
||||||
Log::info(sprintf('Received HTTP code %d', $httpCode));
|
|
||||||
|
|
||||||
// Only log a result if we have one and it's in JSON format (i.e. not a file download)
|
|
||||||
if (!is_null($result) && $result !== false && $postOptions['response_body_is_json'])
|
|
||||||
{
|
|
||||||
Log::debug($result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// According to the Backblaze B2 Protocol, if we get a 500/503, we should retry the request
|
|
||||||
if ($httpCode == 500 || $httpCode == 503)
|
|
||||||
{
|
|
||||||
throw new BackblazeRetryException(
|
|
||||||
$httpCode,
|
|
||||||
new \Exception(sprintf('Exception from Backblaze B2: %s', $result))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if ($httpCode != 200 && $httpCode != 304)
|
|
||||||
{
|
|
||||||
throw new \Exception(sprintf('Exception from Backblaze B2: %s', $result));
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
return $postOptions['response_body_is_json']
|
|
||||||
? json_decode($result)
|
|
||||||
: $result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,269 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
use App\Exceptions\DropboxRetryException;
|
|
||||||
use App\Storage;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
class DropboxService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $accessToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration related to the Backblaze B2 service.
|
|
||||||
* @var \Illuminate\Config\Repository|mixed
|
|
||||||
*/
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->config = config('services.dropbox');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function authoriseUrl(Storage $storage)
|
|
||||||
{
|
|
||||||
$service = $storage->externalService;
|
|
||||||
$redirectUrl = $this->callbackUrl();
|
|
||||||
|
|
||||||
return sprintf(
|
|
||||||
'%s?client_id=%s&response_type=code&redirect_uri=%s&state=%s',
|
|
||||||
$this->config['authorise_url'],
|
|
||||||
urlencode(decrypt($service->app_id)),
|
|
||||||
urlencode($redirectUrl),
|
|
||||||
urlencode(encrypt($storage->id))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function callbackUrl()
|
|
||||||
{
|
|
||||||
return route('services.authoriseDropbox');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteFile($pathOnStorage)
|
|
||||||
{
|
|
||||||
$dropboxData = ['path' => $pathOnStorage];
|
|
||||||
|
|
||||||
$deleteResult = $this->sendRequest(
|
|
||||||
$this->config['delete_url'],
|
|
||||||
'POST',
|
|
||||||
$dropboxData,
|
|
||||||
[
|
|
||||||
'http_headers' => [
|
|
||||||
'Content-Type: application/json'
|
|
||||||
],
|
|
||||||
'post_body_is_json' => true
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
Log::debug('DropboxService - response to deleteFile.', ['response' => $deleteResult, 'path' => $pathOnStorage]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function downloadFile($pathOnStorage)
|
|
||||||
{
|
|
||||||
$dropboxArgs = ['path' => $pathOnStorage];
|
|
||||||
|
|
||||||
return $this->sendRequest(
|
|
||||||
$this->config['download_url'],
|
|
||||||
'POST',
|
|
||||||
null,
|
|
||||||
[
|
|
||||||
'http_headers' => [
|
|
||||||
sprintf('Dropbox-API-Arg: %s', json_encode($dropboxArgs)),
|
|
||||||
'Content-Type: application/octet-stream'
|
|
||||||
],
|
|
||||||
'post_body_is_json' => false,
|
|
||||||
'response_body_is_json' => false
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleAuthenticationResponse(Request $request, Storage $storage)
|
|
||||||
{
|
|
||||||
$authorisationCode = $request->query('code');
|
|
||||||
|
|
||||||
$storage->access_token = encrypt($this->convertAuthorisationCodeToToken($authorisationCode, $storage));
|
|
||||||
$storage->save();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $accessToken
|
|
||||||
*/
|
|
||||||
public function setAccessToken(string $accessToken)
|
|
||||||
{
|
|
||||||
$this->accessToken = $accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uploadFile($pathToFileToUpload, $pathOnStorage)
|
|
||||||
{
|
|
||||||
$dropboxArgs = [
|
|
||||||
'path' => $pathOnStorage,
|
|
||||||
'mode' => 'overwrite',
|
|
||||||
'mute' => true
|
|
||||||
];
|
|
||||||
|
|
||||||
$shouldRetry = true;
|
|
||||||
while ($shouldRetry)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$uploadResult = $this->sendRequest(
|
|
||||||
$this->config['upload_url'],
|
|
||||||
'POST',
|
|
||||||
file_get_contents($pathToFileToUpload),
|
|
||||||
[
|
|
||||||
'http_headers' => [
|
|
||||||
sprintf('Dropbox-API-Arg: %s', json_encode($dropboxArgs)),
|
|
||||||
'Content-Type: application/octet-stream'
|
|
||||||
],
|
|
||||||
'post_body_is_json' => false
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
$shouldRetry = false;
|
|
||||||
Log::debug('DropboxService - response to uploadFile.', ['response' => $uploadResult, 'path' => $pathOnStorage]);
|
|
||||||
}
|
|
||||||
catch (DropboxRetryException $dre)
|
|
||||||
{
|
|
||||||
// Retry - leave shouldRetry as true
|
|
||||||
Log::debug('DropboxService - Dropbox reported a lock/rate limit and requested to retry');
|
|
||||||
sleep(2);
|
|
||||||
}
|
|
||||||
catch (\Exception $ex)
|
|
||||||
{
|
|
||||||
$shouldRetry = false;
|
|
||||||
Log::debug('DropboxService - exception in uploadFile.', ['exception' => $ex->getMessage()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function convertAuthorisationCodeToToken($authorisationCode, Storage $storage)
|
|
||||||
{
|
|
||||||
$service = $storage->externalService;
|
|
||||||
$credentials = sprintf('%s:%s', decrypt($service->app_id), decrypt($service->app_secret));
|
|
||||||
$redirectUrl = $this->callbackUrl();
|
|
||||||
|
|
||||||
$httpHeaders = [
|
|
||||||
'Accept: application/json',
|
|
||||||
sprintf('Authorization: Basic %s', base64_encode($credentials))
|
|
||||||
];
|
|
||||||
|
|
||||||
$ch = curl_init($this->config['token_url']);
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
|
||||||
'code' => $authorisationCode,
|
|
||||||
'grant_type' => 'authorization_code',
|
|
||||||
'redirect_uri' => $redirectUrl
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = json_decode(curl_exec($ch));
|
|
||||||
if (is_null($response) || $response === false)
|
|
||||||
{
|
|
||||||
throw new \Exception('Unable to read the response from Dropbox');
|
|
||||||
}
|
|
||||||
else if (isset($response->error_description))
|
|
||||||
{
|
|
||||||
throw new \Exception(sprintf('Error from Dropbox: %s', $response->error_description));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $response->access_token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getBasicHttpClient($url, $method = 'GET', array $httpHeaders = [])
|
|
||||||
{
|
|
||||||
$httpHeaders = array_merge(
|
|
||||||
[
|
|
||||||
'Accept: application/json',
|
|
||||||
sprintf('Authorization: Bearer %s', $this->accessToken)
|
|
||||||
],
|
|
||||||
$httpHeaders
|
|
||||||
);
|
|
||||||
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
|
|
||||||
switch (strtoupper($method))
|
|
||||||
{
|
|
||||||
case 'GET':
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'POST':
|
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sendRequest($url, $method = 'GET', $postData = null, array $postOptions = [])
|
|
||||||
{
|
|
||||||
$postOptions = array_merge(
|
|
||||||
[
|
|
||||||
'http_headers' => [],
|
|
||||||
'post_body_is_json' => true,
|
|
||||||
'response_body_is_json' => true
|
|
||||||
],
|
|
||||||
$postOptions
|
|
||||||
);
|
|
||||||
$httpHeaders = $postOptions['http_headers'];
|
|
||||||
|
|
||||||
$ch = $this->getBasicHttpClient($url, $method, $httpHeaders);
|
|
||||||
|
|
||||||
Log::info(sprintf('DropboxService - %s: %s', strtoupper($method), $url));
|
|
||||||
Log::debug('DropboxService - HTTP headers:', $httpHeaders);
|
|
||||||
|
|
||||||
if (!is_null($postData))
|
|
||||||
{
|
|
||||||
if ($postOptions['post_body_is_json'])
|
|
||||||
{
|
|
||||||
// Only log a post body if we have one and it's in JSON format (i.e. not a file upload)
|
|
||||||
Log::debug('DropboxService - Body: ', $postData);
|
|
||||||
$postData = json_encode($postData);
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = curl_exec($ch);
|
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
|
|
||||||
Log::info(sprintf('DropboxService - Received HTTP code %d', $httpCode));
|
|
||||||
|
|
||||||
// Only log a result if we have one and it's in JSON format (i.e. not a file download)
|
|
||||||
if (!is_null($result) && $result !== false && $postOptions['response_body_is_json'])
|
|
||||||
{
|
|
||||||
Log::debug($result);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if ($httpCode != 200 && $httpCode != 304)
|
|
||||||
{
|
|
||||||
if ($httpCode == 429)
|
|
||||||
{
|
|
||||||
throw new DropboxRetryException($httpCode, new \Exception(sprintf('Exception from Dropbox: %s', $result)));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new \Exception(sprintf('Exception from Dropbox: %s', $result));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $postOptions['response_body_is_json']
|
|
||||||
? json_decode($result)
|
|
||||||
: $result;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
curl_close($ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
class GiteaService
|
|
||||||
{
|
|
||||||
private $cacheFile = null;
|
|
||||||
private $config = [];
|
|
||||||
private $currentVersionNumber;
|
|
||||||
|
|
||||||
public function __construct(array $config = null, $currentVersionNumber = null)
|
|
||||||
{
|
|
||||||
// This class is used in the Bootstrapper to fetch release information, therefore
|
|
||||||
// we need to check if the Laravel helper functions are loaded before we use them
|
|
||||||
if (is_null($config) && function_exists('config'))
|
|
||||||
{
|
|
||||||
$this->config = config('services.gitea');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->config = $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($currentVersionNumber) && function_exists('config'))
|
|
||||||
{
|
|
||||||
$this->currentVersionNumber = config('app.version');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->currentVersionNumber = $currentVersionNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (function_exists('storage_path'))
|
|
||||||
{
|
|
||||||
$this->cacheFile = storage_path('app/gitea_cache.txt');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function checkForLatestRelease()
|
|
||||||
{
|
|
||||||
$cacheData = null;
|
|
||||||
|
|
||||||
if ($this->doesCacheExist())
|
|
||||||
{
|
|
||||||
// Get the etag from the cache
|
|
||||||
$cacheData = $this->getCacheData();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Lookup and store the version information
|
|
||||||
$statusCode = -1;
|
|
||||||
$result = $this->getReleasesFromGitea($statusCode);
|
|
||||||
|
|
||||||
if ($statusCode == 200)
|
|
||||||
{
|
|
||||||
$releases = json_decode($result[1]);
|
|
||||||
|
|
||||||
$latestRelease = null;
|
|
||||||
foreach ($releases as $release)
|
|
||||||
{
|
|
||||||
if (is_null($latestRelease) || version_compare($release->tag_name, $latestRelease->tag_name) > 0)
|
|
||||||
{
|
|
||||||
$latestRelease = $release;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$cacheData = $this->setCacheData($latestRelease);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GitHub compatibility
|
|
||||||
$cacheData->html_url = sprintf($this->config['releases_url'], $this->config['repo_owner'], $this->config['repo_name']);
|
|
||||||
|
|
||||||
return $cacheData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSpecificRelease($versionNumber)
|
|
||||||
{
|
|
||||||
$cacheData = null;
|
|
||||||
|
|
||||||
// Lookup and store the version information
|
|
||||||
$statusCode = -1;
|
|
||||||
$result = $this->getReleasesFromGitea($statusCode);
|
|
||||||
|
|
||||||
if ($statusCode == 200)
|
|
||||||
{
|
|
||||||
$releases = json_decode($result[1]);
|
|
||||||
|
|
||||||
$foundRelease = null;
|
|
||||||
foreach ($releases as $release)
|
|
||||||
{
|
|
||||||
if (version_compare($release->tag_name, $versionNumber) === 0)
|
|
||||||
{
|
|
||||||
return $release;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function doesCacheExist()
|
|
||||||
{
|
|
||||||
$exists = file_exists($this->cacheFile);
|
|
||||||
|
|
||||||
if ($exists)
|
|
||||||
{
|
|
||||||
// Check modified time on the file
|
|
||||||
$stat = stat($this->cacheFile);
|
|
||||||
|
|
||||||
$diff = time() - $stat['mtime'];
|
|
||||||
if ($diff > $this->config['cache_time_seconds'])
|
|
||||||
{
|
|
||||||
$exists = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $exists;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getCacheData()
|
|
||||||
{
|
|
||||||
return json_decode(file_get_contents($this->cacheFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getReleasesFromGitea(&$statusCode)
|
|
||||||
{
|
|
||||||
$httpHeaders = [
|
|
||||||
sprintf('User-Agent: aheathershaw/blue-twilight (v%s)', $this->currentVersionNumber)
|
|
||||||
];
|
|
||||||
|
|
||||||
if (isset($this->config['api_key']) && !empty($this->config['api_key']))
|
|
||||||
{
|
|
||||||
$httpHeaders[] = sprintf('Authorization: %s', $this->config['api_key']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$apiUrl = sprintf('%s/repos/%s/%s/releases', $this->config['api_url'], $this->config['repo_owner'], $this->config['repo_name']);
|
|
||||||
|
|
||||||
$ch = curl_init($apiUrl);
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
|
|
||||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
$result = curl_exec($ch);
|
|
||||||
|
|
||||||
if ($result === false)
|
|
||||||
{
|
|
||||||
throw new \Exception(sprintf('Error from Gitea: %s', curl_error($ch)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
|
|
||||||
return explode("\r\n\r\n", $result, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setCacheData($data)
|
|
||||||
{
|
|
||||||
if (!is_null($this->cacheFile))
|
|
||||||
{
|
|
||||||
file_put_contents($this->cacheFile, json_encode(get_object_vars($data)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services;
|
|
||||||
|
|
||||||
class GithubService
|
|
||||||
{
|
|
||||||
private $cacheFile = null;
|
|
||||||
private $config = [];
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->config = config('services.github');
|
|
||||||
$this->cacheFile = storage_path('app/github_cache.txt');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function checkForLatestRelease()
|
|
||||||
{
|
|
||||||
$releaseInfo = [];
|
|
||||||
$etag = '';
|
|
||||||
|
|
||||||
if ($this->doesCacheExist())
|
|
||||||
{
|
|
||||||
// Get the etag from the cache
|
|
||||||
$cacheData = $this->getCacheData();
|
|
||||||
$etag = $cacheData->latest_release->etag;
|
|
||||||
$releaseInfo = $cacheData->latest_release->release_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup and store the version information
|
|
||||||
$statusCode = -1;
|
|
||||||
$result = $this->getLatestReleaseFromGithub($etag, $statusCode);
|
|
||||||
|
|
||||||
if ($statusCode == 200)
|
|
||||||
{
|
|
||||||
// Store the etag (in HTTP headers) for future reference
|
|
||||||
$matches = [];
|
|
||||||
$etag = '';
|
|
||||||
if (preg_match('/^etag: "(.+)"/mi', $result[0], $matches))
|
|
||||||
{
|
|
||||||
$etag = $matches[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
$releaseInfo = json_decode($result[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($etag))
|
|
||||||
{
|
|
||||||
$this->setCacheData([
|
|
||||||
'latest_release' => [
|
|
||||||
'etag' => $etag,
|
|
||||||
'release_info' => $releaseInfo
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $releaseInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function doesCacheExist()
|
|
||||||
{
|
|
||||||
return file_exists($this->cacheFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getCacheData()
|
|
||||||
{
|
|
||||||
return json_decode(file_get_contents($this->cacheFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getLatestReleaseFromGithub($etag = '', &$statusCode)
|
|
||||||
{
|
|
||||||
$httpHeaders = [
|
|
||||||
sprintf('User-Agent: pandy06269/blue-twilight (v%s)', config('app.version'))
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!empty($etag))
|
|
||||||
{
|
|
||||||
$httpHeaders[] = sprintf('If-None-Match: "%s"', $etag);
|
|
||||||
}
|
|
||||||
|
|
||||||
$ch = curl_init($this->config['latest_release_url']);
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
|
|
||||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
$result = curl_exec($ch);
|
|
||||||
|
|
||||||
if ($result === false)
|
|
||||||
{
|
|
||||||
throw new \Exception(sprintf('Error from Github: %s', curl_error($ch)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
|
|
||||||
return explode("\r\n\r\n", $result, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setCacheData(array $data)
|
|
||||||
{
|
|
||||||
file_put_contents($this->cacheFile, json_encode($data));
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,16 +4,15 @@ namespace App\Services;
|
|||||||
|
|
||||||
use App\Album;
|
use App\Album;
|
||||||
use App\AlbumSources\IAlbumSource;
|
use App\AlbumSources\IAlbumSource;
|
||||||
use App\AlbumSources\IAnalysisQueueSource;
|
use App\Helpers\FileHelper;
|
||||||
use App\Helpers\AnalysisQueueHelper;
|
|
||||||
use App\Helpers\ImageHelper;
|
use App\Helpers\ImageHelper;
|
||||||
use App\Helpers\MiscHelper;
|
|
||||||
use App\Helpers\ThemeHelper;
|
use App\Helpers\ThemeHelper;
|
||||||
use App\Photo;
|
use App\Photo;
|
||||||
|
use Symfony\Component\HttpFoundation\File\File;
|
||||||
|
|
||||||
class PhotoService
|
class PhotoService
|
||||||
{
|
{
|
||||||
const METADATA_VERSION = 2;
|
const METADATA_VERSION = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Album
|
* @var Album
|
||||||
@ -50,12 +49,10 @@ class PhotoService
|
|||||||
$this->themeHelper = new ThemeHelper();
|
$this->themeHelper = new ThemeHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function analyse($queueToken, $isReanalyse = false)
|
public function analyse($queueToken)
|
||||||
{
|
{
|
||||||
/** @var IAnalysisQueueSource $analysisQueueStorage */
|
$queuePath = FileHelper::getQueuePath($queueToken);
|
||||||
$analysisQueueStorage = AnalysisQueueHelper::getStorageQueueSource();
|
$photoFile = join(DIRECTORY_SEPARATOR, [$queuePath, $this->photo->storage_file_name]);
|
||||||
|
|
||||||
$photoFile = $analysisQueueStorage->fetchItemFromAnalysisQueue($queueToken, $this->photo->storage_file_name);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -71,23 +68,12 @@ class PhotoService
|
|||||||
$this->photo->mime_type = $imageInfo['mime'];
|
$this->photo->mime_type = $imageInfo['mime'];
|
||||||
|
|
||||||
// Read the Exif data
|
// Read the Exif data
|
||||||
if (empty($this->photo->raw_exif_data))
|
$exifData = @exif_read_data($photoFile);
|
||||||
{
|
$isExifDataFound = ($exifData !== false && is_array($exifData));
|
||||||
$exifData = @exif_read_data($photoFile);
|
|
||||||
$isExifDataFound = ($exifData !== false && is_array($exifData));
|
|
||||||
$this->photo->raw_exif_data = $isExifDataFound ? base64_encode(serialize($exifData)) : '';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$exifData = unserialize(base64_decode($this->photo->raw_exif_data));
|
|
||||||
$isExifDataFound = ($exifData !== false && is_array($exifData));
|
|
||||||
}
|
|
||||||
|
|
||||||
$angleToRotate = 0;
|
$angleToRotate = 0;
|
||||||
|
|
||||||
// If Exif data contains an Orientation, ensure we rotate the original image as such (providing we don't
|
// If Exif data contains an Orientation, ensure we rotate the original image as such
|
||||||
// currently have a metadata version - i.e. it hasn't been read and rotated already before)
|
if ($isExifDataFound && isset($exifData['Orientation']))
|
||||||
if ($isExifDataFound && isset($exifData['Orientation']) && !$isReanalyse)
|
|
||||||
{
|
{
|
||||||
switch ($exifData['Orientation'])
|
switch ($exifData['Orientation'])
|
||||||
{
|
{
|
||||||
@ -121,14 +107,10 @@ class PhotoService
|
|||||||
if ($isExifDataFound)
|
if ($isExifDataFound)
|
||||||
{
|
{
|
||||||
$this->photo->metadata_version = self::METADATA_VERSION;
|
$this->photo->metadata_version = self::METADATA_VERSION;
|
||||||
$this->photo->taken_at = $this->metadataDateTime($exifData, $this->photo->taken_at);
|
$this->photo->taken_at = $this->metadataDateTime($exifData);
|
||||||
$this->photo->camera_make = $this->metadataCameraMake($exifData, $this->photo->camera_make);
|
$this->photo->camera_make = $this->metadataCameraMake($exifData);
|
||||||
$this->photo->camera_model = $this->metadataCameraModel($exifData, $this->photo->camera_model);
|
$this->photo->camera_model = $this->metadataCameraModel($exifData);
|
||||||
$this->photo->camera_software = $this->metadataCameraSoftware($exifData, $this->photo->camera_software);
|
$this->photo->camera_software = $this->metadataCameraSoftware($exifData);
|
||||||
$this->photo->aperture_fnumber = $this->metadataApertureFNumber($exifData, $this->photo->aperture_fnumber);
|
|
||||||
$this->photo->iso_number = $this->metadataIsoNumber($exifData, $this->photo->iso_number);
|
|
||||||
$this->photo->focal_length = $this->metadataFocalLength($exifData, $this->photo->focal_length);
|
|
||||||
$this->photo->shutter_speed = $this->metadataExposureTime($exifData, $this->photo->shutter_speed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->photo->is_analysed = true;
|
$this->photo->is_analysed = true;
|
||||||
@ -145,11 +127,10 @@ class PhotoService
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// Remove the temporary file
|
|
||||||
@unlink($photoFile);
|
@unlink($photoFile);
|
||||||
|
|
||||||
// Remove from the storage
|
// If the queue directory is now empty, get rid of it
|
||||||
$analysisQueueStorage->deleteItemFromAnalysisQueue($queueToken, $this->photo->storage_file_name);
|
FileHelper::deleteIfEmpty($queuePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,24 +179,6 @@ class PhotoService
|
|||||||
$this->photo->delete();
|
$this->photo->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function downloadOriginalToFolder($folderPath)
|
|
||||||
{
|
|
||||||
$photoPath = join(DIRECTORY_SEPARATOR, [$folderPath, $this->photo->storage_file_name]);
|
|
||||||
$photoHandle = fopen($photoPath, 'w');
|
|
||||||
|
|
||||||
$stream = $this->albumSource->fetchPhotoContent($this->photo);
|
|
||||||
$stream->rewind();
|
|
||||||
while (!$stream->eof())
|
|
||||||
{
|
|
||||||
fwrite($photoHandle, $stream->read(4096));
|
|
||||||
}
|
|
||||||
fflush($photoHandle);
|
|
||||||
fclose($photoHandle);
|
|
||||||
$stream->close();
|
|
||||||
|
|
||||||
return $photoPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function flip($horizontal, $vertical)
|
public function flip($horizontal, $vertical)
|
||||||
{
|
{
|
||||||
// First export the original photo from the storage provider
|
// First export the original photo from the storage provider
|
||||||
@ -301,22 +264,6 @@ class PhotoService
|
|||||||
@unlink($photoPath);
|
@unlink($photoPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function calculateValueFromFraction($input)
|
|
||||||
{
|
|
||||||
$split = explode('/', $input);
|
|
||||||
if (count($split) != 2)
|
|
||||||
{
|
|
||||||
return $split;
|
|
||||||
}
|
|
||||||
|
|
||||||
$numerator = intval($split[0]);
|
|
||||||
$denominator = intval($split[1]);
|
|
||||||
|
|
||||||
return $denominator == 0
|
|
||||||
? 0
|
|
||||||
: ($numerator / $denominator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function downloadToTemporaryFolder()
|
private function downloadToTemporaryFolder()
|
||||||
{
|
{
|
||||||
$photoPath = tempnam(sys_get_temp_dir(), 'BlueTwilight_');
|
$photoPath = tempnam(sys_get_temp_dir(), 'BlueTwilight_');
|
||||||
@ -324,7 +271,7 @@ class PhotoService
|
|||||||
|
|
||||||
$stream = $this->albumSource->fetchPhotoContent($this->photo);
|
$stream = $this->albumSource->fetchPhotoContent($this->photo);
|
||||||
$stream->rewind();
|
$stream->rewind();
|
||||||
while (!$stream->eof())
|
while (!$stream->feof())
|
||||||
{
|
{
|
||||||
fwrite($photoHandle, $stream->read(4096));
|
fwrite($photoHandle, $stream->read(4096));
|
||||||
}
|
}
|
||||||
@ -335,54 +282,37 @@ class PhotoService
|
|||||||
return $photoPath;
|
return $photoPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function metadataApertureFNumber(array $exifData, $originalValue = null)
|
private function metadataCameraMake(array $exifData)
|
||||||
{
|
|
||||||
if (isset($exifData['FNumber']))
|
|
||||||
{
|
|
||||||
$value = $this->calculateValueFromFraction($exifData['FNumber']);
|
|
||||||
|
|
||||||
if (intval($value) === $value)
|
|
||||||
{
|
|
||||||
return sprintf('f/%d', $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sprintf('f/%0.1f', $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $originalValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function metadataCameraMake(array $exifData, $originalValue = null)
|
|
||||||
{
|
{
|
||||||
if (isset($exifData['Make']))
|
if (isset($exifData['Make']))
|
||||||
{
|
{
|
||||||
return $exifData['Make'];
|
return $exifData['Make'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $originalValue;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function metadataCameraModel(array $exifData, $originalValue = null)
|
private function metadataCameraModel(array $exifData)
|
||||||
{
|
{
|
||||||
if (isset($exifData['Model']))
|
if (isset($exifData['Model']))
|
||||||
{
|
{
|
||||||
return $exifData['Model'];
|
return $exifData['Model'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $originalValue;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function metadataCameraSoftware(array $exifData, $originalValue = null)
|
private function metadataCameraSoftware(array $exifData)
|
||||||
{
|
{
|
||||||
if (isset($exifData['Software']))
|
if (isset($exifData['Software']))
|
||||||
{
|
{
|
||||||
return $exifData['Software'];
|
return $exifData['Software'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $originalValue;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function metadataDateTime(array $exifData, $originalValue = null)
|
private function metadataDateTime(array $exifData)
|
||||||
{
|
{
|
||||||
$dateTime = null;
|
$dateTime = null;
|
||||||
if (isset($exifData['DateTimeOriginal']))
|
if (isset($exifData['DateTimeOriginal']))
|
||||||
@ -394,43 +324,11 @@ class PhotoService
|
|||||||
$dateTime = $exifData['DateTime'];
|
$dateTime = $exifData['DateTime'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($dateTime))
|
if (!is_null($dateTime))
|
||||||
{
|
{
|
||||||
return $originalValue;
|
$dateTime = preg_replace('/^([\d]{4}):([\d]{2}):([\d]{2})/', '$1-$2-$3', $dateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return preg_replace('/^([\d]{4}):([\d]{2}):([\d]{2})/', '$1-$2-$3', $dateTime);
|
return $dateTime;
|
||||||
}
|
|
||||||
|
|
||||||
private function metadataExposureTime(array $exifData, $originalValue = null)
|
|
||||||
{
|
|
||||||
if (isset($exifData['ExposureTime']))
|
|
||||||
{
|
|
||||||
$decimal = $this->calculateValueFromFraction($exifData['ExposureTime']);
|
|
||||||
$fraction = MiscHelper::decimalToFraction($decimal);
|
|
||||||
return sprintf('%d/%d', $fraction[0], $fraction[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $originalValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function metadataFocalLength(array $exifData, $originalValue = null)
|
|
||||||
{
|
|
||||||
if (isset($exifData['FocalLength']))
|
|
||||||
{
|
|
||||||
return $this->calculateValueFromFraction($exifData['FocalLength']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $originalValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function metadataIsoNumber(array $exifData, $originalValue = null)
|
|
||||||
{
|
|
||||||
if (isset($exifData['ISOSpeedRatings']))
|
|
||||||
{
|
|
||||||
return $exifData['ISOSpeedRatings'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $originalValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user