Browse Source

added feature: jobs can now have file attachments

pull/43/head
Damilola Olowookere 7 years ago
parent
commit
b28a104662
  1. 7
      app/Entities/Projects/Job.php
  2. 13
      app/Entities/Projects/JobsRepository.php
  3. 12
      app/Http/Controllers/JobsController.php
  4. 19
      app/Http/Controllers/Projects/FilesController.php
  5. 150
      config/medialibrary.php
  6. 38
      database/migrations/2019_06_29_145856_create_media_table.php
  7. 1
      resources/lang/de/job.php
  8. 1
      resources/lang/en/job.php
  9. 1
      resources/lang/id/job.php
  10. 12
      resources/views/jobs/edit.blade.php
  11. 11
      resources/views/jobs/partials/job-show.blade.php
  12. 5
      routes/web/projects.php

7
app/Entities/Projects/Job.php

@ -2,19 +2,22 @@
namespace App\Entities\Projects; namespace App\Entities\Projects;
use DB;
use App\Entities\Users\User; use App\Entities\Users\User;
use DB;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Laracasts\Presenter\PresentableTrait; use Laracasts\Presenter\PresentableTrait;
use Spatie\MediaLibrary\HasMedia\HasMedia;
use Spatie\MediaLibrary\HasMedia\HasMediaTrait;
/** /**
* Job Model. * Job Model.
* *
* @author Nafies Luthfi <nafiesl@gmail.com> * @author Nafies Luthfi <nafiesl@gmail.com>
*/ */
class Job extends Model
class Job extends Model implements HasMedia
{ {
use PresentableTrait; use PresentableTrait;
use HasMediaTrait;
/** /**
* @var \App\Entities\Projects\JobPresenter * @var \App\Entities\Projects\JobPresenter

13
app/Entities/Projects/JobsRepository.php

@ -82,8 +82,19 @@ class JobsRepository extends BaseRepository
} }
$jobData['price'] = str_replace('.', '', $jobData['price']); $jobData['price'] = str_replace('.', '', $jobData['price']);
$jobData = collect( $jobData);
$job = $this->requireById($jobId); $job = $this->requireById($jobId);
$job->update($jobData);
if($jobData->has('files')){
foreach ($jobData->get('files') as $file) {
$path = $file->getPathName();
if(is_file($path)){
$job->addMedia($path)->toMediaCollection();
}
}
}
$job->update($jobData->except('files')->toArray());
return $job; return $job;
} }

12
app/Http/Controllers/JobsController.php

@ -2,13 +2,13 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Entities\Projects\Job;
use App\Entities\Projects\Comment; use App\Entities\Projects\Comment;
use App\Entities\Projects\Project;
use App\Entities\Projects\Job;
use App\Entities\Projects\JobsRepository; use App\Entities\Projects\JobsRepository;
use App\Entities\Projects\Project;
use App\Http\Requests\Jobs\DeleteRequest; use App\Http\Requests\Jobs\DeleteRequest;
use App\Http\Requests\Jobs\UpdateRequest; use App\Http\Requests\Jobs\UpdateRequest;
use Illuminate\Http\Request;
/** /**
* Jobs Controller. * Jobs Controller.
@ -92,11 +92,13 @@ class JobsController extends Controller
*/ */
public function edit(Job $job) public function edit(Job $job)
{ {
$files = $job->getMedia();
$this->authorize('view', $job); $this->authorize('view', $job);
$workers = $this->repo->getWorkersList(); $workers = $this->repo->getWorkersList();
return view('jobs.edit', compact('job', 'workers'));
return view('jobs.edit', compact('job', 'workers', 'files'));
} }
/** /**

19
app/Http/Controllers/Projects/FilesController.php

@ -2,10 +2,10 @@
namespace App\Http\Controllers\Projects; namespace App\Http\Controllers\Projects;
use File as FileSystem;
use Illuminate\Http\Request;
use App\Entities\Projects\File; use App\Entities\Projects\File;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use File as FileSystem;
use Illuminate\Http\Request;
/** /**
* Project Files Controller. * Project Files Controller.
@ -125,4 +125,19 @@ class FilesController extends Controller
{ {
return strtolower((new \ReflectionClass($modelName))->getShortName()); return strtolower((new \ReflectionClass($modelName))->getShortName());
} }
public function showAttachment(Request $request, \App\Entities\Projects\Job $job, $media_id)
{
$files = $job->getMedia();
foreach ($files as $file) {
if ($file->id == $media_id) {
return \Response::file($file->getPath());
}
}
return back()->withErrors(['media not found']);
}
} }

150
config/medialibrary.php

@ -0,0 +1,150 @@
<?php
return [
/*
* The disk on which to store added files and derived images by default. Choose
* one or more of the disks you've configured in config/filesystems.php.
*/
'disk_name' => 'public',
/*
* The maximum file size of an item in bytes.
* Adding a larger file will result in an exception.
*/
'max_file_size' => 1024 * 1024 * 10,
/*
* This queue will be used to generate derived and responsive images.
* Leave empty to use the default queue.
*/
'queue_name' => '',
/*
* The fully qualified class name of the media model.
*/
'media_model' => Spatie\MediaLibrary\Models\Media::class,
's3' => [
/*
* The domain that should be prepended when generating urls.
*/
'domain' => 'https://'.env('AWS_BUCKET').'.s3.amazonaws.com',
],
'remote' => [
/*
* Any extra headers that should be included when uploading media to
* a remote disk. Even though supported headers may vary between
* different drivers, a sensible default has been provided.
*
* Supported by S3: CacheControl, Expires, StorageClass,
* ServerSideEncryption, Metadata, ACL, ContentEncoding
*/
'extra_headers' => [
'CacheControl' => 'max-age=604800',
],
],
'responsive_images' => [
/*
* This class is responsible for calculating the target widths of the responsive
* images. By default we optimize for filesize and create variations that each are 20%
* smaller than the previous one. More info in the documentation.
*
* https://docs.spatie.be/laravel-medialibrary/v7/advanced-usage/generating-responsive-images
*/
'width_calculator' => Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\FileSizeOptimizedWidthCalculator::class,
/*
* By default rendering media to a responsive image will add some javascript and a tiny placeholder.
* This ensures that the browser can already determine the correct layout.
*/
'use_tiny_placeholders' => true,
/*
* This class will generate the tiny placeholder used for progressive image loading. By default
* the medialibrary will use a tiny blurred jpg image.
*/
'tiny_placeholder_generator' => Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\Blurred::class,
],
/*
* When urls to files get generated, this class will be called. Leave empty
* if your files are stored locally above the site root or on s3.
*/
'url_generator' => null,
/*
* The class that contains the strategy for determining a media file's path.
*/
'path_generator' => null,
/*
* Medialibrary will try to optimize all converted images by removing
* metadata and applying a little bit of compression. These are
* the optimizers that will be used by default.
*/
'image_optimizers' => [
Spatie\ImageOptimizer\Optimizers\Jpegoptim::class => [
'--strip-all', // this strips out all text information such as comments and EXIF data
'--all-progressive', // this will make sure the resulting image is a progressive one
],
Spatie\ImageOptimizer\Optimizers\Pngquant::class => [
'--force', // required parameter for this package
],
Spatie\ImageOptimizer\Optimizers\Optipng::class => [
'-i0', // this will result in a non-interlaced, progressive scanned image
'-o2', // this set the optimization level to two (multiple IDAT compression trials)
'-quiet', // required parameter for this package
],
Spatie\ImageOptimizer\Optimizers\Svgo::class => [
'--disable=cleanupIDs', // disabling because it is known to cause troubles
],
Spatie\ImageOptimizer\Optimizers\Gifsicle::class => [
'-b', // required parameter for this package
'-O3', // this produces the slowest but best results
],
],
/*
* These generators will be used to create an image of media files.
*/
'image_generators' => [
Spatie\MediaLibrary\ImageGenerators\FileTypes\Image::class,
Spatie\MediaLibrary\ImageGenerators\FileTypes\Webp::class,
Spatie\MediaLibrary\ImageGenerators\FileTypes\Pdf::class,
Spatie\MediaLibrary\ImageGenerators\FileTypes\Svg::class,
Spatie\MediaLibrary\ImageGenerators\FileTypes\Video::class,
],
/*
* The engine that should perform the image conversions.
* Should be either `gd` or `imagick`.
*/
'image_driver' => 'gd',
/*
* FFMPEG & FFProbe binaries paths, only used if you try to generate video
* thumbnails and have installed the php-ffmpeg/php-ffmpeg composer
* dependency.
*/
'ffmpeg_path' => env('FFMPEG_PATH', '/usr/bin/ffmpeg'),
'ffprobe_path' => env('FFPROBE_PATH', '/usr/bin/ffprobe'),
/*
* The path where to store temporary files while performing image conversions.
* If set to null, storage_path('medialibrary/temp') will be used.
*/
'temporary_directory_path' => null,
/*
* Here you can override the class names of the jobs used by this package. Make sure
* your custom jobs extend the ones provided by the package.
*/
'jobs' => [
'perform_conversions' => Spatie\MediaLibrary\Jobs\PerformConversions::class,
'generate_responsive_images' => Spatie\MediaLibrary\Jobs\GenerateResponsiveImages::class,
],
];

38
database/migrations/2019_06_29_145856_create_media_table.php

@ -0,0 +1,38 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMediaTable extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::create('media', function (Blueprint $table) {
$table->increments('id');
$table->morphs('model');
$table->string('collection_name');
$table->string('name');
$table->string('file_name');
$table->string('mime_type')->nullable();
$table->string('disk');
$table->unsignedInteger('size');
$table->json('manipulations');
$table->json('custom_properties');
$table->json('responsive_images');
$table->unsignedInteger('order_column')->nullable();
$table->nullableTimestamps();
});
}
/**
* Reverse the migrations.
*/
public function down()
{
Schema::dropIfExists('media');
}
}

