wip - upload
This commit is contained in:
parent
e0b9f4e37e
commit
8d74373ad0
43
app/Http/Controllers/Staff/AttachmentController.php
Normal file
43
app/Http/Controllers/Staff/AttachmentController.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Staff;
|
||||
|
||||
use App\Http\Controllers\HashidControllerBase;
|
||||
use App\Models\Enums\Permission;
|
||||
use App\Models\Study;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AttachmentController extends HashidControllerBase
|
||||
{
|
||||
public function upload(Request $request)
|
||||
{
|
||||
abort_unless(auth()->user()->may(Permission::AttachmentUpload), 403);
|
||||
$this->decodeKeys();
|
||||
$study = Study::findOrFail($this->key);
|
||||
|
||||
$request->validate([
|
||||
'file.*' => 'required|mimes:pdf,jpg,png|max:2048',
|
||||
]);
|
||||
|
||||
foreach ($request->file('file') as $file) {
|
||||
$study->addMedia($file)->toMediaCollection('uploads');
|
||||
}
|
||||
|
||||
return response()->json(['success' => 'Files uploaded successfully']);
|
||||
}
|
||||
|
||||
public function delete($mediaId)
|
||||
{
|
||||
abort_unless(auth()->user()->may(Permission::AttachmentUpload), 403);
|
||||
$this->decodeKeys();
|
||||
$study = Study::findOrFail($this->key);
|
||||
$media = $study->getMedia('uploads')->where('id', (int) $mediaId)->first();
|
||||
if ($media) {
|
||||
$media->delete();
|
||||
|
||||
return redirect()->back()->with('success', 'File deleted successfully');
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', 'File not found');
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
use App\Http\Controllers\HashidControllerBase;
|
||||
use App\Http\Requests\StudyHistoryRequest;
|
||||
use App\Models\Enums\Permission;
|
||||
use App\Models\Study;
|
||||
use App\Models\StudyDetails;
|
||||
|
||||
class StudyHistoryController extends HashidControllerBase
|
||||
@ -14,8 +15,9 @@ public function view()
|
||||
abort_unless(auth()->user()->may(Permission::StudyHistoryView), 403);
|
||||
$this->decodeKeys();
|
||||
$details = StudyDetails::historyOnly($this->key);
|
||||
$study = Study::findOrFail($this->key);
|
||||
|
||||
return view('staff.history.view', compact('details'));
|
||||
return view('staff.history.view', compact('details', 'study'));
|
||||
}
|
||||
|
||||
public function edit()
|
||||
@ -23,8 +25,9 @@ public function edit()
|
||||
abort_unless(auth()->user()->may(Permission::StudyHistoryEdit), 403);
|
||||
$this->decodeKeys();
|
||||
$details = StudyDetails::historyOnly($this->key);
|
||||
$study = Study::findOrFail($this->key);
|
||||
|
||||
return view('staff.history.edit', compact('details'));
|
||||
return view('staff.history.edit', compact('details', 'study'));
|
||||
}
|
||||
|
||||
public function save(StudyHistoryRequest $request)
|
||||
|
@ -13,10 +13,13 @@
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
|
||||
class Study extends BaseModel
|
||||
class Study extends BaseModel implements HasMedia
|
||||
{
|
||||
use HashableId;
|
||||
use InteractsWithMedia;
|
||||
|
||||
public function details(): HasOne
|
||||
{
|
||||
|
@ -23,6 +23,7 @@
|
||||
"rap2hpoutre/fast-excel": "^5.5",
|
||||
"rawilk/laravel-settings": "^3.4",
|
||||
"sentry/sentry-laravel": "^4.10",
|
||||
"spatie/laravel-medialibrary": "^11.11",
|
||||
"spatie/laravel-permission": "^6.10",
|
||||
"vinkla/hashids": "^12.0",
|
||||
"yajra/laravel-datatables": "^11.0"
|
||||
|
280
config/media-library.php
Normal file
280
config/media-library.php
Normal file
@ -0,0 +1,280 @@
|
||||
<?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' => env('MEDIA_DISK', '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, // 10MB
|
||||
|
||||
/*
|
||||
* This queue connection will be used to generate derived and responsive images.
|
||||
* Leave empty to use the default queue connection.
|
||||
*/
|
||||
'queue_connection_name' => env('QUEUE_CONNECTION', 'sync'),
|
||||
|
||||
/*
|
||||
* This queue will be used to generate derived and responsive images.
|
||||
* Leave empty to use the default queue.
|
||||
*/
|
||||
'queue_name' => env('MEDIA_QUEUE', ''),
|
||||
|
||||
/*
|
||||
* By default all conversions will be performed on a queue.
|
||||
*/
|
||||
'queue_conversions_by_default' => env('QUEUE_CONVERSIONS_BY_DEFAULT', true),
|
||||
|
||||
/*
|
||||
* Should database transactions be run after database commits?
|
||||
*/
|
||||
'queue_conversions_after_database_commit' => env('QUEUE_CONVERSIONS_AFTER_DB_COMMIT', true),
|
||||
|
||||
/*
|
||||
* The fully qualified class name of the media model.
|
||||
*/
|
||||
'media_model' => Spatie\MediaLibrary\MediaCollections\Models\Media::class,
|
||||
|
||||
/*
|
||||
* When enabled, media collections will be serialised using the default
|
||||
* laravel model serialization behaviour.
|
||||
*
|
||||
* Keep this option disabled if using Media Library Pro components (https://medialibrary.pro)
|
||||
*/
|
||||
'use_default_collection_serialization' => false,
|
||||
|
||||
/*
|
||||
* The fully qualified class name of the model used for temporary uploads.
|
||||
*
|
||||
* This model is only used in Media Library Pro (https://medialibrary.pro)
|
||||
*/
|
||||
'temporary_upload_model' => Spatie\MediaLibraryPro\Models\TemporaryUpload::class,
|
||||
|
||||
/*
|
||||
* When enabled, Media Library Pro will only process temporary uploads that were uploaded
|
||||
* in the same session. You can opt to disable this for stateless usage of
|
||||
* the pro components.
|
||||
*/
|
||||
'enable_temporary_uploads_session_affinity' => true,
|
||||
|
||||
/*
|
||||
* When enabled, Media Library pro will generate thumbnails for uploaded file.
|
||||
*/
|
||||
'generate_thumbnails_for_temporary_uploads' => true,
|
||||
|
||||
/*
|
||||
* This is the class that is responsible for naming generated files.
|
||||
*/
|
||||
'file_namer' => Spatie\MediaLibrary\Support\FileNamer\DefaultFileNamer::class,
|
||||
|
||||
/*
|
||||
* The class that contains the strategy for determining a media file's path.
|
||||
*/
|
||||
'path_generator' => Spatie\MediaLibrary\Support\PathGenerator\DefaultPathGenerator::class,
|
||||
|
||||
/*
|
||||
* The class that contains the strategy for determining how to remove files.
|
||||
*/
|
||||
'file_remover_class' => Spatie\MediaLibrary\Support\FileRemover\DefaultFileRemover::class,
|
||||
|
||||
/*
|
||||
* Here you can specify which path generator should be used for the given class.
|
||||
*/
|
||||
'custom_path_generators' => [
|
||||
// Model::class => PathGenerator::class
|
||||
// or
|
||||
// 'model_morph_alias' => PathGenerator::class
|
||||
],
|
||||
|
||||
/*
|
||||
* When urls to files get generated, this class will be called. Use the default
|
||||
* if your files are stored locally above the site root or on s3.
|
||||
*/
|
||||
'url_generator' => Spatie\MediaLibrary\Support\UrlGenerator\DefaultUrlGenerator::class,
|
||||
|
||||
/*
|
||||
* Moves media on updating to keep path consistent. Enable it only with a custom
|
||||
* PathGenerator that uses, for example, the media UUID.
|
||||
*/
|
||||
'moves_media_on_update' => false,
|
||||
|
||||
/*
|
||||
* Whether to activate versioning when urls to files get generated.
|
||||
* When activated, this attaches a ?v=xx query string to the URL.
|
||||
*/
|
||||
'version_urls' => false,
|
||||
|
||||
/*
|
||||
* The media library 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 => [
|
||||
'-m85', // set maximum quality to 85%
|
||||
'--force', // ensure that progressive generation is always done also if a little bigger
|
||||
'--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
|
||||
],
|
||||
Spatie\ImageOptimizer\Optimizers\Cwebp::class => [
|
||||
'-m 6', // for the slowest compression method in order to get the best compression.
|
||||
'-pass 10', // for maximizing the amount of analysis pass.
|
||||
'-mt', // multithreading for some speed improvements.
|
||||
'-q 90', // quality factor that brings the least noticeable changes.
|
||||
],
|
||||
Spatie\ImageOptimizer\Optimizers\Avifenc::class => [
|
||||
'-a cq-level=23', // constant quality level, lower values mean better quality and greater file size (0-63).
|
||||
'-j all', // number of jobs (worker threads, "all" uses all available cores).
|
||||
'--min 0', // min quantizer for color (0-63).
|
||||
'--max 63', // max quantizer for color (0-63).
|
||||
'--minalpha 0', // min quantizer for alpha (0-63).
|
||||
'--maxalpha 63', // max quantizer for alpha (0-63).
|
||||
'-a end-usage=q', // rate control mode set to Constant Quality mode.
|
||||
'-a tune=ssim', // SSIM as tune the encoder for distortion metric.
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
* These generators will be used to create an image of media files.
|
||||
*/
|
||||
'image_generators' => [
|
||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Image::class,
|
||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Webp::class,
|
||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Avif::class,
|
||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Pdf::class,
|
||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Svg::class,
|
||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Video::class,
|
||||
],
|
||||
|
||||
/*
|
||||
* The path where to store temporary files while performing image conversions.
|
||||
* If set to null, storage_path('media-library/temp') will be used.
|
||||
*/
|
||||
'temporary_directory_path' => null,
|
||||
|
||||
/*
|
||||
* The engine that should perform the image conversions.
|
||||
* Should be either `gd` or `imagick`.
|
||||
*/
|
||||
'image_driver' => env('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'),
|
||||
|
||||
/*
|
||||
* 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\Conversions\Jobs\PerformConversionsJob::class,
|
||||
'generate_responsive_images' => Spatie\MediaLibrary\ResponsiveImages\Jobs\GenerateResponsiveImagesJob::class,
|
||||
],
|
||||
|
||||
/*
|
||||
* When using the addMediaFromUrl method you may want to replace the default downloader.
|
||||
* This is particularly useful when the url of the image is behind a firewall and
|
||||
* need to add additional flags, possibly using curl.
|
||||
*/
|
||||
'media_downloader' => Spatie\MediaLibrary\Downloaders\DefaultDownloader::class,
|
||||
|
||||
/*
|
||||
* When using the addMediaFromUrl method the SSL is verified by default.
|
||||
* This is option disables SSL verification when downloading remote media.
|
||||
* Please note that this is a security risk and should only be false in a local environment.
|
||||
*/
|
||||
'media_downloader_ssl' => env('MEDIA_DOWNLOADER_SSL', true),
|
||||
|
||||
'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 30%
|
||||
* smaller than the previous one. More info in the documentation.
|
||||
*
|
||||
* https://docs.spatie.be/laravel-medialibrary/v9/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.
|
||||
* When disabled, no tiny placeholder is generated.
|
||||
*/
|
||||
'use_tiny_placeholders' => true,
|
||||
|
||||
/*
|
||||
* This class will generate the tiny placeholder used for progressive image loading. By default
|
||||
* the media library will use a tiny blurred jpg image.
|
||||
*/
|
||||
'tiny_placeholder_generator' => Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\Blurred::class,
|
||||
],
|
||||
|
||||
/*
|
||||
* When enabling this option, a route will be registered that will enable
|
||||
* the Media Library Pro Vue and React components to move uploaded files
|
||||
* in a S3 bucket to their right place.
|
||||
*/
|
||||
'enable_vapor_uploads' => env('ENABLE_MEDIA_LIBRARY_VAPOR_UPLOADS', false),
|
||||
|
||||
/*
|
||||
* When converting Media instances to response the media library will add
|
||||
* a `loading` attribute to the `img` tag. Here you can set the default
|
||||
* value of that attribute.
|
||||
*
|
||||
* Possible values: 'lazy', 'eager', 'auto' or null if you don't want to set any loading instruction.
|
||||
*
|
||||
* More info: https://css-tricks.com/native-lazy-loading/
|
||||
*/
|
||||
'default_loading_attribute_value' => null,
|
||||
|
||||
/*
|
||||
* You can specify a prefix for that is used for storing all media.
|
||||
* If you set this to `/my-subdir`, all your media will be stored in a `/my-subdir` directory.
|
||||
*/
|
||||
'prefix' => env('MEDIA_PREFIX', '/attachments'),
|
||||
|
||||
/*
|
||||
* When forcing lazy loading, media will be loaded even if you don't eager load media and you have
|
||||
* disabled lazy loading globally in the service provider.
|
||||
*/
|
||||
'force_lazy_loading' => env('FORCE_MEDIA_LIBRARY_LAZY_LOADING', true),
|
||||
];
|
@ -33,6 +33,8 @@
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Trumbowyg/2.27.3/plugins/cleanpaste/trumbowyg.cleanpaste.min.js"
|
||||
integrity="sha512-UInqT8f+K1tkck6llPo0HDxlT/Zxv8t4OGeCuVfsIlXLrnP1ZKDGb+tBsBPMqDW15OcmV8NDfQe9+EaAG4aXeg=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"></script>
|
||||
|
||||
@endsection
|
||||
|
||||
@section('page-script')
|
||||
@ -49,6 +51,20 @@
|
||||
autogrow: true,
|
||||
resetCss: true
|
||||
});
|
||||
|
||||
Dropzone.options.fileDropzone = {
|
||||
paramName: 'file',
|
||||
maxFilesize: 2, // MB
|
||||
acceptedFiles: '.pdf,.jpg,.png',
|
||||
uploadMultiple: true,
|
||||
parallelUploads: 3,
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': "{{ csrf_token() }}"
|
||||
},
|
||||
success: function (file, response) {
|
||||
console.log(response.success);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
@ -93,6 +109,35 @@
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<h1>Upload Files</h1>
|
||||
|
||||
<!-- List of already uploaded files -->
|
||||
<div class="uploaded-files">
|
||||
<h2>Uploaded Files</h2>
|
||||
<ul>
|
||||
@foreach ($study->getMedia('uploads') as $media)
|
||||
<li>
|
||||
<i class="fa {{ $media->mime_type == 'application/pdf' ? 'fa-file-pdf' : 'fa-file-image' }}"></i>
|
||||
<a href="{{ $media->getUrl() }}" target="_blank">{{ $media->file_name }}</a>
|
||||
({{ $media->human_readable_size }}) - Uploaded on {{ $media->created_at->format('Y-m-d H:i') }}
|
||||
|
||||
<form action="{{ route('staff.attachment.delete', [$study->hash, $media->id]) }}" method="POST" style="display:inline;">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
|
||||
</form>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Dropzone area -->
|
||||
<form action="{{ route('staff.attachment.upload', $study->hash) }}" class="dropzone" id="file-dropzone">
|
||||
@csrf
|
||||
</form>
|
||||
|
||||
@endsection
|
||||
|
||||
|
||||
|
@ -31,28 +31,46 @@
|
||||
|
||||
<h4>Clinical Information</h4>
|
||||
|
||||
<h5>Clinical History</h5>
|
||||
<div class="p-4 border-gray-100">
|
||||
<div class="show-text" disabled name="clinical_history" id="clinical_history" >{!! $details->clinical_history !!}</di>
|
||||
<h5>Clinical History</h5>
|
||||
<div class="p-4 border-gray-100">
|
||||
<div class="show-text" disabled name="clinical_history"
|
||||
id="clinical_history">{!! $details->clinical_history !!}</di>
|
||||
</div>
|
||||
<x-section-border/>
|
||||
|
||||
<h5>surgical history</h5>
|
||||
<div class="p-4 border-gray-100">
|
||||
<div class="show-text" disabled name="surgical_history" id="surgical_history" >{!! $details->surgical_history !!}</div>
|
||||
<div class="show-text" disabled name="surgical_history"
|
||||
id="surgical_history">{!! $details->surgical_history !!}</div>
|
||||
</div>
|
||||
<x-section-border/>
|
||||
|
||||
<h5>lab results</h5>
|
||||
<div class="p-4 border-gray-100">
|
||||
<div class="show-text" disabled name="lab_results" id="lab_results" >{!! $details->lab_results !!}</div>
|
||||
<div class="show-text" disabled name="lab_results" id="lab_results">{!! $details->lab_results !!}</div>
|
||||
</div>
|
||||
<x-section-border/>
|
||||
|
||||
<h5>clinical diagnosis</h5>
|
||||
<div class="p-4 border-gray-100">
|
||||
<div class="show-text" disabled name="clinical_diagnosis" id="clinical_diagnosis" >{!! $details->clinical_diagnosis !!}</div>
|
||||
<div class="show-text" disabled name="clinical_diagnosis"
|
||||
id="clinical_diagnosis">{!! $details->clinical_diagnosis !!}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- List of already uploaded files -->
|
||||
<div class="uploaded-files">
|
||||
<h2>Uploaded Files</h2>
|
||||
<ul>
|
||||
@foreach ($study->getMedia('uploads') as $media)
|
||||
<li>
|
||||
<i class="fa {{ $media->mime_type == 'application/pdf' ? 'fa-file-pdf' : 'fa-file-image' }}"></i>
|
||||
<a href="{{ $media->getUrl() }}" target="_blank">{{ $media->file_name }}</a>
|
||||
({{ $media->human_readable_size }}) - Uploaded
|
||||
on {{ $media->created_at->format('Y-m-d H:i:s') }}
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
@ -4,6 +4,7 @@
|
||||
use App\Http\Controllers\Guest\ViewSharedStudyController;
|
||||
use App\Http\Controllers\Radiologist\ReportWriteController;
|
||||
use App\Http\Controllers\SocialLoginController;
|
||||
use App\Http\Controllers\Staff\AttachmentController;
|
||||
use App\Http\Controllers\Staff\StudiesController;
|
||||
use App\Http\Controllers\Staff\StudyHistoryController;
|
||||
use App\Http\Controllers\Staff\StudyViewerController;
|
||||
@ -61,6 +62,11 @@
|
||||
Route::post('{hashid}', [StudyHistoryController::class, 'save'])->name('save');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'attachment', 'as' => 'attachment.'], function () {
|
||||
Route::post('upload/{hashid}', [AttachmentController::class, 'upload'])->name('upload');
|
||||
Route::delete('delete/{hashid}/{media}', [AttachmentController::class, 'delete'])->name('delete');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'meta', 'as' => 'meta.'], function () {
|
||||
Route::get('{hashid}', [StudyMetadataController::class, 'view'])->name('view');
|
||||
Route::get('{hashid}/edit', [StudyMetadataController::class, 'edit'])->name('edit');
|
||||
|
Loading…
Reference in New Issue
Block a user