Merge branch 'master' into v2.2
This commit is contained in:
commit
6692c530e4
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App;
|
||||
|
||||
use App\AlbumSources\AlbumSourceBase;
|
||||
use App\AlbumSources\IAlbumSource;
|
||||
use App\AlbumSources\LocalFilesystemSource;
|
||||
use App\Helpers\MiscHelper;
|
||||
@ -158,10 +159,7 @@ class Album extends Model
|
||||
*/
|
||||
public function getAlbumSource()
|
||||
{
|
||||
$fullClassName = sprintf('App\AlbumSources\%s', $this->storage->source);
|
||||
|
||||
/** @var IAlbumSource $source */
|
||||
$source = new $fullClassName;
|
||||
$source = AlbumSourceBase::make($this->storage->source);
|
||||
$source->setAlbum($this);
|
||||
$source->setConfiguration($this->storage);
|
||||
|
||||
|
@ -17,6 +17,31 @@ abstract class AlbumSourceBase
|
||||
*/
|
||||
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 setAlbum(Album $album)
|
||||
{
|
||||
$this->album = $album;
|
||||
|
248
app/AlbumSources/BackblazeB2Source.php
Normal file
248
app/AlbumSources/BackblazeB2Source.php
Normal file
@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
namespace App\AlbumSources;
|
||||
|
||||
use App\BackblazeB2FileIdCache;
|
||||
use App\Photo;
|
||||
use App\Services\BackblazeB2Service;
|
||||
use App\Storage;
|
||||
use Guzzle\Http\EntityBody;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
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 EntityBody
|
||||
*/
|
||||
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 EntityBody::fromString('');
|
||||
}
|
||||
|
||||
return EntityBody::fromString(
|
||||
$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
|
||||
);
|
||||
}
|
||||
}
|
19
app/BackblazeB2FileIdCache.php
Normal file
19
app/BackblazeB2FileIdCache.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?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'
|
||||
];
|
||||
}
|
25
app/Exceptions/BackblazeRetryException.php
Normal file
25
app/Exceptions/BackblazeRetryException.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\AlbumSources\AmazonS3Source;
|
||||
use App\AlbumSources\BackblazeB2Source;
|
||||
use App\AlbumSources\IAlbumSource;
|
||||
use App\AlbumSources\LocalFilesystemSource;
|
||||
use App\AlbumSources\OpenStackSource;
|
||||
@ -46,6 +47,7 @@ class ConfigHelper
|
||||
$classes = [
|
||||
LocalFilesystemSource::class,
|
||||
AmazonS3Source::class,
|
||||
BackblazeB2Source::class,
|
||||
OpenStackSource::class,
|
||||
RackspaceSource::class
|
||||
];
|
||||
|
@ -88,7 +88,8 @@ class StorageController extends Controller
|
||||
'container_name',
|
||||
'cdn_url',
|
||||
'access_key',
|
||||
'secret_key'
|
||||
'secret_key',
|
||||
'b2_bucket_type'
|
||||
]));
|
||||
$storage->is_active = true;
|
||||
$storage->is_default = (strtolower($request->get('is_default')) == 'on');
|
||||
@ -217,7 +218,8 @@ class StorageController extends Controller
|
||||
'container_name',
|
||||
'cdn_url',
|
||||
'access_key',
|
||||
'secret_key'
|
||||
'secret_key',
|
||||
'b2_bucket_type'
|
||||
]));
|
||||
$storage->is_active = (strtolower($request->get('is_active')) == 'on');
|
||||
$storage->is_default = (strtolower($request->get('is_default')) == 'on');
|
||||
|
400
app/Services/BackblazeB2Service.php
Normal file
400
app/Services/BackblazeB2Service.php
Normal file
@ -0,0 +1,400 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
@ -30,7 +30,8 @@ class Storage extends Model
|
||||
'container_name',
|
||||
'cdn_url',
|
||||
'access_key',
|
||||
'secret_key'
|
||||
'secret_key',
|
||||
'b2_bucket_type'
|
||||
];
|
||||
|
||||
public function albums()
|
||||
|
@ -6,6 +6,7 @@
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"laravel/framework": "5.5.*",
|
||||
"rackspace/php-opencloud": "^1.16",
|
||||
|
380
composer.lock
generated
380
composer.lock
generated
@ -4,20 +4,20 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "359fa33910865037863ae6ae724af956",
|
||||
"content-hash": "53d67647c5a4d0d450470522c903f745",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.105.0",
|
||||
"version": "3.111.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "3a1159eeb14f707780817bebf73dd3eeeeb710cc"
|
||||
"reference": "a31376012346118b2b88df6d2f0c185af71e3096"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3a1159eeb14f707780817bebf73dd3eeeeb710cc",
|
||||
"reference": "3a1159eeb14f707780817bebf73dd3eeeeb710cc",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a31376012346118b2b88df6d2f0c185af71e3096",
|
||||
"reference": "a31376012346118b2b88df6d2f0c185af71e3096",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -87,7 +87,7 @@
|
||||
"s3",
|
||||
"sdk"
|
||||
],
|
||||
"time": "2019-07-08T18:16:57+00:00"
|
||||
"time": "2019-09-09T18:13:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/cache",
|
||||
@ -389,28 +389,30 @@
|
||||
},
|
||||
{
|
||||
"name": "doctrine/lexer",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/lexer.git",
|
||||
"reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8"
|
||||
"reference": "e17f069ede36f7534b95adec71910ed1b49c74ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8",
|
||||
"reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8",
|
||||
"url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea",
|
||||
"reference": "e17f069ede36f7534b95adec71910ed1b49c74ea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.2"
|
||||
"php": "^7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.5"
|
||||
"doctrine/coding-standard": "^6.0",
|
||||
"phpstan/phpstan": "^0.11.8",
|
||||
"phpunit/phpunit": "^8.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -423,14 +425,14 @@
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Roman Borschel",
|
||||
"email": "roman@code-factory.org"
|
||||
},
|
||||
{
|
||||
"name": "Guilherme Blanco",
|
||||
"email": "guilhermeblanco@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Borschel",
|
||||
"email": "roman@code-factory.org"
|
||||
},
|
||||
{
|
||||
"name": "Johannes Schmitt",
|
||||
"email": "schmittjoh@gmail.com"
|
||||
@ -445,20 +447,20 @@
|
||||
"parser",
|
||||
"php"
|
||||
],
|
||||
"time": "2019-06-08T11:03:04+00:00"
|
||||
"time": "2019-07-30T19:33:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "egulias/email-validator",
|
||||
"version": "2.1.9",
|
||||
"version": "2.1.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/egulias/EmailValidator.git",
|
||||
"reference": "128cc721d771ec2c46ce59698f4ca42b73f71b25"
|
||||
"reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/128cc721d771ec2c46ce59698f4ca42b73f71b25",
|
||||
"reference": "128cc721d771ec2c46ce59698f4ca42b73f71b25",
|
||||
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/92dd169c32f6f55ba570c309d83f5209cefb5e23",
|
||||
"reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -468,7 +470,8 @@
|
||||
"require-dev": {
|
||||
"dominicsayers/isemail": "dev-master",
|
||||
"phpunit/phpunit": "^4.8.35||^5.7||^6.0",
|
||||
"satooshi/php-coveralls": "^1.0.1"
|
||||
"satooshi/php-coveralls": "^1.0.1",
|
||||
"symfony/phpunit-bridge": "^4.4@dev"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
|
||||
@ -476,7 +479,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
"dev-master": "2.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -502,7 +505,7 @@
|
||||
"validation",
|
||||
"validator"
|
||||
],
|
||||
"time": "2019-06-23T10:14:27+00:00"
|
||||
"time": "2019-08-13T17:33:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
@ -835,25 +838,25 @@
|
||||
},
|
||||
{
|
||||
"name": "kylekatarnls/update-helper",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kylekatarnls/update-helper.git",
|
||||
"reference": "b34a46d7f5ec1795b4a15ac9d46b884377262df9"
|
||||
"reference": "5786fa188e0361b9adf9e8199d7280d1b2db165e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/kylekatarnls/update-helper/zipball/b34a46d7f5ec1795b4a15ac9d46b884377262df9",
|
||||
"reference": "b34a46d7f5ec1795b4a15ac9d46b884377262df9",
|
||||
"url": "https://api.github.com/repos/kylekatarnls/update-helper/zipball/5786fa188e0361b9adf9e8199d7280d1b2db165e",
|
||||
"reference": "5786fa188e0361b9adf9e8199d7280d1b2db165e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-plugin-api": "^1.1.0",
|
||||
"composer-plugin-api": "^1.1.0 || ^2.0.0",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeclimate/php-test-reporter": "dev-master",
|
||||
"composer/composer": "^2.0.x-dev",
|
||||
"composer/composer": "2.0.x-dev || ^2.0.0-dev",
|
||||
"phpunit/phpunit": ">=4.8.35 <6.0"
|
||||
},
|
||||
"type": "composer-plugin",
|
||||
@ -876,20 +879,20 @@
|
||||
}
|
||||
],
|
||||
"description": "Update helper",
|
||||
"time": "2019-06-05T08:34:23+00:00"
|
||||
"time": "2019-07-29T11:03:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v5.5.45",
|
||||
"version": "v5.5.48",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "52c79ecf54b6168a54730ccb6c4c9f3561732a80"
|
||||
"reference": "e3e8d585dcfab5abe6261b060f4df0d48f9924bf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/52c79ecf54b6168a54730ccb6c4c9f3561732a80",
|
||||
"reference": "52c79ecf54b6168a54730ccb6c4c9f3561732a80",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/e3e8d585dcfab5abe6261b060f4df0d48f9924bf",
|
||||
"reference": "e3e8d585dcfab5abe6261b060f4df0d48f9924bf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1010,7 +1013,7 @@
|
||||
"framework",
|
||||
"laravel"
|
||||
],
|
||||
"time": "2019-01-28T20:53:19+00:00"
|
||||
"time": "2019-08-20T15:46:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/socialite",
|
||||
@ -1077,16 +1080,16 @@
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "1.0.53",
|
||||
"version": "1.0.55",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem.git",
|
||||
"reference": "08e12b7628f035600634a5e76d95b5eb66cea674"
|
||||
"reference": "33c91155537c6dc899eacdc54a13ac6303f156e6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/08e12b7628f035600634a5e76d95b5eb66cea674",
|
||||
"reference": "08e12b7628f035600634a5e76d95b5eb66cea674",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/33c91155537c6dc899eacdc54a13ac6303f156e6",
|
||||
"reference": "33c91155537c6dc899eacdc54a13ac6303f156e6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1157,7 +1160,7 @@
|
||||
"sftp",
|
||||
"storage"
|
||||
],
|
||||
"time": "2019-06-18T20:09:29+00:00"
|
||||
"time": "2019-08-24T11:17:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth1-client",
|
||||
@ -1251,16 +1254,16 @@
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "1.24.0",
|
||||
"version": "1.25.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/monolog.git",
|
||||
"reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266"
|
||||
"reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
|
||||
"reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/70e65a5470a42cfec1a7da00d30edb6e617e8dcf",
|
||||
"reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1325,7 +1328,7 @@
|
||||
"logging",
|
||||
"psr-3"
|
||||
],
|
||||
"time": "2018-11-05T09:00:11+00:00"
|
||||
"time": "2019-09-06T13:49:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mtdowling/cron-expression",
|
||||
@ -1534,22 +1537,22 @@
|
||||
},
|
||||
{
|
||||
"name": "php-amqplib/php-amqplib",
|
||||
"version": "v2.9.2",
|
||||
"version": "v2.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-amqplib/php-amqplib.git",
|
||||
"reference": "76faddcd668dabb8d4f7c00e86b8a9decd781a59"
|
||||
"reference": "04e5366f032906d5f716890427e425e71307d3a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/76faddcd668dabb8d4f7c00e86b8a9decd781a59",
|
||||
"reference": "76faddcd668dabb8d4f7c00e86b8a9decd781a59",
|
||||
"url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/04e5366f032906d5f716890427e425e71307d3a8",
|
||||
"reference": "04e5366f032906d5f716890427e425e71307d3a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-bcmath": "*",
|
||||
"ext-sockets": "*",
|
||||
"php": ">=5.4.0"
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"replace": {
|
||||
"videlalvaro/php-amqplib": "self.version"
|
||||
@ -1557,14 +1560,14 @@
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"nategood/httpful": "^0.2.20",
|
||||
"phpdocumentor/phpdocumentor": "^2.9",
|
||||
"phpunit/phpunit": "^4.8",
|
||||
"phpdocumentor/phpdocumentor": "dev-master",
|
||||
"phpunit/phpunit": "^5.7|^6.5|^7.0",
|
||||
"squizlabs/php_codesniffer": "^2.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.8-dev"
|
||||
"dev-master": "2.10-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -1583,18 +1586,18 @@
|
||||
},
|
||||
{
|
||||
"name": "John Kelly",
|
||||
"email": "johnmkelly86@gmail.com",
|
||||
"role": "Maintainer"
|
||||
"role": "Maintainer",
|
||||
"email": "johnmkelly86@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Raúl Araya",
|
||||
"email": "nubeiro@gmail.com",
|
||||
"role": "Maintainer"
|
||||
"role": "Maintainer",
|
||||
"email": "nubeiro@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Luke Bakken",
|
||||
"email": "luke@bakken.io",
|
||||
"role": "Maintainer"
|
||||
"role": "Maintainer",
|
||||
"email": "luke@bakken.io"
|
||||
}
|
||||
],
|
||||
"description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.",
|
||||
@ -1604,7 +1607,7 @@
|
||||
"queue",
|
||||
"rabbitmq"
|
||||
],
|
||||
"time": "2019-04-24T15:36:21+00:00"
|
||||
"time": "2019-08-08T18:28:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
@ -2043,16 +2046,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v3.4.29",
|
||||
"version": "v3.4.31",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "c4d2f3529755ffc0be9fb823583b28d8744eeb3d"
|
||||
"reference": "4510f04e70344d70952566e4262a0b11df39cb10"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/c4d2f3529755ffc0be9fb823583b28d8744eeb3d",
|
||||
"reference": "c4d2f3529755ffc0be9fb823583b28d8744eeb3d",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/4510f04e70344d70952566e4262a0b11df39cb10",
|
||||
"reference": "4510f04e70344d70952566e4262a0b11df39cb10",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2111,7 +2114,7 @@
|
||||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-06-05T11:33:52+00:00"
|
||||
"time": "2019-08-26T07:52:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
@ -2168,16 +2171,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
"version": "v3.4.29",
|
||||
"version": "v3.4.31",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/debug.git",
|
||||
"reference": "1172dc1abe44dfadd162239153818b074e6e53bf"
|
||||
"reference": "0b600300918780001e2821db77bc28b677794486"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/1172dc1abe44dfadd162239153818b074e6e53bf",
|
||||
"reference": "1172dc1abe44dfadd162239153818b074e6e53bf",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/0b600300918780001e2821db77bc28b677794486",
|
||||
"reference": "0b600300918780001e2821db77bc28b677794486",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2220,7 +2223,7 @@
|
||||
],
|
||||
"description": "Symfony Debug Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-06-18T21:26:03+00:00"
|
||||
"time": "2019-08-20T13:31:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
@ -2284,16 +2287,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v3.4.29",
|
||||
"version": "v3.4.31",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "5f80266a729e30bbcc37f8bf0e62c3d5a38c8208"
|
||||
"reference": "1fcad80b440abcd1451767349906b6f9d3961d37"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/5f80266a729e30bbcc37f8bf0e62c3d5a38c8208",
|
||||
"reference": "5f80266a729e30bbcc37f8bf0e62c3d5a38c8208",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/1fcad80b440abcd1451767349906b6f9d3961d37",
|
||||
"reference": "1fcad80b440abcd1451767349906b6f9d3961d37",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2329,20 +2332,20 @@
|
||||
],
|
||||
"description": "Symfony Finder Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-05-30T15:47:52+00:00"
|
||||
"time": "2019-08-14T09:39:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v3.4.29",
|
||||
"version": "v3.4.31",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "8cfbf75bb3a72963b12c513a73e9247891df24f8"
|
||||
"reference": "b3d57a1c325f39f703b249bed7998ce8c64236b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/8cfbf75bb3a72963b12c513a73e9247891df24f8",
|
||||
"reference": "8cfbf75bb3a72963b12c513a73e9247891df24f8",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/b3d57a1c325f39f703b249bed7998ce8c64236b4",
|
||||
"reference": "b3d57a1c325f39f703b249bed7998ce8c64236b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2383,20 +2386,20 @@
|
||||
],
|
||||
"description": "Symfony HttpFoundation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-06-22T20:10:25+00:00"
|
||||
"time": "2019-08-26T07:50:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v3.4.29",
|
||||
"version": "v3.4.31",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "abbb38dbab652ddc40a86d0c3b0e14ca52d58ed2"
|
||||
"reference": "f6d35bb306b26812df007525f5757a8b0e95857e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/abbb38dbab652ddc40a86d0c3b0e14ca52d58ed2",
|
||||
"reference": "abbb38dbab652ddc40a86d0c3b0e14ca52d58ed2",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/f6d35bb306b26812df007525f5757a8b0e95857e",
|
||||
"reference": "f6d35bb306b26812df007525f5757a8b0e95857e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2472,20 +2475,20 @@
|
||||
],
|
||||
"description": "Symfony HttpKernel Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-06-26T13:56:39+00:00"
|
||||
"time": "2019-08-26T16:36:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.11.0",
|
||||
"version": "v1.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "82ebae02209c21113908c229e9883c419720738a"
|
||||
"reference": "550ebaac289296ce228a706d0867afc34687e3f4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a",
|
||||
"reference": "82ebae02209c21113908c229e9883c419720738a",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4",
|
||||
"reference": "550ebaac289296ce228a706d0867afc34687e3f4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2497,7 +2500,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.11-dev"
|
||||
"dev-master": "1.12-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2513,13 +2516,13 @@
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Gert de Pagter",
|
||||
"email": "BackEndTea@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for ctype functions",
|
||||
@ -2530,20 +2533,20 @@
|
||||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"time": "2019-02-06T07:57:58+00:00"
|
||||
"time": "2019-08-06T08:03:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-iconv",
|
||||
"version": "v1.11.0",
|
||||
"version": "v1.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-iconv.git",
|
||||
"reference": "f037ea22acfaee983e271dd9c3b8bb4150bd8ad7"
|
||||
"reference": "685968b11e61a347c18bf25db32effa478be610f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/f037ea22acfaee983e271dd9c3b8bb4150bd8ad7",
|
||||
"reference": "f037ea22acfaee983e271dd9c3b8bb4150bd8ad7",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/685968b11e61a347c18bf25db32effa478be610f",
|
||||
"reference": "685968b11e61a347c18bf25db32effa478be610f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2555,7 +2558,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.11-dev"
|
||||
"dev-master": "1.12-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2589,20 +2592,20 @@
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2019-02-06T07:57:58+00:00"
|
||||
"time": "2019-08-06T08:03:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-idn",
|
||||
"version": "v1.11.0",
|
||||
"version": "v1.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-idn.git",
|
||||
"reference": "c766e95bec706cdd89903b1eda8afab7d7a6b7af"
|
||||
"reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c766e95bec706cdd89903b1eda8afab7d7a6b7af",
|
||||
"reference": "c766e95bec706cdd89903b1eda8afab7d7a6b7af",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6af626ae6fa37d396dc90a399c0ff08e5cfc45b2",
|
||||
"reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2616,7 +2619,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.9-dev"
|
||||
"dev-master": "1.12-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2632,13 +2635,13 @@
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Laurent Bassin",
|
||||
"email": "laurent@bassin.info"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
|
||||
@ -2651,20 +2654,20 @@
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2019-03-04T13:44:35+00:00"
|
||||
"time": "2019-08-06T08:03:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.11.0",
|
||||
"version": "v1.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
|
||||
"reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
|
||||
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17",
|
||||
"reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2676,7 +2679,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.11-dev"
|
||||
"dev-master": "1.12-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2710,20 +2713,20 @@
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2019-02-06T07:57:58+00:00"
|
||||
"time": "2019-08-06T08:03:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php70",
|
||||
"version": "v1.11.0",
|
||||
"version": "v1.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php70.git",
|
||||
"reference": "bc4858fb611bda58719124ca079baff854149c89"
|
||||
"reference": "54b4c428a0054e254223797d2713c31e08610831"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/bc4858fb611bda58719124ca079baff854149c89",
|
||||
"reference": "bc4858fb611bda58719124ca079baff854149c89",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/54b4c428a0054e254223797d2713c31e08610831",
|
||||
"reference": "54b4c428a0054e254223797d2713c31e08610831",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2733,7 +2736,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.11-dev"
|
||||
"dev-master": "1.12-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2769,20 +2772,20 @@
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2019-02-06T07:57:58+00:00"
|
||||
"time": "2019-08-06T08:03:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php72",
|
||||
"version": "v1.11.0",
|
||||
"version": "v1.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php72.git",
|
||||
"reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c"
|
||||
"reference": "04ce3335667451138df4307d6a9b61565560199e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/ab50dcf166d5f577978419edd37aa2bb8eabce0c",
|
||||
"reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e",
|
||||
"reference": "04ce3335667451138df4307d6a9b61565560199e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2791,7 +2794,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.11-dev"
|
||||
"dev-master": "1.12-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -2824,20 +2827,20 @@
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2019-02-06T07:57:58+00:00"
|
||||
"time": "2019-08-06T08:03:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v3.4.29",
|
||||
"version": "v3.4.31",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "d129c017e8602507688ef2c3007951a16c1a8407"
|
||||
"reference": "d822cb654000a95b7855362c0d5b127f6a6d8baa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/d129c017e8602507688ef2c3007951a16c1a8407",
|
||||
"reference": "d129c017e8602507688ef2c3007951a16c1a8407",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/d822cb654000a95b7855362c0d5b127f6a6d8baa",
|
||||
"reference": "d822cb654000a95b7855362c0d5b127f6a6d8baa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2873,20 +2876,20 @@
|
||||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-05-30T15:47:52+00:00"
|
||||
"time": "2019-08-26T07:52:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v3.4.29",
|
||||
"version": "v3.4.31",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/routing.git",
|
||||
"reference": "8d804d8a65a26dc9de1aaf2ff3a421e581d050e6"
|
||||
"reference": "8b0faa681c4ee14701e76a7056fef15ac5384163"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/8d804d8a65a26dc9de1aaf2ff3a421e581d050e6",
|
||||
"reference": "8d804d8a65a26dc9de1aaf2ff3a421e581d050e6",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/8b0faa681c4ee14701e76a7056fef15ac5384163",
|
||||
"reference": "8b0faa681c4ee14701e76a7056fef15ac5384163",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2949,26 +2952,26 @@
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"time": "2019-06-26T11:14:13+00:00"
|
||||
"time": "2019-08-26T07:50:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v4.3.2",
|
||||
"version": "v4.3.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "934ab1d18545149e012aa898cf02e9f23790f7a0"
|
||||
"reference": "28498169dd334095fa981827992f3a24d50fed0f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/934ab1d18545149e012aa898cf02e9f23790f7a0",
|
||||
"reference": "934ab1d18545149e012aa898cf02e9f23790f7a0",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/28498169dd334095fa981827992f3a24d50fed0f",
|
||||
"reference": "28498169dd334095fa981827992f3a24d50fed0f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1.3",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/translation-contracts": "^1.1.2"
|
||||
"symfony/translation-contracts": "^1.1.6"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/config": "<3.4",
|
||||
@ -3025,20 +3028,20 @@
|
||||
],
|
||||
"description": "Symfony Translation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-06-13T11:03:18+00:00"
|
||||
"time": "2019-08-26T08:55:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation-contracts",
|
||||
"version": "v1.1.5",
|
||||
"version": "v1.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation-contracts.git",
|
||||
"reference": "cb4b18ad7b92a26e83b65dde940fab78339e6f3c"
|
||||
"reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/cb4b18ad7b92a26e83b65dde940fab78339e6f3c",
|
||||
"reference": "cb4b18ad7b92a26e83b65dde940fab78339e6f3c",
|
||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/325b17c24f3ee23cbecfa63ba809c6d89b5fa04a",
|
||||
"reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3082,20 +3085,20 @@
|
||||
"interoperability",
|
||||
"standards"
|
||||
],
|
||||
"time": "2019-06-13T11:15:36+00:00"
|
||||
"time": "2019-08-02T12:15:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v3.4.29",
|
||||
"version": "v3.4.31",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "7b92618169c44af4bb226f69dbac42b56b1a7745"
|
||||
"reference": "5408ad7194737ee1bc5ab7a9683fb6925f92c3e4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/7b92618169c44af4bb226f69dbac42b56b1a7745",
|
||||
"reference": "7b92618169c44af4bb226f69dbac42b56b1a7745",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/5408ad7194737ee1bc5ab7a9683fb6925f92c3e4",
|
||||
"reference": "5408ad7194737ee1bc5ab7a9683fb6925f92c3e4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3151,7 +3154,7 @@
|
||||
"debug",
|
||||
"dump"
|
||||
],
|
||||
"time": "2019-06-13T16:26:35+00:00"
|
||||
"time": "2019-08-26T07:50:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tijsverkoyen/css-to-inline-styles",
|
||||
@ -3311,16 +3314,16 @@
|
||||
},
|
||||
{
|
||||
"name": "filp/whoops",
|
||||
"version": "2.4.1",
|
||||
"version": "2.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filp/whoops.git",
|
||||
"reference": "6fb502c23885701a991b0bba974b1a8eb6673577"
|
||||
"reference": "cde50e6720a39fdacb240159d3eea6865d51fd96"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/6fb502c23885701a991b0bba974b1a8eb6673577",
|
||||
"reference": "6fb502c23885701a991b0bba974b1a8eb6673577",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/cde50e6720a39fdacb240159d3eea6865d51fd96",
|
||||
"reference": "cde50e6720a39fdacb240159d3eea6865d51fd96",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3368,7 +3371,7 @@
|
||||
"throwable",
|
||||
"whoops"
|
||||
],
|
||||
"time": "2019-07-04T09:00:00+00:00"
|
||||
"time": "2019-08-07T09:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fzaninotto/faker",
|
||||
@ -3532,16 +3535,16 @@
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.9.1",
|
||||
"version": "1.9.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72"
|
||||
"reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72",
|
||||
"reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea",
|
||||
"reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3576,7 +3579,7 @@
|
||||
"object",
|
||||
"object graph"
|
||||
],
|
||||
"time": "2019-04-07T13:18:21+00:00"
|
||||
"time": "2019-08-09T12:45:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@ -4501,16 +4504,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/exporter.git",
|
||||
"reference": "234199f4528de6d12aaa58b612e98f7d36adb937"
|
||||
"reference": "06a9a5947f47b3029d76118eb5c22802e5869687"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937",
|
||||
"reference": "234199f4528de6d12aaa58b612e98f7d36adb937",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/06a9a5947f47b3029d76118eb5c22802e5869687",
|
||||
"reference": "06a9a5947f47b3029d76118eb5c22802e5869687",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -4537,6 +4540,10 @@
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"email": "sebastian@phpunit.de"
|
||||
},
|
||||
{
|
||||
"name": "Jeff Welch",
|
||||
"email": "whatthejeff@gmail.com"
|
||||
@ -4545,17 +4552,13 @@
|
||||
"name": "Volker Dusch",
|
||||
"email": "github@wallbash.com"
|
||||
},
|
||||
{
|
||||
"name": "Bernhard Schussek",
|
||||
"email": "bschussek@2bepublished.at"
|
||||
},
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"email": "sebastian@phpunit.de"
|
||||
},
|
||||
{
|
||||
"name": "Adam Harvey",
|
||||
"email": "aharvey@php.net"
|
||||
},
|
||||
{
|
||||
"name": "Bernhard Schussek",
|
||||
"email": "bschussek@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Provides the functionality to export PHP variables for visualization",
|
||||
@ -4564,7 +4567,7 @@
|
||||
"export",
|
||||
"exporter"
|
||||
],
|
||||
"time": "2017-04-03T13:19:02+00:00"
|
||||
"time": "2019-08-11T12:43:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
@ -4945,16 +4948,16 @@
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
"version": "1.4.0",
|
||||
"version": "1.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/webmozart/assert.git",
|
||||
"reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
|
||||
"reference": "88e6d84706d09a236046d686bbea96f07b3a34f4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
|
||||
"reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
|
||||
"url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4",
|
||||
"reference": "88e6d84706d09a236046d686bbea96f07b3a34f4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -4962,8 +4965,7 @@
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.6",
|
||||
"sebastian/version": "^1.0.1"
|
||||
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@ -4992,7 +4994,7 @@
|
||||
"check",
|
||||
"validate"
|
||||
],
|
||||
"time": "2018-12-25T11:19:39+00:00"
|
||||
"time": "2019-08-24T08:43:50+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
@ -5001,7 +5003,9 @@
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=7.0.0"
|
||||
"php": ">=7.0.0",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"platform-dev": []
|
||||
}
|
||||
|
@ -14,6 +14,11 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'backblaze_b2' => [
|
||||
'auth_url' => 'https://api.backblazeb2.com/b2api/v2/b2_authorize_account',
|
||||
'download_token_lifetime' => 300
|
||||
],
|
||||
|
||||
'gitea' => [
|
||||
'api_url' => 'https://apps.andysh.uk/api/v1',
|
||||
'cache_time_seconds' => 3600,
|
||||
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddBackblazeStorageColumns extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('storages', function (Blueprint $table)
|
||||
{
|
||||
$table->tinyInteger('b2_bucket_type')->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('storages', function (Blueprint $table)
|
||||
{
|
||||
$table->dropColumn('b2_bucket_type');
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateBackblazeB2FileIdCachesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('backblaze_b2_file_id_caches', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('photo_id');
|
||||
$table->string('storage_path');
|
||||
$table->string('b2_file_id');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('photo_id')
|
||||
->references('id')->on('photos')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('backblaze_b2_file_id_caches');
|
||||
}
|
||||
}
|
143
public/b2_test.php
Normal file
143
public/b2_test.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
function b2_authorize_account()
|
||||
{
|
||||
$application_key_id = "0023254ec9bda08000000000a"; // Obtained from your B2 account page
|
||||
$application_key = "K002eARNPUlxdj1XaVJbwEYPMz0c7e8"; // Obtained from your B2 account page
|
||||
$credentials = base64_encode($application_key_id . ":" . $application_key);
|
||||
$url = "https://api.backblazeb2.com/b2api/v2/b2_authorize_account";
|
||||
|
||||
$session = curl_init($url);
|
||||
|
||||
// Add headers
|
||||
$headers = array();
|
||||
$headers[] = "Accept: application/json";
|
||||
$headers[] = "Authorization: Basic " . $credentials;
|
||||
curl_setopt($session, CURLOPT_HTTPHEADER, $headers); // Add headers
|
||||
|
||||
curl_setopt($session, CURLOPT_HTTPGET, true); // HTTP GET
|
||||
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response
|
||||
$server_output = curl_exec($session);
|
||||
curl_close ($session);
|
||||
echo ($server_output);
|
||||
|
||||
return json_decode($server_output);
|
||||
}
|
||||
|
||||
function b2_download_file_by_id($download_url, $auth_token)
|
||||
{
|
||||
//$download_url = ""; // From b2_authorize_account call
|
||||
$file_id = "4_z731245f41efc196b6dda0018_f116729ca6de74b38_d20190910_m132847_c002_v0001127_t0021"; // The ID of the file you want to download
|
||||
$uri = $download_url . "/b2api/v2/b2_download_file_by_id?fileId=" . $file_id;
|
||||
|
||||
$session = curl_init($uri);
|
||||
|
||||
curl_setopt($session, CURLOPT_HTTPGET, true); // HTTP GET
|
||||
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response
|
||||
|
||||
echo '<p>' . $uri . '</p>';
|
||||
|
||||
$server_output = curl_exec($session); // Let's do this!
|
||||
|
||||
if (curl_getinfo($session, CURLINFO_HTTP_CODE) != 200)
|
||||
{
|
||||
echo '<p>' . $server_output . '</p>';
|
||||
}
|
||||
else
|
||||
{
|
||||
echo '<p>' . (strlen($server_output) . ' bytes received') . '</p>'; // Tell me about the rabbits, George!
|
||||
}
|
||||
|
||||
curl_close ($session); // Clean up
|
||||
|
||||
//$download_url = ""; // From b2_authorize_account call
|
||||
$file_id = "4_z731245f41efc196b6dda0018_f116729ca6de74b38_d20190910_m132847_c002_v0001127_t0021"; // The ID of the file you want to download
|
||||
$uri = $download_url . "/b2api/v2/b2_download_file_by_id?fileId=" . $file_id . '&Authorization=' . $auth_token;
|
||||
|
||||
$session = curl_init($uri);
|
||||
|
||||
curl_setopt($session, CURLOPT_HTTPGET, true); // HTTP GET
|
||||
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response
|
||||
|
||||
echo '<p>' . $uri . '</p>';
|
||||
|
||||
$server_output = curl_exec($session); // Let's do this!
|
||||
|
||||
if (curl_getinfo($session, CURLINFO_HTTP_CODE) != 200)
|
||||
{
|
||||
echo '<p>' . $server_output . '</p>';
|
||||
}
|
||||
else
|
||||
{
|
||||
echo '<p>' . (strlen($server_output) . ' bytes received') . '</p>'; // Tell me about the rabbits, George!
|
||||
}
|
||||
|
||||
curl_close ($session); // Clean up
|
||||
}
|
||||
|
||||
function b2_download_file_by_name($download_url, $auth_token)
|
||||
{
|
||||
//$download_url = ""; // From b2_authorize_account call
|
||||
$bucket_name = "andysh-bt-test"; // The NAME of the bucket you want to download from
|
||||
$file_name = "B2-Test-Album/preview/7tgoy55do1vjv180ytlp.jpeg"; // The name of the file you want to download
|
||||
$uri = $download_url . "/file/" . $bucket_name . "/" . $file_name;
|
||||
|
||||
$session = curl_init($uri);
|
||||
|
||||
curl_setopt($session, CURLOPT_HTTPGET, true); // HTTP GET
|
||||
curl_setopt($session, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response
|
||||
|
||||
echo '<p>' . $uri . '</p>';
|
||||
|
||||
$server_output = curl_exec($session); // Let's do this!
|
||||
|
||||
if (curl_getinfo($session, CURLINFO_HTTP_CODE) != 200)
|
||||
{
|
||||
echo '<p>' . $server_output . '</p>';
|
||||
}
|
||||
else
|
||||
{
|
||||
echo '<p>' . (strlen($server_output) . ' bytes received') . '</p>'; // Tell me about the rabbits, George!
|
||||
}
|
||||
|
||||
curl_close ($session); // Clean up
|
||||
|
||||
// You will need to use the account authorization token if your bucket's type is allPrivate.
|
||||
|
||||
//$download_url = ""; // From b2_authorize_account call
|
||||
$bucket_name = "andysh-bt-test"; // The NAME of the bucket you want to download from
|
||||
$file_name = "B2-Test-Album/preview/7tgoy55do1vjv180ytlp.jpeg"; // The name of the file you want to download
|
||||
//$auth_token = ""; // From b2_authorize_account call
|
||||
$uri = $download_url . "/file/" . $bucket_name . "/" . $file_name . '?Authorization=' . $auth_token;
|
||||
|
||||
$session = curl_init($uri);
|
||||
|
||||
curl_setopt($session, CURLOPT_HTTPGET, true); // HTTP POST
|
||||
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response
|
||||
|
||||
echo '<p>' . $uri . '</p>';
|
||||
|
||||
$server_output = curl_exec($session); // Let's do this!
|
||||
|
||||
if (curl_getinfo($session, CURLINFO_HTTP_CODE) != 200)
|
||||
{
|
||||
echo '<p>' . $server_output . '</p>';
|
||||
}
|
||||
else
|
||||
{
|
||||
echo '<p>' . (strlen($server_output) . ' bytes received') . '</p>'; // Tell me about the rabbits, George!
|
||||
}
|
||||
|
||||
curl_close ($session); // Clean up
|
||||
}
|
||||
|
||||
?>
|
||||
<h2>b2_authorize_account</h2>
|
||||
<?php $authorize_account_result = b2_authorize_account(); ?>
|
||||
|
||||
<h2>b2_download_file_by_name</h2>
|
||||
<?php b2_download_file_by_name($authorize_account_result->downloadUrl, $authorize_account_result->authorizationToken); ?>
|
||||
|
||||
<h2>b2_download_file_by_id</h2>
|
||||
<?php b2_download_file_by_id($authorize_account_result->downloadUrl, $authorize_account_result->authorizationToken); ?>
|
18
readme.md
18
readme.md
@ -6,28 +6,24 @@ It takes advantage of modern frameworks (Laravel, Bootstrap 4, VueJS) as well as
|
||||
|
||||
You can see Blue Twilight in action on my own photo gallery - the reason I wrote Blue Twilight - at: [photos.andysh.uk](https://photos.andysh.uk)
|
||||
|
||||
## Version 2 Branch (2.0, 2.1, etc.)
|
||||
## Blue Twilight Cloud
|
||||
|
||||
Version 2 is the first version I have released as open-source. The previous version (1.1.2) was only ever used on my own gallery.
|
||||
|
||||
This is a major update that includes 2 key new features: fine-grained security controls, and nested albums. It also updates the default template to Bootstrap v4 and VueJS (replacing KnockoutJS.)
|
||||
|
||||
With the launch of version 2.0.0, this has now been officially released - see the [Releases](https://apps.andysh.uk/aheathershaw/blue-twilight/releases) page for the latest version.
|
||||
If you want your own dedicated, private instance of Blue Twilight without the hassle of managing servers, hosting and updates - check out [Blue Twilight Cloud](https://showmy.photos).
|
||||
|
||||
## Demo System
|
||||
|
||||
See Blue Twilight in action using the demo system. Full details are [available here](https://andysh.uk/software/blue-twilight-php-photo-gallery/demo/).
|
||||
See Blue Twilight in action using the demo system. Full details are [available here](https://showmy.photos/demo/).
|
||||
|
||||
The link to the demo system is: http://demo.showmy.photos. Login with:
|
||||
The link to the demo system is: https://demo.showmy.photos. Login with:
|
||||
|
||||
* Username: **demo@demo.com**
|
||||
* Password: **demo123**
|
||||
|
||||
## Useful Links
|
||||
|
||||
* [Blue Twilight website](https://andysh.uk/software/blue-twilight-php-photo-gallery/)
|
||||
* [User Manual](https://andysh.uk/software/blue-twilight-php-photo-gallery/manual/)
|
||||
* [Installation Guide](https://andysh.uk/software/blue-twilight-php-photo-gallery/manual/installation/)
|
||||
* [Blue Twilight website](https://showmy.photos/)
|
||||
* [User Manual](https://showmy.photos/user-guide/)
|
||||
* [Installation Guide](https://showmy.photos/user-guide/installation/)
|
||||
* [Issues/Tasks](https://apps.andysh.uk/aheathershaw/blue-twilight/issues)
|
||||
* [Roadmap](https://apps.andysh.uk/aheathershaw/blue-twilight/milestones)
|
||||
|
||||
|
@ -323,6 +323,7 @@ return [
|
||||
'photos' => 'photo|photos',
|
||||
'users' => 'user|users',
|
||||
],
|
||||
'storage_backblaze_access_key_id_help' => 'To use your account\'s master key, enter your account ID here.',
|
||||
'storage_title' => 'Storage Locations',
|
||||
'sysinfo_panel' => 'System information',
|
||||
'sysinfo_widget' => [
|
||||
|
@ -98,7 +98,15 @@ return [
|
||||
'storage_access_key_label' => 'Access key:',
|
||||
'storage_active_label' => 'Location is active. Uncheck to prevent creating new albums in this location.',
|
||||
'storage_api_key_label' => 'API key:',
|
||||
'storage_application_key_id_label' => 'Application key ID:',
|
||||
'storage_application_key_label' => 'Application key:',
|
||||
'storage_auth_url_label' => 'Authentication URL:',
|
||||
'storage_b2_bucket_type' => [
|
||||
'autodetect' => 'Auto-detect',
|
||||
'label' => 'Bucket type:',
|
||||
'private' => 'Private',
|
||||
'public' => 'Public'
|
||||
],
|
||||
'storage_bucket_name_label' => 'Bucket name:',
|
||||
'storage_cdn_url_label' => 'Public CDN URL (if supported and enabled):',
|
||||
'storage_container_name_label' => 'Container name:',
|
||||
|
@ -2,6 +2,7 @@
|
||||
return [
|
||||
'album_sources' => [
|
||||
'amazon_s3' => 'Amazon S3 (or S3-compatible)',
|
||||
'backblaze_b2' => 'Backblaze B2 Cloud',
|
||||
'filesystem' => 'Local filesystem',
|
||||
'openstack' => 'OpenStack cloud storage',
|
||||
'rackspace' => 'Rackspace cloud storage'
|
||||
|
@ -63,6 +63,10 @@
|
||||
@include(Theme::viewName('partials.admin_storages_rackspace_options'))
|
||||
</div>
|
||||
|
||||
<div v-if="storage_driver == 'BackblazeB2Source'">
|
||||
@include(Theme::viewName('partials.admin_storages_backblaze_b2_options'))
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id="is-default" name="is_default"@if (old('is_default', $storage->is_default)) checked="checked"@endif>
|
||||
<label class="form-check-label" for="is-default">@lang('forms.default_storage_label')</label>
|
||||
|
@ -61,6 +61,11 @@
|
||||
@include(Theme::viewName('partials.admin_storages_rackspace_options'))
|
||||
@endif
|
||||
|
||||
<div v-if="storage_driver == 'BackblazeB2Source'">
|
||||
<hr/>
|
||||
@include(Theme::viewName('partials.admin_storages_backblaze_b2_options'))
|
||||
</div>
|
||||
|
||||
<div class="text-right">
|
||||
<a href="{{ route('storage.index') }}" class="btn btn-default">@lang('forms.cancel_action')</a>
|
||||
<button type="submit" class="btn btn-success"><i class="fa fa-fw fa-check"></i> @lang('forms.save_action')</button>
|
||||
|
@ -0,0 +1,58 @@
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-control-label" for="access-key">@lang('forms.storage_application_key_id_label')</label>
|
||||
<input type="text" class="form-control{{ $errors->has('access_key') ? ' is-invalid' : '' }}" id="access-key" name="access_key" value="{{ old('access_key', $storage->access_key) }}">
|
||||
<small class="form-text text-muted">@lang('admin.storage_backblaze_access_key_id_help')</small>
|
||||
|
||||
@if ($errors->has('access_key'))
|
||||
<div class="invalid-feedback">
|
||||
<strong>{{ $errors->first('access_key') }}</strong>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-control-label" for="secret-key">@lang('forms.storage_application_key_label')</label>
|
||||
<input type="text" class="form-control{{ $errors->has('secret_key') ? ' is-invalid' : '' }}" id="secret-key" name="secret_key" value="{{ old('secret_key', $storage->secret_key) }}">
|
||||
|
||||
@if ($errors->has('secret_key'))
|
||||
<div class="invalid-feedback">
|
||||
<strong>{{ $errors->first('secret_key') }}</strong>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-control-label" for="container-name">@lang('forms.storage_bucket_name_label')</label>
|
||||
<input type="text" class="form-control{{ $errors->has('container_name') ? ' is-invalid' : '' }}" id="container-name" name="container_name" value="{{ old('container_name', $storage->container_name) }}">
|
||||
|
||||
@if ($errors->has('container_name'))
|
||||
<div class="invalid-feedback">
|
||||
<strong>{{ $errors->first('container_name') }}</strong>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-control-label" for="b2-bucket-type">@lang('forms.storage_b2_bucket_type.label')</label>
|
||||
<select class="form-control{{ $errors->has('b2_bucket_type') ? ' is-invalid' : '' }}" id="b2-bucket-type" name="b2_bucket_type">
|
||||
<option value="0"{{ old('b2_bucket_type', $storage->b2_bucket_type) === 0 ? ' selected="selected"' : '' }}>@lang('forms.storage_b2_bucket_type.autodetect')</option>
|
||||
<option value="1"{{ old('b2_bucket_type', $storage->b2_bucket_type) === 1 ? ' selected="selected"' : '' }}>@lang('forms.storage_b2_bucket_type.private')</option>
|
||||
<option value="2"{{ old('b2_bucket_type', $storage->b2_bucket_type) === 2 ? ' selected="selected"' : '' }}>@lang('forms.storage_b2_bucket_type.public')</option>
|
||||
</select>
|
||||
|
||||
@if ($errors->has('b2_bucket_type'))
|
||||
<div class="invalid-feedback">
|
||||
<strong>{{ $errors->first('b2_bucket_type') }}</strong>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user