Skip to content

Commit 77fc918

Browse files
authored
Merge pull request #5 from bnbwebexpertise/shareable
adds creation of shareable links with inbuilt expiry mechanism
2 parents 8958294 + b4338b9 commit 77fc918

File tree

7 files changed

+79
-0
lines changed

7 files changed

+79
-0
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,12 @@ public function boot()
277277
}
278278
```
279279

280+
## Temporary URLs
281+
It is possible to generate a unique temporary URL for downloading the attachments via the `getTemporaryUrl` method of the `Attachment` model, for sharing purposes foremost.
282+
The `getTemporaryUrl` method has one parameter : a `Carbon` date, after which the link will no longer be valid.
283+
284+
The default generated URL is of the form : `http://example.com/attachments/shared/<a very long string>`. The share path can be modified in the config file under the `shared_pattern` key.
285+
280286
## Cleanup commands
281287

282288
A command is provided to cleanup the attachments not bound to a model

config/attachments.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
'prefix' => 'attachments',
1616
'middleware' => 'web',
1717
'pattern' => '/{id}/{name}',
18+
'shared_pattern' => '/shared/{token}',
1819
'dropzone' => [
1920
'upload_pattern' => '/dropzone',
2021
'delete_pattern' => '/dropzone/{id}',

routes/web.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
'prefix' => config('attachments.routes.prefix'),
55
'middleware' => config('attachments.routes.middleware')
66
], function () {
7+
Route::get(config('attachments.routes.shared_pattern'), 'Bnb\Laravel\Attachments\Http\Controllers\ShareController@download')
8+
->where('token', '.+')
9+
->where('id', '[a-zA-Z0-9-]+')
10+
->where('name', '.+')
11+
->name('attachments.download-shared');
712
Route::get(config('attachments.routes.pattern'), 'Bnb\Laravel\Attachments\Http\Controllers\DownloadController@download')
813
->where('id', '[a-zA-Z0-9-]+')
914
->where('name', '.+')

src/Attachment.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
namespace Bnb\Laravel\Attachments;
44

5+
use Carbon\Carbon;
56
use File as FileHelper;
67
use Illuminate\Database\Eloquent\Model;
78
use Storage;
89
use Symfony\Component\HttpFoundation\File\File as FileObj;
910
use Symfony\Component\HttpFoundation\File\UploadedFile;
11+
use Crypt;
1012

1113
/**
1214
* @property int id
@@ -364,6 +366,27 @@ public function getExtension()
364366
}
365367

366368

369+
/**
370+
* Generate a temporary url at which the current file can be downloaded until $expire
371+
*
372+
* @param Carbon $expire
373+
*
374+
* @return string
375+
*/
376+
public function getTemporaryUrl(Carbon $expire)
377+
{
378+
379+
$payload = Crypt::encryptString(collect([
380+
'id' => $this->uuid,
381+
'expire' => $expire->getTimestamp(),
382+
'shared_at' => Carbon::now()->getTimestamp()
383+
])->toJson());
384+
385+
return route('attachments.download-shared', ['token' => $payload]);
386+
387+
}
388+
389+
367390
/**
368391
* Generates a partition for the file.
369392
* return /ABC/DE1/234 for an name of ABCDE1234.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Bnb\Laravel\Attachments\Http\Controllers;
4+
5+
use Bnb\Laravel\Attachments\Attachment;
6+
use Carbon\Carbon;
7+
use Illuminate\Contracts\Encryption\DecryptException;
8+
use Illuminate\Http\Request;
9+
use Illuminate\Routing\Controller;
10+
use Lang;
11+
use Crypt;
12+
13+
class ShareController extends Controller
14+
{
15+
16+
public function download($token, Request $request)
17+
{
18+
try {
19+
$data = json_decode(Crypt::decryptString($token));
20+
$id = $data->id;
21+
$expire = $data->expire;
22+
23+
} catch(DecryptException $e) {
24+
abort(404, Lang::get('attachments::messages.errors.file_not_found'));
25+
}
26+
27+
if(Carbon::createFromTimestamp($expire)->isPast()) {
28+
abort(403, Lang::get('attachments::messages.errors.expired'));
29+
}
30+
31+
$disposition = ($disposition = $request->input('disposition')) === 'inline' ? $disposition : 'attachment';
32+
33+
if ($file = Attachment::where('uuid', $id)->first()) {
34+
/** @var Attachment $file */
35+
if ( ! $file->output($disposition)) {
36+
abort(403, Lang::get('attachments::messages.errors.access_denied'));
37+
}
38+
}
39+
40+
abort(404, Lang::get('attachments::messages.errors.file_not_found'));
41+
}
42+
}

translations/en/messages.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
'errors' => [
55
'access_denied' => 'Access denied.',
66
'not_found' => 'Not found.',
7+
'expired' => 'The file is no longer available.',
78
'upload_failed' => 'Upload has failed.',
89
'upload_denied' => 'Upload has been denied.',
910
'delete_denied' => 'Resource deletion has been denied.',

translations/fr/messages.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
'errors' => [
55
'access_denied' => 'Vous n’êtes pas autorisé à accéder à la ressource demandée.',
66
'not_found' => 'La ressource demandé est introuvable.',
7+
'expired' => 'Le fichier n’est plus disponible.',
78
'upload_failed' => 'L’envoi de la ressource a échoué.',
89
'upload_denied' => 'L’envoi de la ressource a été refusée.',
910
'delete_denied' => 'La suppression de la ressource a été refusée.',

0 commit comments

Comments
 (0)