forked from miguel456/rbrecruiter
RSM-8 Add team files page and ability to download files
This commit is contained in:
parent
b8a2a64354
commit
06d1e0ad3f
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Faker\Factory;
|
||||
use Faker\Generator;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class MakeFile extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'files:make {count : How many test files to generate}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generates test files for the TeamFile model. Use in conjunction with it\'s factory.';
|
||||
|
||||
|
||||
/**
|
||||
* The faker instance used to obtain dummy text.
|
||||
*
|
||||
* @var Generator
|
||||
*/
|
||||
private $faker;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->faker = Factory::create();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$count = $this->argument('count');
|
||||
$this->info('Creating ' . $this->argument('count') . ' files!');
|
||||
|
||||
for ($max = 1; $max < $count; $max++)
|
||||
{
|
||||
Storage::disk('local')->put('factory_files/testfile_' . rand(0, 5000) . '.txt', $this->faker->paragraphs(40, true));
|
||||
|
||||
}
|
||||
|
||||
$this->info('Finished creating files! They will be randomly picked by the factory.');
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\TeamFile;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use League\Flysystem\FileNotFoundException;
|
||||
|
||||
class TeamFileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
if (is_null(Auth::user()->currentTeam))
|
||||
{
|
||||
$request->session()->flash('error', 'Please choose a team before viewing it\'s files.');
|
||||
return redirect()->to(route('teams.index'));
|
||||
}
|
||||
|
||||
return view('dashboard.teams.team-files')
|
||||
->with('files', TeamFile::with('team', 'uploader')->paginate(20));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
public function download(Request $request, TeamFile $teamFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Storage::download('uploads/' . $teamFile->name);
|
||||
}
|
||||
catch (FileNotFoundException $ex)
|
||||
{
|
||||
$request->session()->flash('error', 'Sorry, but the requested file could not be found in storage. Sometimes, files may be physically deleted by admins, but not from the app\'s database.');
|
||||
return redirect()->back();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param \App\TeamFile $teamFile
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(TeamFile $teamFile)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\TeamFile $teamFile
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(TeamFile $teamFile)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\TeamFile $teamFile
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, TeamFile $teamFile)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\TeamFile $teamFile
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(TeamFile $teamFile)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
|
@ -36,4 +36,10 @@ class Team extends TeamworkTeam
|
|||
{
|
||||
return $this->belongsToMany('App\Vacancy', 'team_has_vacancy');
|
||||
}
|
||||
|
||||
|
||||
public function files()
|
||||
{
|
||||
return $this->hasMany('App\TeamFile', 'team_id');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Mpociot\Teamwork\Traits\UsedByTeams;
|
||||
|
||||
class TeamFile extends Model
|
||||
{
|
||||
use HasFactory, UsedByTeams;
|
||||
|
||||
|
||||
protected $fillable = [
|
||||
'uploaded_by',
|
||||
'team_id',
|
||||
'name',
|
||||
'fs_location',
|
||||
'extension'
|
||||
];
|
||||
|
||||
public function uploader()
|
||||
{
|
||||
return $this->belongsTo('App\User', 'uploaded_by', 'id');
|
||||
}
|
||||
|
||||
public function team()
|
||||
{
|
||||
return $this->belongsTo('App\Team');
|
||||
}
|
||||
}
|
10
app/User.php
10
app/User.php
|
@ -60,7 +60,8 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
'email_verified_at' => 'datetime',
|
||||
];
|
||||
|
||||
//
|
||||
// RELATIONSHIPS
|
||||
|
||||
public function applications()
|
||||
{
|
||||
return $this->hasMany('App\Application', 'applicantUserID', 'id');
|
||||
|
@ -86,6 +87,13 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
return $this->hasMany('App\Comment', 'authorID', 'id');
|
||||
}
|
||||
|
||||
public function files()
|
||||
{
|
||||
return $this->hasMany('App\TeamFile', 'uploaded_by');
|
||||
}
|
||||
|
||||
// UTILITY LOGIC
|
||||
|
||||
public function isBanned()
|
||||
{
|
||||
return ! $this->bans()->get()->isEmpty();
|
||||
|
|
|
@ -607,6 +607,8 @@ return [
|
|||
'location' => 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.15/css/bootstrap-multiselect.css',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
[
|
||||
'name' => 'DropzoneJS',
|
||||
'active' => true,
|
||||
|
@ -624,5 +626,4 @@ return [
|
|||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\TeamFile;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class TeamFileFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = TeamFile::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$prefix = Storage::disk('local')->getAdapter()->getPathPrefix();
|
||||
|
||||
return [
|
||||
'uploaded_by' => rand(1, 10), // Also assuming that the user seeder has ran before
|
||||
'team_id' => rand(1, 3), // Assuming you create 3 teams beforehand
|
||||
'name' => $this->faker->file($prefix . 'factory_files', $prefix . 'uploads', false),
|
||||
'caption' => $this->faker->sentence(),
|
||||
'description' => $this->faker->paragraphs(3, true),
|
||||
'fs_location' => $this->faker->file($prefix . 'factory_files', $prefix . 'uploads'),
|
||||
'extension' => 'txt',
|
||||
'size' => rand(1, 1000) // random fake size between 0 bytes and 1 mb
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateTeamFilesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('team_files', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->bigInteger('uploaded_by')->unsigned()->index();
|
||||
$table->integer('team_id')->unsigned()->index();
|
||||
$table->string('name');
|
||||
$table->string('fs_location'); // filesystem location
|
||||
$table->string('extension');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('uploaded_by')
|
||||
->references('id')
|
||||
->on('users')
|
||||
->cascadeOnDelete()
|
||||
->cascadeOnUpdate();
|
||||
|
||||
$table->foreign('team_id')
|
||||
->references('id')
|
||||
->on('teams')
|
||||
->cascadeOnDelete()
|
||||
->cascadeOnUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('team_files');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddDetailsToTeamFiles extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('team_files', function (Blueprint $table) {
|
||||
$table->integer('size')->nullable()->after('extension');
|
||||
$table->string('caption')->nullable()->after('name');
|
||||
$table->mediumText('description')->nullable()->after('caption');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('team_files', function (Blueprint $table) {
|
||||
$table->dropColumn('size');
|
||||
$table->dropColumn('caption');
|
||||
$table->dropColumn('description');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -35,5 +35,8 @@ class DatabaseSeeder extends Seeder
|
|||
$this->call(PermissionSeeder::class);
|
||||
$this->call(UserSeeder::class);
|
||||
$this->call(DefaultOptionsSeeder::class);
|
||||
$this->call(NewPermissions::class);
|
||||
$this->call(TeamSeeder::class);
|
||||
$this->call(TeamFileSeeder::class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\TeamFile;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class TeamFileSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
TeamFile::factory()->count(50)->create();
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
|
@ -0,0 +1,113 @@
|
|||
@extends('adminlte::page')
|
||||
|
||||
@section('title', config('app.name') . ' | Team Files')
|
||||
|
||||
@section('content_header')
|
||||
<h1>{{config('app.name')}} / Teams / Files</h1>
|
||||
@stop
|
||||
|
||||
@section('js')
|
||||
|
||||
<x-global-errors></x-global-errors>
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
|
||||
<x-modal id="upload-dropzone" modal-label="upload-dropzone-modal" modal-title="Upload Files" include-close-button="true">
|
||||
|
||||
<form class="dropzone" id="teamFile" action="{{route('uploadTeamFile')}}"></form>
|
||||
|
||||
<x-slot name="modalFooter">
|
||||
|
||||
</x-slot>
|
||||
</x-modal>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-3 offset-3">
|
||||
<img src="/img/files.svg" width="230px" height="230px" alt="Team files illustration">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<div class="card bg-gray-dark">
|
||||
|
||||
<div class="card-header bg-indigo">
|
||||
<div class="card-title"><h4 class="text-bold">Team Files <span class="badge badge-warning"><i class="fas fa-check-circle"></i> {{ (Auth::user()->currentTeam) ? Auth::user()->currentTeam->name : '(No team)' }}</span></h4></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
@if(!$files->isEmpty())
|
||||
|
||||
<table class="table table-active table-borderless" style="white-space: nowrap">
|
||||
|
||||
<thead>
|
||||
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>File name</th>
|
||||
<th>Caption</th>
|
||||
<th>Size</th>
|
||||
<th>Last updated</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
@foreach($files as $file)
|
||||
|
||||
<tr>
|
||||
<td>{{$file->id}}</td>
|
||||
<td>{{ Str::of($file->name)->limit(10, '(..).' . $file->extension) }}</td>
|
||||
<td>{{ Str::of($file->caption)->limit(10) }}</td>
|
||||
<td>{{ $file->size }} bytes</td>
|
||||
<td>{{ $file->updated_at }}</td>
|
||||
<td>
|
||||
<button rel="buttonTxtTooltip" data-toggle="tooltip" data-placement="top" title="Download" type="button" class="btn btn-success btn-sm ml-3" onclick="window.location='{{route('downloadTeamFile', ['teamFile' => $file->id])}}'"><i class="fas fa-download"></i></button>
|
||||
<button rel="buttonTxtTooltip" data-toggle="tooltip" data-placement="top" title="View" type="button" class="btn btn-success btn-sm ml-3"><i class="fas fa-eye"></i></button>
|
||||
<button rel="buttonTxtTooltip" data-toggle="tooltip" data-placement="top" title="Delete File" type="button" class="btn btn-danger btn-sm ml-3"><i class="fas fa-trash"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@endforeach
|
||||
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
@else
|
||||
|
||||
<div class="alert alert-warning">
|
||||
|
||||
<span class="text-bold"><i class="fas fa-exclamation-triangle"></i> There are currently no team files. Try uploading some to get started.</span>
|
||||
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-center">
|
||||
<button type="button" class="btn btn-warning" onclick="$('#upload-dropzone').modal('show')"><i class="fas fa-upload"></i> Upload Files</button>
|
||||
{{ $files->links() }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@stop
|
||||
|
||||
@section('footer')
|
||||
@include('breadcrumbs.dashboard.footer')
|
||||
@stop
|
|
@ -31,6 +31,7 @@ use App\Http\Controllers\FormController;
|
|||
use App\Http\Controllers\HomeController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\TeamController;
|
||||
use App\Http\Controllers\TeamFileController;
|
||||
use App\Http\Controllers\UserController;
|
||||
use App\Http\Controllers\VacancyController;
|
||||
use App\Http\Controllers\VoteController;
|
||||
|
@ -86,6 +87,17 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['lo
|
|||
Route::get('teams/invites/{action}/{token}', [TeamController::class, 'processInviteAction'])
|
||||
->name('processInvite');
|
||||
|
||||
|
||||
Route::get('team/files', [TeamFileController::class, 'index'])
|
||||
->name('showTeamFiles');
|
||||
|
||||
Route::post('team/files/upload', [TeamFileController::class, 'store'])
|
||||
->name('uploadTeamFile');
|
||||
|
||||
Route::get('team/files/{teamFile}/download', [TeamFileController::class, 'download'])
|
||||
->name('downloadTeamFile');
|
||||
|
||||
|
||||
Route::group(['prefix' => '/applications'], function () {
|
||||
Route::get('/my-applications', [ApplicationController::class, 'showUserApps'])
|
||||
->name('showUserApps')
|
||||
|
|
Loading…
Reference in New Issue