1
resources/lang/de/job.php

@ -46,6 +46,7 @@ return [
'target_end_date' => 'Target End Date', 'target_end_date' => 'Target End Date',
'actual_start_date' => 'Actual Start Date', 'actual_start_date' => 'Actual Start Date',
'actual_end_date' => 'Actual End Date', 'actual_end_date' => 'Actual End Date',
'files' => 'An diesen Job angehängte Dateien',
// Types // Types
'main' => 'Haupt', 'main' => 'Haupt',

1
resources/lang/en/job.php

@ -48,6 +48,7 @@ return [
'target_end_date' => 'Target End Date', 'target_end_date' => 'Target End Date',
'actual_start_date' => 'Actual Start Date', 'actual_start_date' => 'Actual Start Date',
'actual_end_date' => 'Actual End Date', 'actual_end_date' => 'Actual End Date',
'files' => 'Files attached to this job',
// Types // Types
'main' => 'Main', 'main' => 'Main',

1
resources/lang/id/job.php

@ -48,6 +48,7 @@ return [
'target_end_date' => 'Target Tgl Selesai', 'target_end_date' => 'Target Tgl Selesai',
'actual_start_date' => 'Tgl Mulai Aktual', 'actual_start_date' => 'Tgl Mulai Aktual',
'actual_end_date' => 'Tgl Selesai Aktual', 'actual_end_date' => 'Tgl Selesai Aktual',
'files' => 'File terlampir pada pekerjaan ini',
// Types // Types
'main' => 'Utama', 'main' => 'Utama',

12
resources/views/jobs/edit.blade.php

@ -5,7 +5,7 @@
@section('content') @section('content')
<div class="row"><br> <div class="row"><br>
<div class="col-md-6"> <div class="col-md-6">
{!! Form::model($job, ['route' => ['jobs.update', $job], 'method' => 'patch']) !!}
{!! Form::model($job, ['route' => ['jobs.update', $job], 'method' => 'patch' , 'files' => true] ) !!}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{ $job->name }} <small>{{ __('job.edit') }}</small></h3></div> <div class="panel-heading"><h3 class="panel-title">{{ $job->name }} <small>{{ __('job.edit') }}</small></h3></div>
<div class="panel-body"> <div class="panel-body">
@ -30,6 +30,16 @@
<div class="col-md-4">{!! FormField::text('actual_end_date', ['label' => __('job.actual_end_date'), 'class' => 'date-select']) !!}</div> <div class="col-md-4">{!! FormField::text('actual_end_date', ['label' => __('job.actual_end_date'), 'class' => 'date-select']) !!}</div>
</div> </div>
{!! FormField::textarea('description', ['label' => __('job.description') , 'class' => 'countable']) !!} {!! FormField::textarea('description', ['label' => __('job.description') , 'class' => 'countable']) !!}
@if ($files->isNotEmpty())
<b>
{{$files->count()}} files
</b>
@else
no files attached yet
@endif
{!! Form::file('files[]', ['label' => __('job.files') , 'multiple' => 'true']) !!}
</div> </div>
<div class="panel-footer"> <div class="panel-footer">

11
resources/views/jobs/partials/job-show.blade.php

@ -11,6 +11,17 @@
<tr><th>{{ __('job.worker') }}</th><td>{{ $job->worker->name }}</td></tr> <tr><th>{{ __('job.worker') }}</th><td>{{ $job->worker->name }}</td></tr>
<tr><th>{{ __('time.updated_at') }}</th><td>{{ $job->updated_at }}</td></tr> <tr><th>{{ __('time.updated_at') }}</th><td>{{ $job->updated_at }}</td></tr>
<tr><th>{{ __('job.description') }}</th><td>{!! nl2br($job->description) !!}</td></tr> <tr><th>{{ __('job.description') }}</th><td>{!! nl2br($job->description) !!}</td></tr>
<tr>
<th>{{ __('job.files') }}</th>
<td>
@if ($job->hasMedia())
@foreach ($job->getMedia() as $item)
<a target="_blank" href="{{route('show-job-file', ['job' => $job->id , 'media_id' => $item->id])}}">{{$item->name}}</a>
@endforeach
@else
@endif
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>

5
routes/web/projects.php

@ -73,6 +73,11 @@ Route::group(['middleware' => ['auth'], 'namespace' => 'Projects'], function ()
Route::get('files/{file}', ['as' => 'files.download', 'uses' => 'FilesController@show']); Route::get('files/{file}', ['as' => 'files.download', 'uses' => 'FilesController@show']);
Route::patch('files/{file}', ['as' => 'files.update', 'uses' => 'FilesController@update']); Route::patch('files/{file}', ['as' => 'files.update', 'uses' => 'FilesController@update']);
Route::delete('files/{file}', ['as' => 'files.destroy', 'uses' => 'FilesController@destroy']); Route::delete('files/{file}', ['as' => 'files.destroy', 'uses' => 'FilesController@destroy']);
/**
* Job file attachments
*/
Route::get('files/show/{job}/{media_id}', 'FilesController@showAttachment')->name('show-job-file');
}); });
Route::group(['middleware' => ['auth']], function () { Route::group(['middleware' => ['auth']], function () {

Loading…
Cancel
Save