Skip to content

Commit

Permalink
Implement in-place replacement of files #93
Browse files Browse the repository at this point in the history
  • Loading branch information
tobifra committed Feb 26, 2024
1 parent 177e599 commit 9872ebc
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 109 deletions.
125 changes: 81 additions & 44 deletions api/app/Http/Controllers/FileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Imagick;
use ImagickPixel;
use Intervention\Image\ImageManagerStatic as Image;
use Illuminate\Support\Facades\Auth;

class FileController extends Controller
{
Expand All @@ -16,7 +17,11 @@ public function index(Request $request)
{
$perPage = $request->input('per_page', 100);
$extensions = $request->input('extensions');
$user = Auth::user();
$query = File::orderBy("id", "desc");
if (!$user->hasRole('admin')) {
$query = $query->where('user_id', $user->id);
}

if ($extensions) {
$extensionsArray = explode(',', $extensions);
Expand All @@ -35,56 +40,23 @@ public function store(Request $request)
'category' => 'string|max:255'
]);

$file = $request->file('file');
$filename = time().'_'.rand().'.'.$file->getClientOriginalExtension();
$category = $validatedData['category'];
$filePath = 'public/uploads/'.$filename;
$name = implode('.', array_slice(explode('.', $file->getClientOriginalName()), 0, -1));


Storage::disk('public')->putFileAs('uploads', $file, $filename);


$fileUrl = Storage::url($filePath);

$fileData = $request->file('file');

$filename = time() . '_' . rand() . '.' . $fileData->getClientOriginalExtension();
$category = $validatedData['category'];
$filePath = 'public/uploads/' . $filename;
$name = implode('.', array_slice(explode('.', $fileData->getClientOriginalName()), 0, -1));
$filePath = Storage::url($filePath);
$user = Auth::user();
$newFile = new File;
$newFile->path = $fileUrl;
$newFile->extension = $file->getClientOriginalExtension();
$newFile->path = $filePath;
$newFile->extension = $fileData->getClientOriginalExtension();
$newFile->category = $category;
$newFile->name = $name;
$newFile->user_id = $user->id;

$this->storeFileAndCreateThumbnail($fileData, $filename, $newFile);

if (in_array($newFile->extension, ['jpg', 'jpeg', 'png', 'gif'])) {
$thumbnailPath = 'public/thumbnails/'.$filename;
$thumbnail = Image::make($file)->resize(200, null, function($constraint) {
$constraint->aspectRatio();
});
$thumbnail->save(storage_path('app/'.$thumbnailPath));
$newFile->thumbnail = Storage::url($thumbnailPath);
} elseif ($newFile->extension === 'pdf') {
$thumbnailPath = 'public/thumbnails/'.$filename.'.png';

$imagick = new Imagick();
$imagick->setResolution(72, 72);

$imagick->readImage($file.'[0]');
if ($imagick->getImageWidth() > $imagick->getImageHeight()) {
$imagick->rotateImage(new ImagickPixel('none'), -90);
}

$imagick->setImageFormat('png');

$imagick->writeImage(storage_path('app/'.$thumbnailPath));


$imagick->clear();
$imagick->destroy();

$newFile->thumbnail = Storage::url($thumbnailPath);
} elseif ($newFile->extension === 'svg') {
$newFile->thumbnail = $fileUrl;
}

$newFile->save();

Expand All @@ -93,7 +65,13 @@ public function store(Request $request)

public function show($id)
{
$user = Auth::user();
$file = File::findOrFail($id);
if (!$user->hasRole('admin')) {
if ($file->user_id !== $user->id) {
return response()->json(['message' => 'You are not allowed to view this file'], 403);
}
}
return response()->json($file);
}

Expand All @@ -103,14 +81,38 @@ public function update(Request $request, $id)
'name' => 'string|max:255',
'category' => 'string|max:255'
]);
$fileData = $request->file('file');
$file = File::findOrFail($id);
$user = Auth::user();
if (!$user->hasRole('admin')) {
if ($file->user_id !== $user->id) {
return response()->json(['message' => 'You are not allowed to update this file'], 403);
}
}
$file->update($validatedData);
if ($fileData) {
Storage::disk('public')->delete($file->path);
if ($file->thumbnail) {
Storage::disk('public')->delete(str_replace('/storage/', '', $file->thumbnail));
}
$splitted = explode('/', $file->path);
$filename = end($splitted);
$this->storeFileAndCreateThumbnail($fileData, $filename, $file);


}
return response()->json(['message' => 'File updated successfully']);
}

public function destroy($id)
{
$file = File::findOrFail($id);
$user = Auth::user();
if (!$user->hasRole('admin')) {
if ($file->user_id !== $user->id) {
return response()->json(['message' => 'You are not allowed to delete this file'], 403);
}
}

// Delete the file from the public disk
Storage::disk('public')->delete($file->path);
Expand All @@ -126,4 +128,39 @@ public function destroy($id)
return response()->json(['message' => 'File deleted successfully']);
}

