Route downloads through controller; file IDs as hash to prevent guessing; download count; download list on airport page
This commit is contained in:
@@ -13,15 +13,19 @@ class CreateFilesTable extends Migration
|
|||||||
public function up()
|
public function up()
|
||||||
{
|
{
|
||||||
Schema::create('files', function (Blueprint $table) {
|
Schema::create('files', function (Blueprint $table) {
|
||||||
$table->increments('id');
|
$table->string('id', \App\Interfaces\Model::ID_MAX_LENGTH);
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('description')->nullable();
|
$table->string('description')->nullable();
|
||||||
$table->string('disk')->nullable();
|
$table->string('disk')->nullable();
|
||||||
$table->string('path');
|
$table->string('path');
|
||||||
$table->boolean('public')->default(true);
|
$table->boolean('public')->default(true);
|
||||||
|
$table->unsignedInteger('download_count')->default(0);
|
||||||
$table->string('ref_model', 50)->nullable();
|
$table->string('ref_model', 50)->nullable();
|
||||||
$table->string('ref_model_id', 36)->nullable();
|
$table->string('ref_model_id', 36)->nullable();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->primary('id');
|
||||||
|
$table->index(['ref_model', 'ref_model_id']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class FilesController extends Controller
|
|||||||
$asset->description = $attrs['file_description'];
|
$asset->description = $attrs['file_description'];
|
||||||
$asset->disk = config('filesystems.public_files');
|
$asset->disk = config('filesystems.public_files');
|
||||||
$asset->path = $file_path;
|
$asset->path = $file_path;
|
||||||
$asset->public = true;
|
$asset->public = false; // need to be logged in to see. default (for now)
|
||||||
$asset->ref_model = $attrs['ref_model'];
|
$asset->ref_model = $attrs['ref_model'];
|
||||||
$asset->ref_model_id = $attrs['ref_model_id'];
|
$asset->ref_model_id = $attrs['ref_model_id'];
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Frontend;
|
||||||
|
|
||||||
|
use App\Interfaces\Controller;
|
||||||
|
use App\Models\File;
|
||||||
|
use Auth;
|
||||||
|
use Flash;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class FileController
|
||||||
|
* @package App\Http\Controllers\Frontend
|
||||||
|
*/
|
||||||
|
class FileController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Show the application dashboard.
|
||||||
|
*/
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var File $file
|
||||||
|
*/
|
||||||
|
$file = File::find($id);
|
||||||
|
if (!$file) {
|
||||||
|
Flash::error('File doesn\'t exist');
|
||||||
|
return redirect()->back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allowed to download? If not, direct to login
|
||||||
|
if (!$file->public && !Auth::check()) {
|
||||||
|
return redirect(config('app.login_redirect'));
|
||||||
|
}
|
||||||
|
|
||||||
|
++$file->download_count;
|
||||||
|
$file->save();
|
||||||
|
|
||||||
|
if($file->disk === 'public') {
|
||||||
|
$storage = Storage::disk('public');
|
||||||
|
return $storage->download($file->path, $file->filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Config for streamed response?
|
||||||
|
return redirect()->to($file->url);
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
-1
@@ -4,23 +4,28 @@ namespace App\Models;
|
|||||||
|
|
||||||
|
|
||||||
use App\Interfaces\Model;
|
use App\Interfaces\Model;
|
||||||
|
use App\Models\Traits\HashIdTrait;
|
||||||
use App\Models\Traits\ReferenceTrait;
|
use App\Models\Traits\ReferenceTrait;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File property
|
|
||||||
* @property string $name
|
* @property string $name
|
||||||
* @property string $description
|
* @property string $description
|
||||||
* @property string $disk
|
* @property string $disk
|
||||||
* @property string $path
|
* @property string $path
|
||||||
* @property boolean $public
|
* @property boolean $public
|
||||||
|
* @property int $download_count
|
||||||
|
* @property string $url
|
||||||
|
* @property string $filename
|
||||||
* @package App\Models
|
* @package App\Models
|
||||||
*/
|
*/
|
||||||
class File extends Model
|
class File extends Model
|
||||||
{
|
{
|
||||||
|
use HashIdTrait;
|
||||||
use ReferenceTrait;
|
use ReferenceTrait;
|
||||||
|
|
||||||
protected $table = 'files';
|
protected $table = 'files';
|
||||||
|
public $incrementing = false;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name',
|
'name',
|
||||||
@@ -32,10 +37,42 @@ class File extends Model
|
|||||||
'ref_model_id',
|
'ref_model_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'public' => 'boolean',
|
||||||
|
];
|
||||||
|
|
||||||
public static $rules = [
|
public static $rules = [
|
||||||
'name' => 'required',
|
'name' => 'required',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private $pathinfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the file extension
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getExtensionAttribute(): string
|
||||||
|
{
|
||||||
|
if (!$this->pathinfo) {
|
||||||
|
$this->pathinfo = pathinfo($this->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pathinfo['extension'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get just the filename
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getFilenameAttribute() :string
|
||||||
|
{
|
||||||
|
if (!$this->pathinfo) {
|
||||||
|
$this->pathinfo = pathinfo($this->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pathinfo['filename'].'.'.$this->pathinfo['extension'];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the full URL to this attribute
|
* Get the full URL to this attribute
|
||||||
* @return string
|
* @return string
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ Route::group([
|
|||||||
|
|
||||||
Route::get('airports/{id}', 'AirportController@show')->name('airports.show');
|
Route::get('airports/{id}', 'AirportController@show')->name('airports.show');
|
||||||
|
|
||||||
|
// Download a file
|
||||||
|
Route::get('files/{id}', 'FileController@show')->name('files.show');
|
||||||
|
|
||||||
Route::get('flights/bids', 'FlightController@bids')->name('flights.bids');
|
Route::get('flights/bids', 'FlightController@bids')->name('flights.bids');
|
||||||
Route::get('flights/search', 'FlightController@search')->name('flights.search');
|
Route::get('flights/search', 'FlightController@search')->name('flights.search');
|
||||||
Route::resource('flights', 'FlightController');
|
Route::resource('flights', 'FlightController');
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ class CheckWx extends Widget
|
|||||||
return $data;
|
return $data;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if($data->results === 1) {
|
if($data->results === 1) {
|
||||||
$data = $data->data[0];
|
$data = $data->data[0];
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ Pass in:
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Name</td>
|
<td>Name</td>
|
||||||
<td>Current File</td>
|
<td>Direct Link</td>
|
||||||
|
<td>Downloads</td>
|
||||||
<td class="text-right"></td>
|
<td class="text-right"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -26,6 +27,7 @@ Pass in:
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ $file->name }}</td>
|
<td>{{ $file->name }}</td>
|
||||||
<td><a href="{{ $file->url }}" target="_blank">Link to file</a></td>
|
<td><a href="{{ $file->url }}" target="_blank">Link to file</a></td>
|
||||||
|
<td>{{$file->download_count}}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{{ Form::open(['route' => ['admin.files.delete', $file->id], 'method' => 'delete']) }}
|
{{ Form::open(['route' => ['admin.files.delete', $file->id], 'method' => 'delete']) }}
|
||||||
{{ Form::hidden('id', $file->id) }}
|
{{ Form::hidden('id', $file->id) }}
|
||||||
|
|||||||
@@ -2,17 +2,19 @@
|
|||||||
@section('title', $airport->full_name)
|
@section('title', $airport->full_name)
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="row">
|
<div class="row" style="margin-bottom: 30px;">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h2 class="description">{{ $airport->full_name }}</h2>
|
<h2 class="description">{{ $airport->full_name }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-5">
|
|
||||||
|
|
||||||
|
{{-- Show the weather widget in one column --}}
|
||||||
|
<div class="col-5">
|
||||||
{{ Widget::checkWx([
|
{{ Widget::checkWx([
|
||||||
'icao' => $airport->icao,
|
'icao' => $airport->icao,
|
||||||
]) }}
|
]) }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- Show the airspace map in the other column --}}
|
||||||
<div class="col-7">
|
<div class="col-7">
|
||||||
{{ Widget::airspaceMap([
|
{{ Widget::airspaceMap([
|
||||||
'width' => '100%',
|
'width' => '100%',
|
||||||
@@ -22,21 +24,47 @@
|
|||||||
]) }}
|
]) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="height: 30px;">
|
<div class="row" style="margin-bottom: 30px;">
|
||||||
|
{{-- There are files uploaded and a user is logged in--}}
|
||||||
|
@if($airport->files && Auth::check())
|
||||||
|
<div class="col-12">
|
||||||
|
<h3 class="description">Downloads</h3>
|
||||||
|
<ul class="list-group">
|
||||||
|
@foreach($airport->files as $file)
|
||||||
|
<li class="list-group-item">
|
||||||
|
<a href="{{route('frontend.files.show', [$file->id])}}" target="_blank">
|
||||||
|
{{ $file->name }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{{-- only show description is one is provided --}}
|
||||||
|
@if($file->description)
|
||||||
|
- {{$file->description}}
|
||||||
|
@endif
|
||||||
|
<span style="margin-left: 20px">{{$file->download_count}} downloads</span>
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<h3 class="description">Inbound Flights</h3>
|
<h3 class="description">Inbound Flights</h3>
|
||||||
@if(!$inbound_flights)
|
@if(!$inbound_flights)
|
||||||
<div class="mini-splash">
|
<div class="jumbotron text-center">
|
||||||
no flights found
|
no flights found
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
@each('airports.table', $inbound_flights, 'flight')
|
@each('airports.table', $inbound_flights, 'flight')
|
||||||
@endif
|
@endif
|
||||||
<h3 class="description">Outbound Flights</h3>
|
<h3 class="description">Outbound Flights</h3>
|
||||||
@each('airports.table', $outbound_flights, 'flight')
|
@if(!$outbound_flights)
|
||||||
|
<div class="jumbotron text-center">
|
||||||
|
no flights found
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
@each('airports.table', $outbound_flights, 'flight')
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
Reference in New Issue
Block a user