1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Yajra\CMS\Http\Controllers; |
4
|
|
|
|
5
|
|
|
use App\Http\Requests; |
6
|
|
|
use Illuminate\Config\Repository; |
7
|
|
|
use Illuminate\Contracts\Filesystem\Filesystem; |
8
|
|
|
use Illuminate\Database\Eloquent\Collection; |
9
|
|
|
use Illuminate\Http\Request; |
10
|
|
|
use Illuminate\Support\Facades\File; |
11
|
|
|
use Intervention\Image\ImageManager; |
12
|
|
|
|
13
|
|
|
class MediaController extends Controller |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* Filesystem interface. |
17
|
|
|
* |
18
|
|
|
* @var \Illuminate\Contracts\Filesystem\Filesystem |
19
|
|
|
*/ |
20
|
|
|
protected $storage; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Image manager. |
24
|
|
|
* |
25
|
|
|
* @var \Intervention\Image\ImageManager |
26
|
|
|
*/ |
27
|
|
|
protected $image; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Config repository. |
31
|
|
|
* |
32
|
|
|
* @var \Illuminate\Config\Repository |
33
|
|
|
*/ |
34
|
|
|
protected $config; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Media manager template. |
38
|
|
|
* |
39
|
|
|
* @var string |
40
|
|
|
*/ |
41
|
|
|
protected $template = 'admin::layouts.master'; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Files listing filter. |
45
|
|
|
* |
46
|
|
|
* @var null|string |
47
|
|
|
*/ |
48
|
|
|
protected $filter = null; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Current dir the user is browsing. |
52
|
|
|
* |
53
|
|
|
* @var string |
54
|
|
|
*/ |
55
|
|
|
protected $currentDir; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @param \Illuminate\Contracts\Filesystem\Filesystem $storage |
59
|
|
|
* @param \Intervention\Image\ImageManager $image |
60
|
|
|
* @param \Illuminate\Config\Repository $config |
61
|
|
|
*/ |
62
|
|
|
public function __construct(Filesystem $storage, ImageManager $image, Repository $config) |
63
|
|
|
{ |
64
|
|
|
$this->storage = $storage; |
65
|
|
|
$this->image = $image; |
66
|
|
|
$this->config = $config; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Display media browser from CKEditor. |
71
|
|
|
* |
72
|
|
|
* @param \Illuminate\Http\Request $request |
73
|
|
|
* @param string|null $filter |
74
|
|
|
* @return \Illuminate\Http\Response |
75
|
|
|
*/ |
76
|
|
|
public function browse(Request $request, $filter = null) |
77
|
|
|
{ |
78
|
|
|
$this->template = 'admin::layouts.component'; |
79
|
|
|
$this->filter = $filter; |
80
|
|
|
|
81
|
|
|
return $this->index($request); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Display a listing of the resource. |
86
|
|
|
* |
87
|
|
|
* @param \Illuminate\Http\Request $request |
88
|
|
|
* @return \Illuminate\Http\Response |
89
|
|
|
*/ |
90
|
|
|
public function index(Request $request) |
91
|
|
|
{ |
92
|
|
|
$storage_root = $this->getRootDir(); |
93
|
|
|
$accepted_files = $this->config->get('media.accepted_files'); |
94
|
|
|
$max_file_size = $this->config->get('media.max_file_size', 3); |
95
|
|
|
$this->currentDir = $this->getRootDir(); |
96
|
|
|
|
97
|
|
|
if ($request->input('folder')) { |
98
|
|
|
$this->currentDir = $request->input('folder'); |
|
|
|
|
99
|
|
|
if ($this->storage->exists($this->currentDir) === false) { |
|
|
|
|
100
|
|
|
return redirect()->route('administrator.media.index'); |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
$items = $this->buildFileAndDirectoryListing(); |
105
|
|
|
|
106
|
|
|
$directories = $this->buildDirectory( |
107
|
|
|
$this->scanDirectory($this->getRootDir()), |
108
|
|
|
$this->getRootDir() |
109
|
|
|
); |
110
|
|
|
|
111
|
|
|
return view('administrator.media.index') |
|
|
|
|
112
|
|
|
->with('template', $this->template) |
113
|
|
|
->with('items', $items) |
114
|
|
|
->with('current_directory', $this->currentDir) |
115
|
|
|
->with('directories', $directories) |
116
|
|
|
->with('accepted_files', $accepted_files) |
117
|
|
|
->with('storage_root', $storage_root) |
118
|
|
|
->with('max_file_size', $max_file_size); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Get root directory. |
123
|
|
|
* |
124
|
|
|
* @return string |
125
|
|
|
*/ |
126
|
|
|
protected function getRootDir() |
127
|
|
|
{ |
128
|
|
|
return 'public/' . $this->config->get('media.root_dir', 'media'); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Build files and directory collection for the current directory. |
133
|
|
|
* |
134
|
|
|
* @return \Illuminate\Database\Eloquent\Collection |
135
|
|
|
*/ |
136
|
|
|
protected function buildFileAndDirectoryListing() |
137
|
|
|
{ |
138
|
|
|
$collection = new Collection; |
139
|
|
|
$files = $this->scanFiles($this->currentDir); |
140
|
|
|
$thumbnails = $this->buildThumbnails($files); |
141
|
|
|
$directories = $this->scanDirectory($this->currentDir); |
142
|
|
|
|
143
|
|
|
if ($this->getRootDir() <> $this->currentDir) { |
144
|
|
|
$back_directory = implode('/', explode('/', $this->currentDir, -1)); |
145
|
|
|
$item = []; |
146
|
|
|
$item['thumbnail'] = ''; |
147
|
|
|
$item['filename'] = '...'; |
148
|
|
|
$item['size'] = ''; |
149
|
|
|
$item['type'] = 'back'; |
150
|
|
|
$item['icon'] = 'fa-reply'; |
151
|
|
|
$item['path'] = $this->currentDir; |
152
|
|
|
$item['url'] = '?folder=' . $back_directory; |
153
|
|
|
$item['delete'] = current_user()->can('media.delete'); |
|
|
|
|
154
|
|
|
$item['select'] = current_user()->can('media.view'); |
|
|
|
|
155
|
|
|
$collection->add($item); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
foreach ($directories as $directory) { |
159
|
|
|
$directory_name = explode('/', $directory); |
160
|
|
|
$directory_name = array_pop($directory_name); |
161
|
|
|
|
162
|
|
|
$item = []; |
163
|
|
|
$item['thumbnail'] = ''; |
164
|
|
|
$item['filename'] = $directory_name; |
165
|
|
|
$item['path'] = $directory; |
166
|
|
|
$item['size'] = ''; |
167
|
|
|
$item['type'] = 'directory'; |
168
|
|
|
$item['icon'] = 'fa-folder'; |
169
|
|
|
$item['url'] = '?folder=' . $directory; |
170
|
|
|
$item['delete'] = current_user()->can('media.delete'); |
|
|
|
|
171
|
|
|
$item['select'] = current_user()->can('media.view'); |
|
|
|
|
172
|
|
|
|
173
|
|
|
$collection->add($item); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
foreach ($files as $path) { |
177
|
|
|
$item = []; |
178
|
|
|
$parts = explode('/', $path); |
179
|
|
|
$filename = array_pop($parts); |
180
|
|
|
$size = $this->storage->size($path); |
181
|
|
|
$icon = file_ext_to_icon(File::extension($path)); |
182
|
|
|
|
183
|
|
|
$item['type'] = 'file'; |
184
|
|
|
$item['icon'] = $icon; |
185
|
|
|
$item['thumbnail'] = ''; |
186
|
|
|
if (file_can_have_thumbnail($path)) { |
187
|
|
|
$item['type'] = 'image'; |
188
|
|
|
$item['icon'] = 'fa-image'; |
189
|
|
|
$item['thumbnail'] = $thumbnails[$path]; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
$item['filename'] = $filename; |
193
|
|
|
$item['size'] = $size; |
194
|
|
|
$item['url'] = preg_replace("/^public/", '', $path); |
195
|
|
|
$item['path'] = $path; |
196
|
|
|
$item['delete'] = current_user()->can('media.delete'); |
|
|
|
|
197
|
|
|
$item['select'] = current_user()->can('media.view'); |
|
|
|
|
198
|
|
|
$collection->add($item); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
return $collection; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Scan files of the given directory. |
206
|
|
|
* |
207
|
|
|
* @param $current_directory |
208
|
|
|
* @return \Illuminate\Support\Collection |
209
|
|
|
*/ |
210
|
|
|
protected function scanFiles($current_directory) |
211
|
|
|
{ |
212
|
|
|
$files = collect($this->storage->files($current_directory)); |
213
|
|
|
$files = $files->filter(function ($file) { |
214
|
|
|
$ext = File::extension($file); |
215
|
|
|
|
216
|
|
|
if ($this->filter === 'images') { |
217
|
|
|
return in_array($ext, $this->config->get('media.images_ext')); |
218
|
|
|
} elseif ($this->filter === 'files') { |
219
|
|
|
return in_array($ext, $this->config->get('media.files_ext')); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
return true; |
223
|
|
|
}); |
224
|
|
|
|
225
|
|
|
return $files; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Build thumbnails for each files that is image type. |
230
|
|
|
* |
231
|
|
|
* @param \Illuminate\Support\Collection $files |
232
|
|
|
* @return array |
233
|
|
|
*/ |
234
|
|
|
protected function buildThumbnails($files) |
235
|
|
|
{ |
236
|
|
|
$thumbnails = []; |
237
|
|
|
|
238
|
|
|
foreach ($files as $file) { |
239
|
|
|
if (file_can_have_thumbnail($file)) { |
240
|
|
|
$thumbnails[$file] = (string) $this->image |
241
|
|
|
->make($this->storage->get($file)) |
242
|
|
|
->resize(20, 20)->encode('data-url'); |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
return $thumbnails; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
/** |
250
|
|
|
* Scan the directory. |
251
|
|
|
* |
252
|
|
|
* @param string $dir |
253
|
|
|
* @return \Illuminate\Support\Collection |
254
|
|
|
*/ |
255
|
|
|
protected function scanDirectory($dir) |
256
|
|
|
{ |
257
|
|
|
return collect($this->storage->directories($dir)); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Build the directory lists. |
262
|
|
|
* |
263
|
|
|
* @param \Illuminate\Support\Collection $directories |
264
|
|
|
* @param string $parent |
265
|
|
|
* @return string |
266
|
|
|
*/ |
267
|
|
|
protected function buildDirectory($directories, $parent) |
268
|
|
|
{ |
269
|
|
|
$directories = $directories->map(function ($dir) use ($parent) { |
270
|
|
|
$dir = str_replace($parent . '/', '', $dir); |
271
|
|
|
|
272
|
|
|
return $dir; |
273
|
|
|
}); |
274
|
|
|
|
275
|
|
|
$html = '<ul>'; |
276
|
|
|
$directories->each(function ($dir) use (&$html, $parent) { |
277
|
|
|
$subParent = $parent . '/' . $dir; |
278
|
|
|
$url = request()->url() . '?folder=' . $dir; |
279
|
|
|
|
280
|
|
|
$html .= "<li>"; |
281
|
|
|
$html .= "<a href='{$url}'>{$dir}</a>"; |
282
|
|
|
if ($child = $this->scanDirectory($subParent)) { |
283
|
|
|
$html .= $this->buildDirectory($child, $subParent); |
284
|
|
|
} |
285
|
|
|
$html .= "</li>"; |
286
|
|
|
}); |
287
|
|
|
$html .= '</ul>'; |
288
|
|
|
|
289
|
|
|
return $html; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Upload a file. |
294
|
|
|
* |
295
|
|
|
* @param \Illuminate\Http\Request $request |
296
|
|
|
* @return \Illuminate\Http\RedirectResponse |
297
|
|
|
*/ |
298
|
|
|
public function addFile(Request $request) |
299
|
|
|
{ |
300
|
|
|
$maxSize = $this->config->get('media.max_file_size', 3); |
301
|
|
|
$validator = $this->getValidationFactory()->make($request->all(), [ |
302
|
|
|
'file' => 'required|mimes:' . $this->getAcceptedFiles() . '|max:' . $maxSize * 1024, |
303
|
|
|
]); |
304
|
|
|
|
305
|
|
|
if ($validator->fails()) { |
306
|
|
|
return response($validator->getMessageBag()->get('file')[0], 500); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
$filename = trim($request->file('file')->getClientOriginalName()); |
310
|
|
|
if ($this->storage->exists($request->input('current_directory') . '/' . $filename)) { |
311
|
|
|
return response($filename . " already exists.", 500); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
$this->storage->put( |
315
|
|
|
$request->input('current_directory') . '/' . $filename, |
316
|
|
|
file_get_contents($request->file('file')) |
317
|
|
|
); |
318
|
|
|
|
319
|
|
|
return redirect()->back(); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Get accepted files for upload. |
324
|
|
|
* |
325
|
|
|
* @return string |
326
|
|
|
*/ |
327
|
|
|
protected function getAcceptedFiles() |
328
|
|
|
{ |
329
|
|
|
$accepted_files = str_replace(' ', '', |
330
|
|
|
str_replace('.', '', $this->config->get('media.accepted_files')) |
331
|
|
|
); |
332
|
|
|
|
333
|
|
|
return $accepted_files; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Create a folder. |
338
|
|
|
* |
339
|
|
|
* @param \Illuminate\Http\Request $request |
340
|
|
|
* @return \Illuminate\Http\RedirectResponse |
341
|
|
|
*/ |
342
|
|
|
public function addFolder(Request $request) |
343
|
|
|
{ |
344
|
|
|
// check if public/media is the parent folder |
345
|
|
|
if (strpos($request->input('current_directory'), $this->getRootDir()) === false) { |
346
|
|
|
flash()->error('Invalid folder!'); |
347
|
|
|
|
348
|
|
|
return redirect()->back(); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
// check if alphanumeric and no space |
352
|
|
|
if (! ctype_alnum($request->input('new_directory'))) { |
353
|
|
|
flash()->error('Invalid folder name! Folder name must be alphanumeric only and no space'); |
354
|
|
|
|
355
|
|
|
return redirect()->back(); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
$this->storage->makeDirectory( |
359
|
|
|
$request->input('current_directory') . '/' . $request->input('new_directory') |
360
|
|
|
); |
361
|
|
|
|
362
|
|
|
// new current directory |
363
|
|
|
$folder = $request->input('current_directory') . '/' . $request->input('new_directory'); |
364
|
|
|
flash()->success('New Folder added successfully!'); |
365
|
|
|
|
366
|
|
|
return redirect()->back()->with('folder', $folder); |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Delete a file(/s) and/or folder(/s). |
371
|
|
|
* |
372
|
|
|
* @param \Illuminate\Http\Request $request |
373
|
|
|
* @return \Illuminate\Http\RedirectResponse |
374
|
|
|
*/ |
375
|
|
|
public function deleteMedia(Request $request) |
376
|
|
|
{ |
377
|
|
|
// if single file/folder '?s=name' |
378
|
|
|
if ($request->has('s')) { |
379
|
|
|
if (is_dir(storage_path('app') . DIRECTORY_SEPARATOR . $request->input('s'))) { |
380
|
|
|
return $this->deleteDirectory($request); |
381
|
|
|
} else { |
382
|
|
|
return $this->deleteFile($request); |
383
|
|
|
} |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
// if multiple files |
387
|
|
|
if ($request->has('files')) { |
388
|
|
|
$this->storage->delete($request->input('files')); |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
// if multiple directories |
392
|
|
|
if ($request->has('directories')) { |
393
|
|
|
if (is_array($request->input('directories'))) { |
394
|
|
|
foreach ($request->input('directories') as $directory) { |
395
|
|
|
$this->storage->deleteDirectory($directory); |
396
|
|
|
} |
397
|
|
|
} else { |
398
|
|
|
$this->storage->deleteDirectory($request->input('directories')); |
399
|
|
|
} |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
if ($request->wantsJson()) { |
403
|
|
|
return $this->notifySuccess('File/Folder deleted successfully!'); |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
flash()->success('File/Folder deleted successfully!'); |
407
|
|
|
|
408
|
|
|
return redirect()->back(); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* Delete a single directory from request. |
413
|
|
|
* |
414
|
|
|
* @param \Illuminate\Http\Request $request |
415
|
|
|
* @return \Illuminate\Http\JsonResponse |
416
|
|
|
*/ |
417
|
|
View Code Duplication |
protected function deleteDirectory(Request $request) |
|
|
|
|
418
|
|
|
{ |
419
|
|
|
try { |
420
|
|
|
$dir = $request->input('s'); |
421
|
|
|
$this->storage->deleteDirectory($dir); |
|
|
|
|
422
|
|
|
|
423
|
|
|
return $this->notifySuccess($dir . ' successfully deleted!'); |
424
|
|
|
} catch (\ErrorException $e) { |
|
|
|
|
425
|
|
|
return $this->notifyError($e->getMessage()); |
426
|
|
|
} |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
/** |
430
|
|
|
* Delete a single file from request. |
431
|
|
|
* |
432
|
|
|
* @param \Illuminate\Http\Request $request |
433
|
|
|
* @return \Illuminate\Http\JsonResponse |
434
|
|
|
*/ |
435
|
|
View Code Duplication |
protected function deleteFile(Request $request) |
|
|
|
|
436
|
|
|
{ |
437
|
|
|
try { |
438
|
|
|
$file = $request->input('s'); |
439
|
|
|
$this->storage->delete($file); |
440
|
|
|
|
441
|
|
|
return $this->notifySuccess($file . ' successfully deleted!'); |
442
|
|
|
} catch (\ErrorException $e) { |
|
|
|
|
443
|
|
|
return $this->notifyError($e->getMessage()); |
444
|
|
|
} |
445
|
|
|
} |
446
|
|
|
} |
447
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.