You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
6.8 KiB

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Models\File;
  4. use App\Models\Project;
  5. use App\Rules\maxBound;
  6. use App\Models\Business;
  7. use Illuminate\Support\Str;
  8. use Illuminate\Http\Request;
  9. use Illuminate\Http\UploadedFile;
  10. use Illuminate\Support\Facades\Auth;
  11. use App\HiLib\Resources\FileResource;
  12. use Spatie\QueryBuilder\QueryBuilder;
  13. use Spatie\QueryBuilder\AllowedFilter;
  14. use Illuminate\Support\Facades\Storage;
  15. class FileController extends Controller
  16. {
  17. public function index(int $business, Request $request)
  18. {
  19. permit('businessAccess');
  20. $this->indexValidation($request);
  21. $per_page = $request->limit > 100 ? 10 : $request->limit;
  22. return $this->indexFiltering($business)->paginate($per_page);
  23. }
  24. public function indexValidation($request)
  25. {
  26. $bound = 10;
  27. $this->validate($request, [
  28. 'filter.project_id' => [new MaxBound($bound)] ,
  29. 'filter.user_id' => [new MaxBound($bound)] ,
  30. ]);
  31. }
  32. public function indexFiltering($business)
  33. {
  34. $query = File::where('business_id', $business);
  35. $fileQ = QueryBuilder::for($query)
  36. ->allowedFilters([
  37. AllowedFilter::exact('user_id'),
  38. AllowedFilter::exact('project_id'),
  39. AllowedFilter::exact('extension'),
  40. ]);
  41. if (\request('_business_info')['info']['users'][\auth()->id()]['level'] != enum('levels.owner.id')) {
  42. $requested_projects = isset(\request('filter')['project_id']) ?
  43. array_unique(explode(',',\request('filter')['project_id'] ?? null )) :
  44. null;
  45. $requested_projects = collect($requested_projects)->keyBy(null)->toArray();
  46. $project_ids = $this->myStateProjects($requested_projects);
  47. $fileQ->where(function ($q) use ($project_ids) {
  48. $q->whereIn('project_id', $project_ids['non_guest_ids'])
  49. ->orWhere(function ($q) use ($project_ids) {
  50. $q->whereIn('project_id', $project_ids['guest_ids'])
  51. ->where('user_id', auth()->id());
  52. });
  53. });
  54. }
  55. if (request()->filled('group')) {
  56. $fileQ->selectRaw("files.group, count(files.id) as file_count, sum(files.size) as file_size")->groupBy('group');
  57. }
  58. return $fileQ;
  59. }
  60. public function myStateProjects($requested_projects)
  61. {
  62. $non_guest_ids = [];
  63. $guest_ids = [];
  64. $is_empty = empty($requested_projects);
  65. foreach (\request('_business_info')['info']['projects'] as $p_id => $p) {
  66. $level = \request('_business_info')['info']['projects'][$p_id]['members'][\auth()->id()]['level'];
  67. if (( $is_empty || isset($requested_projects[$p_id]))
  68. && $level > enum('levels.guest.id')) {
  69. array_push($non_guest_ids, $p_id);
  70. }
  71. if (( $is_empty || isset($requested_projects[$p_id]))
  72. && $level == enum('levels.guest.id')) {
  73. array_push($guest_ids, $p_id);
  74. }
  75. }
  76. return ['non_guest_ids' => $non_guest_ids, 'guest_ids' => $guest_ids];
  77. }
  78. public function store(Request $request, int $business, int $project)
  79. {
  80. // different size and different validation
  81. // validate
  82. // validate the wallet is not so much in debt
  83. // create record in the db
  84. // put file in s3
  85. // return file resource
  86. $business = Business::findOrFail($business);
  87. $project = Project::findOrFail($project);
  88. // $this->validate($request, ['file' => 'required|file',]);
  89. $file = $request->file('file');
  90. $file_extension = $file->getClientOriginalExtension();
  91. $file_name = Str::random(40).'.'.$file_extension;
  92. $file->storeAs(
  93. $business->id.\DIRECTORY_SEPARATOR.$project->id,
  94. $file_name,
  95. 's3'
  96. );
  97. $file_record = File::create([
  98. 'user_id' => Auth::id(),
  99. 'business_id' => $business->id,
  100. 'project_id' => $project->id,
  101. 'disk' => 's3', // default disk
  102. 'original_name' => $file->getClientOriginalName(),
  103. 'extension' => $file_extension,
  104. 'name' => $file_name,
  105. 'mime' => $file->getClientMimeType(),
  106. 'group' => $this->groupDetection($file),
  107. 'size' => $file->getSize(),
  108. 'description' => $request->description
  109. ]);
  110. return new FileResource($file_record);
  111. }
  112. public function groupDetection(UploadedFile $file)
  113. {
  114. // Media files like mp4, mp3, wma and png or jpeg
  115. [$type, $subtype] = Str::of($file->getMimeType())->explode("/",2)->pad(2, null);
  116. if (in_array($type, ['audio', 'video', 'image'])) {
  117. return $type;
  118. }
  119. // Covert string to \Illuminate\Support\Stringable object
  120. $subtype = Str::of($subtype);
  121. // PDF files
  122. if ($subtype->contains(["pdf"])) {
  123. return "pdf";
  124. }
  125. // Compressed files like zip, cab, rar, etc.
  126. if ($subtype->contains(['compressed']) || in_array($file->getClientOriginalExtension(), ['zip', 'rar','7z','cab'])) {
  127. return "compressed";
  128. }
  129. // Office files like xls, xlsx, doc, docx, etc.
  130. if ($subtype->contains(['vnd.ms', 'vnd.sealed', 'officedocument', 'opendocument'])) {
  131. return "office";
  132. }
  133. // Non of the above files
  134. return "other";
  135. }
  136. public function download(int $business, int $project, int $file)
  137. {
  138. // requested file belongs to this project and this business
  139. // check permisson
  140. // create perma link or temp link
  141. // return the file resource or stream it
  142. return File::findOrFail($file)->getTemporaryLink();
  143. }
  144. public function rename(Request $request, int $business, int $project, int $file)
  145. {
  146. // requested file belongs to this project and this business
  147. // check permisson
  148. // update original name
  149. // return the file resource
  150. // sanitize the name for slashs and back slashes
  151. $this->validate($request, [
  152. 'name' => 'required|string'
  153. ]);
  154. $file = File::findOrFail($file);
  155. $file->update(['original_name' => $request->name.".".$file->extension]);
  156. return new FileResource($file);
  157. }
  158. public function delete(Request $request, int $business, int $project, int $file)
  159. {
  160. // requested file belongs to this project and this business
  161. // check permisson
  162. // check it's relations
  163. // delete the file form File table
  164. // delete file from s3
  165. $file = File::findOrFail($file);
  166. Storage::disk('s3')->delete($file->getPath());
  167. return $file->delete();
  168. }
  169. }