laravel-vue-file-share/app/Http/Controllers/FileController.php

1118 lines
43 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Http\Requests\NewFolder;
use App\Http\Requests\UploadFiles;
use App\Http\Requests\SelectedFiles;
use App\Http\Requests\RecycledFiles;
use App\Http\Requests\ShareFiles;
use App\Http\Requests\SharedFiles;
use App\Http\Resources\FileResource;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Inertia\Inertia;
use App\Models\File;
use App\Models\User;
use App\Models\FileShare;
use Carbon\Carbon;
use Kalnoy\Nestedset\Collection;
use ZipArchive;
use App\Mail\ShareFilesNotification;
class FileController extends Controller
{
// render user's files
public function userFiles(Request $request, string $folder = null) {
// validate passed in $folder param
// if passed in,
if ($folder) {
$folder = File::query()
->where('created_by', Auth::id()) // ensure matching user for access
->where('path', $folder) // ensure that the specified folder exists in the database
->firstOrFail(); // if none found, fail
}
else {
// use root folder if $folder not passed in
$folder = $this->getRoot();
}
// get all files and folders created by current user
$files = File::query()->where('created_by', Auth::id())
// that have not been deleted
->whereNull('deleted_at')
// order by folders first
->orderByDesc('is_folder')
// then order by creation date
->orderByDesc('created_at');
$search = $request->get('search');
if ($search) {
$files->where('name', 'like', '%' . $search . '%');
} else { // return all files filtered by parent_id
$files->where('parent_id', $folder->id);
}
$files = $files->get();
$files = FileResource::collection($files);
$ancestors = FileResource::collection([...$folder->ancestors, $folder]);
$folder = new FileResource($folder);
return Inertia::render('UserFiles', compact('files', 'folder', 'ancestors'));
}
// render files shared with user
public function sharedWithMe(Request $request, $folder = null) {
// validate passed in $folder param
if ($folder) {
// get ancestors
$firstQuery = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
->where('file_shares.user_id', Auth::id())
->whereNull('files.deleted_at')
->find(intval($folder)); // ensure that the specified id exists in the database
if ($firstQuery) {
$ancestors = $firstQuery->ancestors;
}
// folder found in the shares db
$folder = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
->where('file_shares.user_id', Auth::id())
->whereNull('files.deleted_at')
->select('files.id as id', 'name', 'size', 'created_by', 'file_shares.created_at as created_at', 'file_shares.updated_at as updated_at', 'parent_id')
->find(intval($folder)); // ensure that the specified id exists in the database
}
// if still exists,
if ($folder) {
// files are those found in the shares db
$files = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
// that are shared with the current user
->where('file_shares.user_id', Auth::id())
// that have not been deleted
->whereNull('files.deleted_at')
// order by folders first
->orderByDesc('files.is_folder')
// then order by creation date
->orderByDesc('files.id')
// then select to reference file_id
->select('files.id as id', 'name', 'size', 'created_by', 'file_shares.created_at as created_at', 'file_shares.updated_at as updated_at', 'mimetype', 'is_folder', 'parent_id');
$search = $request->get('search');
if ($search) {
$files->where('name', 'like', '%' . $search . '%');
} else { // return all files filtered by $folder
$files->where('parent_id', $folder->id);
}
$files = $files->get();
$files = FileResource::collection($files);
// ancestors
$ancestors = FileResource::collection($ancestors);
$folder = new FileResource($folder);
// pass everything into SharedWith
return Inertia::render('SharedWith', compact('files', 'folder', 'ancestors'));
}
else { // no folder
// files are those found in the shares db
$files = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
// that are shared with the current user
->where('file_shares.user_id', Auth::id())
// that have not been deleted
->whereNull('files.deleted_at')
// order by folders first
->orderByDesc('files.is_folder')
// then order by creation date
->orderByDesc('files.id')
->select('files.id as id', 'name', 'size', 'created_by', 'file_shares.created_at as created_at', 'file_shares.updated_at as updated_at', 'mimetype', 'is_folder', 'parent_id');
$search = $request->get('search');
if ($search) {
$files->where('name', 'like', '%' . $search . '%');
}
$files = $files->get();
// make into collection
$files = FileResource::collection($files);
$allIds = array();
// get $parentIds of each file
foreach($files as $file) {
array_push($allIds, intval($file->id));
}
// run through and filter the $files array to not have any files which are children of the displayed files
foreach($files as $key => $value) {
foreach($allIds as $allId) {
if ($value->parent_id == $allId) {
unset($files[$key]);
}
}
}
// pass only files into SharedWith
return Inertia::render('SharedWith', compact('files'));
}
}
// render files shared by user
public function sharedByMe(Request $request, $folder = null) {
// validate passed in $folder param
if ($folder) {
// run a query just for ancestors
$firstQuery = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
->where('files.created_by', Auth::id())
->whereNull('files.deleted_at')
->find(intval($folder)); // ensure that the specified id exists in the database
if ($firstQuery) {
$ancestors = $firstQuery->ancestors;
}
// folder found in the shares db
$folder = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
->where('files.created_by', Auth::id())
->whereNull('files.deleted_at')
->select('files.id as id', 'name', 'size', 'created_by', 'file_shares.created_at as created_at', 'file_shares.updated_at as updated_at', 'parent_id')
->find(intval($folder)); // ensure that the specified id exists in the database
}
// if still exists,
if ($folder) {
// files are those found in the shares db
$files = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
// that are made by the current user
->where('files.created_by', Auth::id())
// that have not been deleted
->whereNull('files.deleted_at')
// order by folders first
->orderByDesc('files.is_folder')
// then order by creation date
->orderByDesc('files.id')
// then select to reference file_id
->select('files.id as id', 'name', 'size', 'created_by', 'file_shares.created_at as created_at', 'file_shares.updated_at as updated_at', 'mimetype', 'is_folder', 'parent_id', 'file_shares.user_id as user_id');
$search = $request->get('search');
if ($search) {
$files->where('name', 'like', '%' . $search . '%');
} else { // return all files filtered by $folder
$files->where('parent_id', $folder->id);
}
$files = $files->get();
$files = FileResource::collection($files);
// get shared_with name for each file
foreach($files as $file) {
$user = User::query()->where('id', $file->user_id)->first();
$file->shared_with = $user->name;
}
// ancestors
$ancestors = FileResource::collection($ancestors);
$folder = new FileResource($folder);
// pass everything into SharedBy
return Inertia::render('SharedBy', compact('files', 'folder', 'ancestors'));
}
else { // no folder
// files are those found in the shares db
$files = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
// that are made by the current user
->where('files.created_by', Auth::id())
// that have not been deleted
->whereNull('files.deleted_at')
// order by folders first
->orderByDesc('files.is_folder')
// then order by creation date
->orderByDesc('files.id')
->select('files.id as id', 'name', 'size', 'created_by', 'file_shares.created_at as created_at', 'file_shares.updated_at as updated_at', 'mimetype', 'is_folder', 'parent_id', 'file_shares.user_id as user_id');
$search = $request->get('search');
if ($search) {
$files->where('name', 'like', '%' . $search . '%');
}
$files = $files->get();
// get shared_with name for each file
foreach($files as $file) {
$user = User::query()->where('id', $file->user_id)->first();
$file->shared_with = $user->name;
}
// make into collection
$files = FileResource::collection($files);
$allIds = array();
// get $parentIds of each file
foreach($files as $file) {
array_push($allIds, intval($file->id));
}
// run through and filter the $files array to not have any files which are children of the displayed files
foreach($files as $key => $value) {
foreach($allIds as $allId) {
if ($value->parent_id == $allId) {
unset($files[$key]);
}
}
}
// pass only files into SharedBy
return Inertia::render('SharedBy', compact('files'));
}
}
// render recycle bin
public function recycleBin(Request $request, $folder = null) {
// validate passed in $folder param
if ($folder) {
// find folder
$folder = File::onlyTrashed()
// made by current user
->where('created_by', Auth::id())
->find(intval($folder));
}
// if still exists,
if ($folder) {
// files are those that are trashed
$files = File::onlyTrashed()
// made by current user
->where('created_by', Auth::id())
->orderBy('is_folder', 'desc')
->orderBy('deleted_at', 'desc')
->orderBy('files.id', 'desc');
$search = $request->get('search');
if ($search) {
$files->where('name', 'like', '%' . $search . '%');
} else { // return all files filtered by $folder
$files->where('parent_id', $folder->id);
}
$files = $files->get();
$files = FileResource::collection($files);
function recursiveAncestors($file, &$ancestors) {
$parentFile = File::onlyTrashed()
->where('created_by', Auth::id())
->find($file->parent_id);
if ($parentFile) {
array_unshift($ancestors, $parentFile);
if ($parentFile->parent_id) {
recursiveAncestors($parentFile, $ancestors);
}
}
}
$ancestors = [];
recursiveAncestors($folder, $ancestors);
// ancestors
$ancestors = FileResource::collection([...$ancestors, $folder]);
$folder = new FileResource($folder);
// pass everything into RecycleBin
return Inertia::render('RecycleBin', compact('files', 'folder', 'ancestors'));
}
else { // no folder
// files are those that are trashed
$files = File::onlyTrashed()
// made by current user
->where('created_by', Auth::id())
->orderBy('is_folder', 'desc')
->orderBy('deleted_at', 'desc')
->orderBy('files.id', 'desc');
$search = $request->get('search');
if ($search) {
$files->where('name', 'like', '%' . $search . '%');
}
$files = $files->get();
// make into collection
$files = FileResource::collection($files);
$allIds = array();
// get $parentIds of each file
foreach($files as $file) {
array_push($allIds, intval($file->id));
}
// run through and filter the $files array to not have any files which are children of the displayed files
foreach($files as $key => $value) {
foreach($allIds as $allId) {
if ($value->parent_id == $allId) {
unset($files[$key]);
}
}
}
// pass only files into SharedBy
return Inertia::render('RecycleBin', compact('files'));
}
}
// create new folder based on passed in NewFolder request
public function newFolder(NewFolder $request) {
// get validated data
$data = $request->validated();
// get parent folder from NewFolder request
$parent = $request->parent;
// make new folder object based on File() object
$folder = new File();
$folder->is_folder = 1;
$folder->name = $data['name'];
$parent->appendNode($folder);
}
// upload files
public function upload(UploadFiles $request) {
// get validated data
$data = $request->validated();
// get parent folder from UploadFiles request
$parent = $request->parent;
// get file tree structure from UploadFiles request
$tree = $request->file_tree;
// if the tree is empty, then files were uploaded without any folder structure - each file needs to be uploaded
// otherwise, the entire folder structure needs to be uploaded
empty($tree) ? $this->uploadFiles($data['files'], $parent) : $this->uploadTree($tree, $parent);
}
// get download link for selected files
public function download(SelectedFiles $request) {
$data = $request->validated();
// get parent folder from DeleteFiles request
$parent = $request->parent;
// get selected Id's otherwise empty array
$Ids = $data['Ids'] ?? [];
// if no files selected
if (!$data['all'] == false && empty($Ids)) {
return [
'message' => 'No files selected!'
];
}
// if all selected, download all
if ($data['all']) {
$downloadURL = $this->makeArchive($parent->children);
// make filename for file
$filename = $parent->name . ".zip";
}
// else download the selected IDs
else {
// if there is only one ID selected
if (count($Ids) === 1) {
// get file
$file = File::find($Ids[0]);
// if file is a folder
if ($file->is_folder) {
// and folder is empty
if($file->children->isEmpty()) {
// return message
return [
"message" => "This folder is empty"
];
}
// else folder is populated
$files = $file->children;
$downloadURL = $this->makeArchive($files);
$filename = $file->name . ".zip";
}
// otherwise
else {
// create a new path
$path = pathinfo($file->stored_at, PATHINFO_BASENAME);
// if file has been uploaded to S3,
if ($file->s3 == 1) {
// pull the file off S3
$s3file = Storage::get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($path, $s3file);
} else {
// modify path
$path = 'public/' . $path;
// get local file
$localFile = Storage::disk('local')->get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($path, $localFile);
}
// define download path as being the file on the public drive
$downloadURL = asset(Storage::disk('public')->url($path));
// define filename
$filename = $file->name;
}
}
// otherwise there are many IDs selected
else {
$files = File::query()->whereIn('id', $Ids)->get();
$downloadURL = $this->makeArchive($files);
$filename = $parent->name . ".zip";
}
}
// return URL and filename
return [
'url' => $downloadURL,
'filename' => $filename,
];
}
// get download link for files that are shared with user
public function downloadSharedWith(SharedFiles $request) {
$data = $request->validated();
// get parent folder from DeleteFiles request
$parent = $request->parent;
// get selected Id's otherwise empty array
$Ids = $data['Ids'] ?? [];
// if no files selected
if (!$data['all'] == false && empty($Ids)) {
return [
'message' => 'No files selected!'
];
}
if ($parent) {
// if all selected, download all
if ($data['all']) {
$downloadURL = $this->makeArchive($parent->children);
// make filename for file
$filename = $parent->name . ".zip";
}
// else download the selected IDs
else {
// if there is only one ID selected
if (count($Ids) === 1) {
// get file
$file = File::find($Ids[0]);
// if file is a folder
if ($file->is_folder) {
// and folder is empty
if ($file->children->isEmpty()) {
// return message
return [
"message" => "This folder is empty"
];
}
// else folder is populated
$files = $file->children;
$downloadURL = $this->makeArchive($files);
$filename = $file->name . ".zip";
}
// otherwise
else {
// create a new path
$path = pathinfo($file->stored_at, PATHINFO_BASENAME);
// if file has been uploaded to S3,
if ($file->s3 == 1) {
// pull the file off S3
$s3file = Storage::get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($path, $s3file);
} else {
// modify path
$path = 'public/' . $path;
// get local file
$localFile = Storage::disk('local')->get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($path, $localFile);
}
// define download path as being the file on the public drive
$downloadURL = asset(Storage::disk('public')->url($path));
// define filename
$filename = $file->name;
}
}
// otherwise there are many IDs selected
else {
$files = File::query()->whereIn('id', $Ids)->get();
$downloadURL = $this->makeArchive($files);
$filename = $parent->name . ".zip";
}
}
}
// else there is no $parent
else {
// if all selected, download all
if ($data['all']) {
// files are those found in the shares db
$files = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
// that are shared with the current user
->where('file_shares.user_id', Auth::id())
// that have not been deleted
->whereNull('files.deleted_at')
// order by folders first
->orderByDesc('files.is_folder')
// then order by creation date
->orderByDesc('files.id')
->get();
$downloadURL = $this->makeArchive($files);
// make filename for file
$filename = "shared-" . Str::random(10) . ".zip";
}
// else download the selected IDs
else {
// if there is only one ID selected
if (count($Ids) === 1) {
// get file
$file = File::find($Ids[0]);
// if file is a folder
if ($file->is_folder) {
// and folder is empty
if ($file->children->isEmpty()) {
// return message
return [
"message" => "This folder is empty"
];
}
// else folder is populated
$files = $file->children;
$downloadURL = $this->makeArchive($files);
$filename = $file->name . ".zip";
}
// otherwise
else {
// create a new path off
$path = pathinfo($file->stored_at, PATHINFO_BASENAME);
// if file has been uploaded to S3,
if ($file->s3 == 1) {
// pull the file off S3
$s3file = Storage::get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($path, $s3file);
} else {
// modify path
$path = 'public/' . $path;
// get local file
$localFile = Storage::disk('local')->get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($path, $localFile);
}
// define download path as being the file on the public drive
$downloadURL = asset(Storage::disk('public')->url($path));
// define filename
$filename = $file->name;
}
}
// otherwise there are many IDs selected
else {
$files = File::query()->whereIn('id', $Ids)->get();
$downloadURL = $this->makeArchive($files);
$filename = "shared-" . Str::random(10) . ".zip";
}
}
}
// return URL and filename
return [
'url' => $downloadURL,
'filename' => $filename,
];
}
// get download link for files that are shared by user
public function downloadSharedBy(SharedFiles $request) {
$data = $request->validated();
// get parent folder from DeleteFiles request
$parent = $request->parent;
// get selected Id's otherwise empty array
$Ids = $data['Ids'] ?? [];
// if no files selected
if (!$data['all'] == false && empty($Ids)) {
return [
'message' => 'No files selected!'
];
}
if ($parent) {
// if all selected, download all
if ($data['all']) {
$downloadURL = $this->makeArchive($parent->children);
// make filename for file
$filename = $parent->name . ".zip";
}
// else download the selected IDs
else {
// if there is only one ID selected
if (count($Ids) === 1) {
// get file
$file = File::find($Ids[0]);
// if file is a folder
if ($file->is_folder) {
// and folder is empty
if ($file->children->isEmpty()) {
// return message
return [
"message" => "This folder is empty"
];
}
// else folder is populated
$files = $file->children;
$downloadURL = $this->makeArchive($files);
$filename = $file->name . ".zip";
}
// otherwise
else {
// create a new path
$path = pathinfo($file->stored_at, PATHINFO_BASENAME);
// if file has been uploaded to S3,
if ($file->s3 == 1) {
// pull the file off S3
$s3file = Storage::get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($path, $s3file);
} else {
// modify path
$path = 'public/' . $path;
// get local file
$localFile = Storage::disk('local')->get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($path, $localFile);
}
// define download path as being the file on the public drive
$downloadURL = asset(Storage::disk('public')->url($path));
// define filename
$filename = $file->name;
}
}
// otherwise there are many IDs selected
else {
$files = File::query()->whereIn('id', $Ids)->get();
$downloadURL = $this->makeArchive($files);
$filename = $parent->name . ".zip";
}
}
}
// else there is no $parent
else {
// if all selected, download all
if ($data['all']) {
// files are those found in the shares db
$files = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
// that are created by the current user
->where('files.created_by', Auth::id())
// that have not been deleted
->whereNull('files.deleted_at')
// order by folders first
->orderByDesc('files.is_folder')
// then order by creation date
->orderByDesc('files.id')
->get();
$downloadURL = $this->makeArchive($files);
// make filename for file
$filename = "shared-" . Str::random(10) . ".zip";
}
// else download the selected IDs
else {
// if there is only one ID selected
if (count($Ids) === 1) {
// get file
$file = File::find($Ids[0]);
// if file is a folder
if ($file->is_folder) {
// and folder is empty
if ($file->children->isEmpty()) {
// return message
return [
"message" => "This folder is empty"
];
}
// else folder is populated
$files = $file->children;
$downloadURL = $this->makeArchive($files);
$filename = $file->name . ".zip";
}
// otherwise
else {
// create a new path off "public/"
$path = pathinfo($file->stored_at, PATHINFO_BASENAME);
// if file has been uploaded to S3,
if ($file->s3 == 1) {
// pull the file off S3
$s3file = Storage::get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($path, $s3file);
} else {
// modify path
$path = 'public/' . $path;
// get local file
$localFile = Storage::disk('local')->get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($path, $localFile);
}
// define download path as being the file on the public drive
$downloadURL = asset(Storage::disk('public')->url($path));
// define filename
$filename = $file->name;
}
}
// otherwise there are many IDs selected
else {
$files = File::query()->whereIn('id', $Ids)->get();
$downloadURL = $this->makeArchive($files);
$filename = "shared-" . Str::random(10) . ".zip";
}
}
}
// return URL and filename
return [
'url' => $downloadURL,
'filename' => $filename,
];
}
// recycle selected files
public function recycle(SelectedFiles $request) {
// get validated data
$data = $request->validated();
// get parent folder from DeleteFiles request
$parent = $request->parent;
// if all files were selected
if ($data['all']) {
foreach ($parent->children as $file) {
$file->delete();
}
}
// otherwise specific IDs were selected
else {
foreach($data['Ids'] ?? [] as $fileId) {
$targetFile = File::find($fileId);
if ($targetFile) $targetFile->delete();
}
}
return redirect()->route('userFiles', [
'folder' => $parent->path
]);
}
// restores selected files
public function restore(RecycledFiles $request) {
// get validated data
$data = $request->validated();
// if all files were selected
if ($data['all']) {
$files = File::onlyTrashed()->get();
foreach ($files as $file) {
$file->restore();
}
}
// otherwise specific IDs were selected
else {
// get Id's unless empty
$Ids = $data['Ids'] ?? [];
// get files
$files = File::onlyTrashed()
->whereIn('id', $Ids)
->get();
foreach ($files as $file) {
$file->restore();
}
}
return redirect()->route('recycleBin');
}
// deletes selected files (permanent delete)
public function delete(RecycledFiles $request) {
// get validated data
$data = $request->validated();
// if all files were selected
if ($data['all']) {
$files = File::onlyTrashed()->get();
foreach ($files as $file) {
$file->shred();
}
}
// otherwise specific IDs were selected
else {
// get Id's unless empty
$Ids = $data['Ids'] ?? [];
// get files
$files = File::onlyTrashed()
->whereIn('id', $Ids)
->get();
foreach ($files as $file) {
$file->shred();
}
}
return redirect()->route('recycleBin');
}
// shares selected files
public function share(ShareFiles $request) {
// get data
$data = $request->validated();
// get parent folder
$parent = $request->parent;
// get selected Id's otherwise empty array
$Ids = $data['Ids'] ?? [];
// get user by email
$user = User::query()->where('email', $data['email'])->first();
// if no files selected
if (!$data['all'] == false && empty($Ids)) {
return [
'message' => 'No files selected!'
];
}
// if user doesn't exist
if (!$user->id) {
return [
'message' => 'Email does not belong to user!'
];
}
// otherwise, if all is passed, get current children. else find files by passed Id's
$files = $data['all'] ? $parent->children : File::find($Ids);
// define recursive function to add files to share
function recursiveShare($queued, $recipient) {
// get children of parent and determine whether recursive function needs to be called
foreach ($queued as $file) {
// check if file already exists in shared DB
$exists = FileShare::query()->where('file_id', $file->id)->where('user_id', $recipient->id)->first();
if ($exists) {
continue;
}
// else
// create new entry
$entry = [
'file_id' => $file->id,
'user_id' => $recipient->id,
'created_at' => $file->created_at,
'updated_at' => Carbon::now(),
];
FileShare::insert($entry);
// recurse on children, if any
if ($file->children) {
recursiveShare($file->children, $recipient);
}
}
}
// begin recursion on files
recursiveShare($files, $user);
// send email to recipient
Mail::to($user)->send(new ShareFilesNotification($user, Auth::user(), $files));
return redirect()->back();
}
// unshares selected files
public function unshare(SharedFiles $request) {
// get data
$data = $request->validated();
// get parent folder
$parent = $request->parent;
// get selected Id's otherwise empty array
$Ids = $data['Ids'] ?? [];
// if no files selected
if (!$data['all'] == false && empty($Ids)) {
return [
'message' => 'No files selected!'
];
}
// if parent exists
if ($parent) {
// if all is passed, get current children. else find files by passed Id's
$files = $data['all'] ? $parent->children : File::find($Ids);
}
// else
else {
// find all files that are created by current user and shared with others
$all = File::query()->join('file_shares', 'file_shares.file_id', 'files.id')
->where('files.created_by', Auth::id())
->whereNull('files.deleted_at')
->get();
$files = $data['all'] ? $all : File::find($Ids);
}
// define recursive function to remove files to share
function recursiveUnshare($queued) {
// get children of parent and determine whether recursive function needs to be called
foreach ($queued as $file) {
// check if file already exists in shared DB
$dbRecord = FileShare::query()->where('file_id', $file->id)->first();
if ($dbRecord) {
// get regular db file
$file = File::query()->where('created_by', Auth::id())->find($file->id);
$dbRecord->forceDelete();
// recurse on children, if any
if ($file->children) {
recursiveUnshare($file->children);
}
}
}
}
// begin recursion on files
recursiveUnshare($files);
return redirect()->back();
}
// gets user's root folder - called from userFiles
private function getRoot() {
return File::query()->whereIsRoot()->where('created_by', Auth::id())->firstOrFail();
}
// upload files - called from upload
private function uploadFiles($files, $parent) {
// loop over files, make and upload each one
foreach ($files as $userFile) {
$file = new File();
$file->stored_at = $userFile->store('/files/' . Auth::id(), 's3');
$file->is_folder = false;
$file->name = $userFile->getClientOriginalName();
$file->mimetype = $userFile->getMimeType();
$file->size = $userFile->getSize();
$file->s3 = 1; // file has NOT been uploaded to s3
$parent->appendNode($file);
}
}
// upload tree - called from upload
private function uploadTree($tree, $parent) {
// loop over files in file tree
foreach($tree as $name => $file) {
// if current node in $tree is an array (that means, a folder)
if (is_array($file)) {
// make folder
$folder = new File();
$folder->is_folder = 1;
$folder->name = $name;
// append folder to parent
$parent->appendNode($folder);
// call function recursively until if condition is false
// each call has the newly-created $folder as $parent
$this->uploadTree($file, $folder);
}
// otherwise the node is a file; use uploadFiles() with file
else {
$this->uploadFiles([$file], $parent);
}
}
}
// makes archive - called from download
private function makeArchive(Collection $files): string
{
// make name for archive
$archiveName = 'archive/' . Str::random() . ".zip";
// create directory if none exists
if (!Storage::disk('public')->has(dirname($archiveName))) {
Storage::disk('public')->makeDirectory(dirname($archiveName));
}
// get path of $archiveName
$path = Storage::disk('public')->path($archiveName);
// create new ZipArchive
$archive = new ZipArchive();
// see if a archive can be created or overwritten at $path
$res = $archive->open($path, ZipArchive::CREATE | ZipArchive::OVERWRITE);
// if so
if ($res === true) {
// define recursive function to add files to archive
function recursiveAdd($queued, $archive, $parent = "") {
// get children of parent and determine whether recursive function needs to be called
foreach ($queued as $file) {
// if there are no children, add file to archive
if ($file->children->isEmpty()) {
// set $internalPath to reflect file
$internalPath = $parent . $file->name;
// default file path is stored on local storage
$filePath = Storage::disk('local')->path($file->stored_at);
// if the file is uploaded to S3
if ($file->s3 == 1) {
// create a new path
$filePath = pathinfo($file->stored_at, PATHINFO_BASENAME);
// pull the file off S3
$s3file = Storage::get($file->stored_at);
// put it on the public drive for use
Storage::disk('public')->put($filePath, $s3file);
// reassign $filePath to the actual file path
$filePath = Storage::disk('public')->path($filePath);
}
// add file to archive
$archive->addFile($filePath, $internalPath);
// continue loop
continue;
}
// else
// set $internalPath to reflect a directory
$internalPath = $parent . $file->name . "/";
// recurse
recursiveAdd($file->children, $archive, $internalPath);
}
}
// add all $files to $archive
recursiveAdd($files, $archive);
}
// close archive
$archive->close();
// return archive
return asset(Storage::disk('public')->url($archiveName));
}
}