mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-29 11:15:58 +00:00
- when member upload files or create folder in team folder, the true owner of the content is creator of team member. That means the upload bandwidth and storage go to creator responsibility
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
<?php
|
||||
namespace Domain\Files\Actions;
|
||||
|
||||
use App\Users\Models\User;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Domain\Folders\Models\Folder;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Domain\Files\Requests\UploadRequest;
|
||||
@@ -60,13 +59,14 @@ class UploadFileAction
|
||||
// File name
|
||||
$fileName = Str::uuid() . '.' . $request->input('extension');
|
||||
|
||||
// Get user data
|
||||
$user = $userId ? User::find($userId) : Auth::user();
|
||||
// Get user
|
||||
$user = $request->filled('parent_id')
|
||||
? Folder::find($request->input('parent_id'))->getLatestParent()->user
|
||||
: auth()->user();
|
||||
|
||||
// File Info
|
||||
$fileSize = $disk_local->size("chunks/$chunkName");
|
||||
|
||||
$file_mimetype = $disk_local->mimeType("chunks/$chunkName");
|
||||
$fileMimetype = $disk_local->mimeType("chunks/$chunkName");
|
||||
|
||||
// Check if user has enough space to upload file
|
||||
if (! $user->canUpload($fileSize)) {
|
||||
@@ -80,27 +80,24 @@ class UploadFileAction
|
||||
|
||||
// Create multiple image thumbnails
|
||||
($this->createImageThumbnail)($fileName, $file, $user->id);
|
||||
|
||||
|
||||
// Move files to external storage
|
||||
if (! isStorageDriver('local')) {
|
||||
($this->moveFileToExternalStorage)($fileName, $user->id);
|
||||
}
|
||||
|
||||
// Store user upload size
|
||||
($this->recordUpload)($fileSize, $user->id);
|
||||
|
||||
// Create new file
|
||||
$item = UserFile::create([
|
||||
'mimetype' => get_file_type_from_mimetype($file_mimetype),
|
||||
'type' => get_file_type($file_mimetype),
|
||||
'mimetype' => get_file_type_from_mimetype($fileMimetype),
|
||||
'type' => get_file_type($fileMimetype),
|
||||
'parent_id' => ($this->getFileParentId)($request, $user->id),
|
||||
'name' => $request->input('filename'),
|
||||
'basename' => $fileName,
|
||||
'author' => $userId ? 'visitor' : 'user',
|
||||
'filesize' => $fileSize,
|
||||
'user_id' => $user->id,
|
||||
'creator_id' => auth()->id(),
|
||||
]);
|
||||
|
||||
|
||||
// Store exif metadata for files
|
||||
($this->storeExifMetadata)($item, $file);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class GetFileController extends Controller
|
||||
->firstOrFail();
|
||||
|
||||
// Check if user can download file
|
||||
if (! $file->owner->canDownload()) {
|
||||
if (! $file->user->canDownload()) {
|
||||
return response(userActionNotAllowedError(), 401);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Domain\Files\Models;
|
||||
|
||||
use App\Users\Models\User;
|
||||
use Domain\Traffic\Actions\RecordUploadAction;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Domain\Sharing\Models\Share;
|
||||
@@ -174,7 +175,12 @@ class File extends Model
|
||||
return $this->hasOne(Share::class, 'item_id', 'id');
|
||||
}
|
||||
|
||||
public function owner(): HasOne
|
||||
public function creator(): HasOne
|
||||
{
|
||||
return $this->hasOne(User::class, 'id', 'creator_id');
|
||||
}
|
||||
|
||||
public function user(): HasOne
|
||||
{
|
||||
return $this->hasOne(User::class, 'id', 'user_id');
|
||||
}
|
||||
@@ -207,6 +213,9 @@ class File extends Model
|
||||
|
||||
static::creating(function ($file) {
|
||||
$file->id = (string) Str::uuid();
|
||||
|
||||
// Store upload record
|
||||
resolve(RecordUploadAction::class)($file->filesize, $file->user_id);
|
||||
});
|
||||
|
||||
static::deleting(function ($file) {
|
||||
|
||||
@@ -29,10 +29,10 @@ class FileResource extends JsonResource
|
||||
'file_url' => $this->file_url,
|
||||
'thumbnail' => $this->thumbnail,
|
||||
'parent_id' => $this->parent_id,
|
||||
'created_at' => set_time_by_user_timezone($this->owner, $this->created_at),
|
||||
'updated_at' => set_time_by_user_timezone($this->owner, $this->updated_at),
|
||||
'created_at' => set_time_by_user_timezone($this->user, $this->created_at),
|
||||
'updated_at' => set_time_by_user_timezone($this->user, $this->updated_at),
|
||||
'deleted_at' => $this->deleted_at
|
||||
? set_time_by_user_timezone($this->owner, $this->deleted_at)
|
||||
? set_time_by_user_timezone($this->user, $this->deleted_at)
|
||||
: null,
|
||||
],
|
||||
'relationships' => [
|
||||
@@ -50,15 +50,15 @@ class FileResource extends JsonResource
|
||||
],
|
||||
],
|
||||
]),
|
||||
$this->mergeWhen($this->owner, fn () => [
|
||||
'owner' => [
|
||||
$this->mergeWhen($this->creator, fn () => [
|
||||
'creator' => [
|
||||
'data' => [
|
||||
'type' => 'owner',
|
||||
'id' => $this->user_id,
|
||||
'type' => 'creator',
|
||||
'id' => $this->creator->id,
|
||||
'attributes' => [
|
||||
'name' => $this->owner->settings->name,
|
||||
'avatar' => $this->owner->settings->avatar,
|
||||
'color' => $this->owner->settings->color,
|
||||
'name' => $this->creator->settings->name,
|
||||
'avatar' => $this->creator->settings->avatar,
|
||||
'color' => $this->creator->settings->color,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -18,35 +18,31 @@ class CreateFolderAction
|
||||
CreateFolderRequest $request,
|
||||
?Share $shared = null,
|
||||
): Folder|array {
|
||||
// Get user model
|
||||
$user = $shared
|
||||
? $shared->user
|
||||
: Auth::user();
|
||||
// Get stuff
|
||||
$isFilledParentId = $request->filled('parent_id');
|
||||
$parentId = $request->input('parent_id');
|
||||
|
||||
// Get user
|
||||
$user = $isFilledParentId
|
||||
? Folder::find($parentId)->getLatestParent()->user
|
||||
: auth()->user();
|
||||
|
||||
// Check if user can create folder
|
||||
if (! $user->canCreateFolder()) {
|
||||
throw new InvalidUserActionException();
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if exist parent team folder, if yes,
|
||||
* then get the latest parent folder to detect whether it is team_folder
|
||||
*/
|
||||
if ($request->has('parent_id')) {
|
||||
$isTeamFolder = Folder::find($request->input('parent_id'))
|
||||
->getLatestParent()
|
||||
->team_folder;
|
||||
}
|
||||
|
||||
// Create folder record
|
||||
return Folder::create([
|
||||
'parent_id' => $request->input('parent_id'),
|
||||
'parent_id' => $parentId,
|
||||
'name' => $request->input('name'),
|
||||
'color' => $request->input('color') ?? null,
|
||||
'emoji' => $request->input('emoji') ?? null,
|
||||
'author' => $shared ? 'visitor' : 'user',
|
||||
'user_id' => $user->id,
|
||||
'team_folder' => $isTeamFolder ?? false,
|
||||
'team_folder' => $isFilledParentId
|
||||
? Folder::find($parentId)->getLatestParent()->team_folder
|
||||
: false
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ class Folder extends Model
|
||||
->withPivot('permission');
|
||||
}
|
||||
|
||||
public function owner(): HasOne
|
||||
public function user(): HasOne
|
||||
{
|
||||
return $this->hasOne(User::class, 'id', 'user_id');
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ class FolderResource extends JsonResource
|
||||
'isTeamFolder' => $this->team_folder,
|
||||
'items' => $this->items,
|
||||
'trashed_items' => $this->trashed_items,
|
||||
'created_at' => set_time_by_user_timezone($this->owner, $this->created_at),
|
||||
'updated_at' => set_time_by_user_timezone($this->owner, $this->updated_at),
|
||||
'created_at' => set_time_by_user_timezone($this->user, $this->created_at),
|
||||
'updated_at' => set_time_by_user_timezone($this->user, $this->updated_at),
|
||||
'deleted_at' => $this->deleted_at
|
||||
? set_time_by_user_timezone($this->owner, $this->deleted_at)
|
||||
? set_time_by_user_timezone($this->user, $this->deleted_at)
|
||||
: null,
|
||||
],
|
||||
'relationships' => [
|
||||
@@ -49,15 +49,15 @@ class FolderResource extends JsonResource
|
||||
],
|
||||
],
|
||||
]),
|
||||
$this->mergeWhen($this->owner, fn () => [
|
||||
'owner' => [
|
||||
$this->mergeWhen($this->user, fn () => [
|
||||
'user' => [
|
||||
'data' => [
|
||||
'type' => 'owner',
|
||||
'type' => 'user',
|
||||
'id' => $this->user_id,
|
||||
'attributes' => [
|
||||
'name' => $this->owner->settings->name,
|
||||
'avatar' => $this->owner->settings->avatar,
|
||||
'color' => $this->owner->settings->color,
|
||||
'name' => $this->user->settings->name,
|
||||
'avatar' => $this->user->settings->avatar,
|
||||
'color' => $this->user->settings->color,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
namespace Domain\Teams\Actions;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Domain\Folders\Models\Folder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class TransferContentOwnershipToTeamFolderOwnerAction
|
||||
{
|
||||
public function __invoke(Folder $folder, $leavingUserId)
|
||||
{
|
||||
// Find and delete attached member from team folder
|
||||
DB::table('team_folder_members')
|
||||
->where('parent_id', $folder->id)
|
||||
->where('user_id', $leavingUserId)
|
||||
->delete();
|
||||
|
||||
// Get all inherited folder from team folder
|
||||
$childrenFolderIds = Folder::with('folders:id,parent_id')
|
||||
->where('id', $folder->id)
|
||||
->get('id');
|
||||
|
||||
$teamFolderIds = Arr::flatten(filter_folders_ids($childrenFolderIds));
|
||||
|
||||
// Replace leaver content ownership for author of team folder
|
||||
DB::table('files')
|
||||
->whereIn('parent_id', $teamFolderIds)
|
||||
->where('user_id', $leavingUserId)
|
||||
->cursor()
|
||||
->each(
|
||||
fn ($file) =>
|
||||
$this->move_files_to_the_new_destination($file, $folder)
|
||||
);
|
||||
|
||||
DB::table('files')
|
||||
->whereIn('parent_id', $teamFolderIds)
|
||||
->where('user_id', $leavingUserId)
|
||||
->update(['user_id' => $folder->user_id]);
|
||||
|
||||
DB::table('folders')
|
||||
->whereIn('id', $teamFolderIds)
|
||||
->where('user_id', $leavingUserId)
|
||||
->update(['user_id' => $folder->user_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $file
|
||||
* @param Folder $folder
|
||||
*/
|
||||
private function move_files_to_the_new_destination($file, Folder $folder): void
|
||||
{
|
||||
// Move image thumbnails
|
||||
if ($file->type === 'image') {
|
||||
// Get image thumbnail list
|
||||
$thumbnailList = getThumbnailFileList($file->basename);
|
||||
|
||||
// move thumbnails to the new location
|
||||
$thumbnailList->each(function ($basename) use ($file, $folder) {
|
||||
$oldPath = "files/$file->user_id/$basename";
|
||||
$newPath = "files/$folder->user_id/$basename";
|
||||
|
||||
if (Storage::exists($oldPath)) {
|
||||
Storage::move($oldPath, $newPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Move single file
|
||||
Storage::move("files/$file->user_id/$file->basename", "files/$folder->user_id/$file->basename");
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,6 @@ use Domain\Folders\Models\Folder;
|
||||
|
||||
class UpdateMembersAction
|
||||
{
|
||||
public function __construct(
|
||||
public TransferContentOwnershipToTeamFolderOwnerAction $transferContentOwnership,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(Folder $folder, $members): void
|
||||
{
|
||||
$existingMembers = $folder
|
||||
@@ -28,9 +23,6 @@ class UpdateMembersAction
|
||||
->where('parent_id', $folder->id)
|
||||
->whereIn('user_id', $deletedMembers->toArray())
|
||||
->delete();
|
||||
|
||||
// Transfer files/folders ownership to team folder owner
|
||||
$deletedMembers->each(fn ($memberId) => ($this->transferContentOwnership)($folder, $memberId));
|
||||
}
|
||||
|
||||
// Update privileges
|
||||
|
||||
@@ -28,7 +28,7 @@ class ConvertFolderIntoTeamFolderController extends Controller
|
||||
}
|
||||
|
||||
// Check if user didn't exceed max team members limit
|
||||
if (! $folder->owner->canInviteTeamMembers($request->input('invitations'))) {
|
||||
if (! $folder->user->canInviteTeamMembers($request->input('invitations'))) {
|
||||
return response([
|
||||
'type' => 'error',
|
||||
'message' => 'You exceed your members limit.',
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
<?php
|
||||
namespace Domain\Teams\Controllers;
|
||||
|
||||
use Auth;
|
||||
use Gate;
|
||||
use Illuminate\Http\Response;
|
||||
use Domain\Folders\Models\Folder;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Domain\Teams\Actions\TransferContentOwnershipToTeamFolderOwnerAction;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class LeaveTeamFolderController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
public TransferContentOwnershipToTeamFolderOwnerAction $transferContentOwnership,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(Folder $folder): Response|Application|ResponseFactory
|
||||
{
|
||||
// Abort in demo mode
|
||||
@@ -29,8 +23,11 @@ class LeaveTeamFolderController extends Controller
|
||||
abort(403, 'Access Denied');
|
||||
}
|
||||
|
||||
// Transfer files/folders ownership to team folder owner
|
||||
($this->transferContentOwnership)($folder, Auth::id());
|
||||
// Find and delete attached member from team folder
|
||||
DB::table('team_folder_members')
|
||||
->where('parent_id', $folder->id)
|
||||
->where('user_id', auth()->id())
|
||||
->delete();
|
||||
|
||||
return response('Done.', 204);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user