private function storeFileAndCreateThumbnail($fileData, $filename, $newFile)
{
Storage::disk('public')->putFileAs('uploads', $fileData, $filename);
if (in_array($newFile->extension, ['jpg', 'jpeg', 'png', 'gif'])) {
$thumbnailPath = 'public/thumbnails/' . $filename;
$thumbnail = Image::make($fileData)->resize(200, null, function ($constraint) {
$constraint->aspectRatio();
});
$thumbnail->save(storage_path('app/' . $thumbnailPath));
$newFile->thumbnail = Storage::url($thumbnailPath);
} elseif ($newFile->extension === 'pdf') {
$thumbnailPath = 'public/thumbnails/' . $filename . '.png';

$imagick = new Imagick();
$imagick->setResolution(72, 72);

$imagick->readImage($fileData . '[0]');
if ($imagick->getImageWidth() > $imagick->getImageHeight()) {
$imagick->rotateImage(new ImagickPixel('none'), -90);
}

$imagick->setImageFormat('png');

$imagick->writeImage(storage_path('app/' . $thumbnailPath));


$imagick->clear();
$imagick->destroy();

$newFile->thumbnail = Storage::url($thumbnailPath);
} elseif ($newFile->extension === 'svg') {
$newFile->thumbnail = $$newFile->path;
}
}

}
1 change: 1 addition & 0 deletions api/routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
Route::post('/files', [FileController::class, 'store']);
Route::get('/files/{id}', [FileController::class, 'show']);
Route::put('/files/{id}', [FileController::class, 'update']);
Route::post('/files/{id}', [FileController::class, 'update']);
Route::delete('/files/{id}', [FileController::class, 'destroy']);
});

Expand Down
69 changes: 69 additions & 0 deletions frontend/src/components/admin/DragAndDropField.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<template>
<div
@dragover.prevent
@dragenter="isDragging = true"
@dragleave="isDragging = false"
@drop.prevent
@drop="dropFile"
>
<div
:class="`flex flex-col justify-center items-center rounded-lg ${
isDragging ? 'bg-gray-50' : ''
} border-2 border-gray-400 border-dashed text-center p-5`"
>
<font-awesome-icon
:icon="icons.faFileCirclePlus"
class="h-12 w-12 text-gray-400"
/>

<div class="flex items-center">
<label
for="file-upload"
class="text-sm font-medium text-gray-900 hover:text-gray-700"
>Upload a File
<input
id="file-upload"
type="file"
class="sr-only"
multiple
@change="uploadFile"
/>
</label>
<div class="text-sm">&nbsp;{{ $t("dashboard.dragAndDrop") }}</div>
</div>
</div>
</div>
</template>

<script>
import { faFileCirclePlus } from "@fortawesome/free-solid-svg-icons";
export default {
emits: ["droppedFile"],
data() {
return {
isDragging: false,
icons: {
faFileCirclePlus,
},
};
},
methods: {
uploadFile(e) {
const files = e.target.files;
this.handleFiles(files);
},
dropFile(e) {
this.isDragging = false;
const files = e.dataTransfer.files;
this.handleFiles(files);
},
handleFiles(files) {
for (let file of files) {
this.$emit("droppedFile", file);
}
},
},
};
</script>

<style></style>
60 changes: 8 additions & 52 deletions frontend/src/components/admin/DragAndDropUpload.vue
Original file line number Diff line number Diff line change
@@ -1,47 +1,14 @@
<template>
<div
@dragover.prevent
@dragenter="isDragging = true"
@dragleave="isDragging = false"
@drop.prevent
@drop="dragFile"
>
<div
:class="`flex flex-col justify-center items-center rounded-lg ${
isDragging ? 'bg-gray-50' : ''
} border-2 border-gray-400 border-dashed text-center p-5`"
>
<font-awesome-icon
:icon="icons.faFileCirclePlus"
class="h-12 w-12 text-gray-400"
/>

<div class="flex items-center">
<label
for="file-upload"
class="text-sm font-medium text-blue-600 hover:text-blue-800"
>{{ text }}
<input
id="file-upload"
type="file"
class="sr-only"
multiple
@change="uploadFile"
/>
</label>
<div class="text-sm">&nbsp;{{ $t("dashboard.dragAndDrop") }}</div>
</div>
</div>
</div>
<DragAndDropField @droppedFile="handleDrop" />
</template>

<script>
import { faFileCirclePlus } from "@fortawesome/free-solid-svg-icons";
import DragAndDropField from "./DragAndDropField.vue";
export default {
emits: ["uploadedFile"],
props: {
category: { default: "general" },
text: { default: "Upload a File" },
},
data() {
return {
Expand All @@ -53,30 +20,19 @@ export default {
};
},
methods: {
async addMedia() {
async handleDrop(file) {
try {
for (const file of this.files) {
let formData = new FormData();
formData.append("file", file, file.name);
formData.append("category", this.category);
await this.callApi("post", "/files", formData);
}
this.files = [];
const formData = new FormData();
formData.append("file", file, file.name);
formData.append("category", this.category);
await this.callApi("post", "/files", formData);
this.$emit("uploadedFile", true);
} catch (e) {
console.log(e);
}
},
uploadFile(e) {
this.files = e.target.files;
this.addMedia();
},
dragFile(e) {
this.isDragging = false;
this.files = e.dataTransfer.files;
this.addMedia();
},
},
components: { DragAndDropField },
};
</script>

Expand Down
1 change: 1 addition & 0 deletions frontend/src/translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export const messages = {
textColor: "Textfarbe",
bgColor: "Hintergrundfarbe",
notFoundPage: "404-Seite",
replaceFile: "Datei ersetzen",
},
page: {
campDateLabel: "Lagerdatum",
Expand Down
Loading

0 comments on commit 9872ebc

Please sign in to comment.