Dropbox #106 - files can be uploaded to a Dropbox account using a generated access token, and downloaded using the Blue Twilight download endpoint.
This commit is contained in:
parent
da99b0b05a
commit
582e5fffaa
118
app/AlbumSources/DropboxSource.php
Normal file
118
app/AlbumSources/DropboxSource.php
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\AlbumSources;
|
||||||
|
|
||||||
|
use App\Photo;
|
||||||
|
use App\Services\DropboxService;
|
||||||
|
use Guzzle\Http\EntityBody;
|
||||||
|
|
||||||
|
class DropboxSource extends AlbumSourceBase implements IAlbumSource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var DropboxService
|
||||||
|
*/
|
||||||
|
private $dropboxClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an entire album's media contents.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deleteAlbumContents()
|
||||||
|
{
|
||||||
|
// TODO: Implement deleteAlbumContents() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
// TODO: Implement deleteThumbnail() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
return EntityBody::fromString($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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ namespace App\Helpers;
|
|||||||
|
|
||||||
use App\AlbumSources\AmazonS3Source;
|
use App\AlbumSources\AmazonS3Source;
|
||||||
use App\AlbumSources\BackblazeB2Source;
|
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;
|
||||||
@ -48,6 +49,7 @@ class ConfigHelper
|
|||||||
LocalFilesystemSource::class,
|
LocalFilesystemSource::class,
|
||||||
AmazonS3Source::class,
|
AmazonS3Source::class,
|
||||||
BackblazeB2Source::class,
|
BackblazeB2Source::class,
|
||||||
|
DropboxSource::class,
|
||||||
OpenStackSource::class,
|
OpenStackSource::class,
|
||||||
RackspaceSource::class
|
RackspaceSource::class
|
||||||
];
|
];
|
||||||
|
@ -23,7 +23,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'];
|
$this->encryptedFields = ['password', 'access_key', 'secret_key', 'access_token'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +89,8 @@ class StorageController extends Controller
|
|||||||
'cdn_url',
|
'cdn_url',
|
||||||
'access_key',
|
'access_key',
|
||||||
'secret_key',
|
'secret_key',
|
||||||
'b2_bucket_type'
|
'b2_bucket_type',
|
||||||
|
'access_token'
|
||||||
]));
|
]));
|
||||||
$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');
|
||||||
@ -219,7 +220,8 @@ class StorageController extends Controller
|
|||||||
'cdn_url',
|
'cdn_url',
|
||||||
'access_key',
|
'access_key',
|
||||||
'secret_key',
|
'secret_key',
|
||||||
'b2_bucket_type'
|
'b2_bucket_type',
|
||||||
|
'access_token'
|
||||||
]));
|
]));
|
||||||
$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');
|
||||||
|
@ -65,6 +65,18 @@ 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['access_key'] = 'sometimes|required';
|
||||||
|
$result['secret_key'] = 'sometimes|required';
|
||||||
|
$result['access_token'] = 'sometimes|required';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -103,6 +115,18 @@ 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['access_key'] = 'sometimes|required';
|
||||||
|
$result['secret_key'] = 'sometimes|required';
|
||||||
|
$result['access_token'] = 'sometimes|required';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
157
app/Services/DropboxService.php
Normal file
157
app/Services/DropboxService.php
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
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 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
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $accessToken
|
||||||
|
*/
|
||||||
|
public function setAccessToken(string $accessToken)
|
||||||
|
{
|
||||||
|
$this->accessToken = $accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uploadFile($pathToFileToUpload, $pathToStorage)
|
||||||
|
{
|
||||||
|
$dropboxArgs = [
|
||||||
|
'path' => $pathToStorage,
|
||||||
|
'mode' => 'add',
|
||||||
|
'mute' => true
|
||||||
|
];
|
||||||
|
|
||||||
|
$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
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($httpCode != 200 && $httpCode != 304)
|
||||||
|
{
|
||||||
|
throw new \Exception(sprintf('Exception from Dropbox: %s', $result));
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
return $postOptions['response_body_is_json']
|
||||||
|
? json_decode($result)
|
||||||
|
: $result;
|
||||||
|
}
|
||||||
|
}
|
@ -31,7 +31,8 @@ class Storage extends Model
|
|||||||
'cdn_url',
|
'cdn_url',
|
||||||
'access_key',
|
'access_key',
|
||||||
'secret_key',
|
'secret_key',
|
||||||
'b2_bucket_type'
|
'b2_bucket_type',
|
||||||
|
'access_token'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function albums()
|
public function albums()
|
||||||
|
@ -19,6 +19,11 @@ return [
|
|||||||
'download_token_lifetime' => 300
|
'download_token_lifetime' => 300
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'dropbox' => [
|
||||||
|
'download_url' => 'https://content.dropboxapi.com/2/files/download',
|
||||||
|
'upload_url' => 'https://content.dropboxapi.com/2/files/upload'
|
||||||
|
],
|
||||||
|
|
||||||
'gitea' => [
|
'gitea' => [
|
||||||
'api_url' => 'https://apps.andysh.uk/api/v1',
|
'api_url' => 'https://apps.andysh.uk/api/v1',
|
||||||
'cache_time_seconds' => 3600,
|
'cache_time_seconds' => 3600,
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddStorageAccessTokenColumn extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('storages', function (Blueprint $table) {
|
||||||
|
$table->text('access_token')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('storages', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('access_token');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -96,10 +96,12 @@ return [
|
|||||||
'settings_social_user_profiles' => 'Enable public user profiles',
|
'settings_social_user_profiles' => 'Enable public user profiles',
|
||||||
'settings_social_user_profiles_help' => 'Display public pages for users showing their albums, cameras used and activity.',
|
'settings_social_user_profiles_help' => 'Display public pages for users showing their albums, cameras used and activity.',
|
||||||
'storage_access_key_label' => 'Access key:',
|
'storage_access_key_label' => 'Access key:',
|
||||||
|
'storage_access_token_label' => 'Access token:',
|
||||||
'storage_active_label' => 'Location is active. Uncheck to prevent creating new albums in this location.',
|
'storage_active_label' => 'Location is active. Uncheck to prevent creating new albums in this location.',
|
||||||
'storage_api_key_label' => 'API key:',
|
'storage_api_key_label' => 'API key:',
|
||||||
'storage_application_key_id_label' => 'Application key ID:',
|
'storage_application_key_id_label' => 'Application key ID:',
|
||||||
'storage_application_key_label' => 'Application key:',
|
'storage_application_key_label' => 'Application key:',
|
||||||
|
'storage_application_secret_label' => 'Application secret:',
|
||||||
'storage_auth_url_label' => 'Authentication URL:',
|
'storage_auth_url_label' => 'Authentication URL:',
|
||||||
'storage_b2_bucket_type' => [
|
'storage_b2_bucket_type' => [
|
||||||
'autodetect' => 'Auto-detect',
|
'autodetect' => 'Auto-detect',
|
||||||
|
@ -3,6 +3,7 @@ return [
|
|||||||
'album_sources' => [
|
'album_sources' => [
|
||||||
'amazon_s3' => 'Amazon S3 (or S3-compatible)',
|
'amazon_s3' => 'Amazon S3 (or S3-compatible)',
|
||||||
'backblaze_b2' => 'Backblaze B2 Cloud',
|
'backblaze_b2' => 'Backblaze B2 Cloud',
|
||||||
|
'dropbox' => 'Dropbox',
|
||||||
'filesystem' => 'Local filesystem',
|
'filesystem' => 'Local filesystem',
|
||||||
'openstack' => 'OpenStack cloud storage',
|
'openstack' => 'OpenStack cloud storage',
|
||||||
'rackspace' => 'Rackspace cloud storage'
|
'rackspace' => 'Rackspace cloud storage'
|
||||||
|
@ -67,6 +67,10 @@
|
|||||||
@include(Theme::viewName('partials.admin_storages_backblaze_b2_options'))
|
@include(Theme::viewName('partials.admin_storages_backblaze_b2_options'))
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="storage_driver == 'DropboxSource'">
|
||||||
|
@include(Theme::viewName('partials.admin_storages_dropbox_options'))
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-check">
|
<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>
|
<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>
|
<label class="form-check-label" for="is-default">@lang('forms.default_storage_label')</label>
|
||||||
|
@ -61,10 +61,15 @@
|
|||||||
@include(Theme::viewName('partials.admin_storages_rackspace_options'))
|
@include(Theme::viewName('partials.admin_storages_rackspace_options'))
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div v-if="storage_driver == 'BackblazeB2Source'">
|
@if ($storage->source == 'BackblazeB2Source')
|
||||||
<hr/>
|
<hr/>
|
||||||
@include(Theme::viewName('partials.admin_storages_backblaze_b2_options'))
|
@include(Theme::viewName('partials.admin_storages_backblaze_b2_options'))
|
||||||
</div>
|
@endif
|
||||||
|
|
||||||
|
@if ($storage->source == 'DropboxSource')
|
||||||
|
<hr/>
|
||||||
|
@include(Theme::viewName('partials.admin_storages_dropbox_options'))
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<a href="{{ route('storage.index') }}" class="btn btn-default">@lang('forms.cancel_action')</a>
|
<a href="{{ route('storage.index') }}" class="btn btn-default">@lang('forms.cancel_action')</a>
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
<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_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) }}">
|
||||||
|
|
||||||
|
@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_secret_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="access-token">@lang('forms.storage_access_token_label')</label>
|
||||||
|
<input type="text" class="form-control{{ $errors->has('access_token') ? ' is-invalid' : '' }}" id="access-token" name="access_token" value="{{ old('access_token', $storage->access_token) }}">
|
||||||
|
|
||||||
|
@if ($errors->has('access_key'))
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
<strong>{{ $errors->first('access_token') }}</strong>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
Reference in New Issue
Block a user