Skip to content

Commit e4f9229

Browse files
authored
Merge pull request #40 from tattersoftware/handlers
Handlers
2 parents fdfae98 + 537430f commit e4f9229

File tree

17 files changed

+197
-165
lines changed

17 files changed

+197
-165
lines changed

.github/workflows/unused.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,4 @@ jobs:
6767
fi
6868
6969
- name: Detect unused packages
70-
run: composer-unused -vvv --profile --ansi --no-interaction --no-progress --excludePackage=php --excludePackage=tatter/alerts --excludePackage=tatter/preferences --excludePackage=tatter/thumbnails
70+
run: composer-unused -vvv --output-format=github --ansi --no-interaction --no-progress

composer-unused.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use ComposerUnused\ComposerUnused\Configuration\Configuration;
6+
use ComposerUnused\ComposerUnused\Configuration\NamedFilter;
7+
8+
return static function (Configuration $config): Configuration {
9+
return $config
10+
->addNamedFilter(NamedFilter::fromString('enyo/dropzone'))
11+
->setAdditionalFilesFor('tatter/preferences', [
12+
__DIR__ . '/vendor/tatter/preferences/src/Helpers/preferences_helper.php',
13+
]);
14+
};

composer.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,11 @@
2323
"php": "^7.4 || ^8.0",
2424
"codeigniter4/authentication-implementation": "^1.0",
2525
"enyo/dropzone": "^6.0",
26-
"tatter/alerts": "^2.0",
27-
"tatter/exports": "^2.0",
26+
"tatter/exports": "^3.0",
2827
"tatter/frontend": "^1.0",
2928
"tatter/permits": "^3.0",
3029
"tatter/preferences": "^1.0",
31-
"tatter/thumbnails": "^1.2"
30+
"tatter/thumbnails": "^2.0"
3231
},
3332
"require-dev": {
3433
"codeigniter4/devkit": "^1.0",

phpstan.neon.dist

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ parameters:
2222
- src/Helpers
2323
- vendor/codeigniter4/framework/system/Helpers
2424
- vendor/tatter/alerts/src/Helpers
25-
- vendor/tatter/handlers/src/Helpers
2625
- vendor/tatter/imposter/src/Helpers
2726
- vendor/tatter/preferences/src/Helpers
2827
dynamicConstantNames:

src/Config/Files.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,12 @@ public function getPath(): string
7777
$this->path = rtrim($storage, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
7878

7979
// Check or create the thumbnails subdirectory
80-
$thumbnails = $storage . 'thumbnails';
80+
$thumbnails = $this->path . 'thumbnails';
8181
if (! is_dir($thumbnails) && ! @mkdir($thumbnails, 0775, true)) {
8282
throw FilesException::forDirFail($thumbnails); // @codeCoverageIgnore
8383
}
8484

85-
return $storage;
85+
return $this->path;
8686
}
8787

8888
/**

src/Controllers/Files.php

Lines changed: 61 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
use CodeIgniter\HTTP\Exceptions\HTTPException;
88
use CodeIgniter\HTTP\RedirectResponse;
99
use CodeIgniter\HTTP\ResponseInterface;
10+
use RuntimeException;
1011
use Tatter\Exports\Exceptions\ExportsException;
12+
use Tatter\Exports\Factories\ExporterFactory;
1113
use Tatter\Files\Config\Files as FilesConfig;
1214
use Tatter\Files\Entities\File;
1315
use Tatter\Files\Exceptions\FilesException;
@@ -30,7 +32,7 @@ class Files extends Controller
3032
/**
3133
* Helpers to load.
3234
*/
33-
protected $helpers = ['alerts', 'files', 'handlers', 'html', 'preferences', 'text'];
35+
protected $helpers = ['alerts', 'files', 'html', 'preferences', 'text'];
3436

3537
/**
3638
* Overriding data for views.
@@ -139,7 +141,7 @@ public function user($userId = null)
139141
if ($userId === null) {
140142
// Check for list permission
141143
if (! $this->model->mayList()) {
142-
return $this->failure(403, lang('Permits.notPermitted'));
144+
return $this->failure(403, lang('Files.notPermitted'));
143145
}
144146

145147
$this->setData([
@@ -152,7 +154,7 @@ public function user($userId = null)
152154
elseif ((int) $userId !== user_id()) {
153155
// Check for list permission
154156
if (! $this->model->mayList()) {
155-
return $this->failure(403, lang('Permits.notPermitted'));
157+
return $this->failure(403, lang('Files.notPermitted'));
156158
}
157159

158160
$this->setData([
@@ -188,7 +190,7 @@ public function new()
188190
{
189191
// Check for create permission
190192
if (! $this->model->mayCreate()) {
191-
return $this->failure(403, lang('Permits.notPermitted'));
193+
return $this->failure(403, lang('Files.notPermitted'));
192194
}
193195

194196
return view('Tatter\Files\Views\new');
@@ -248,7 +250,7 @@ public function delete($fileId)
248250
return $this->failure(400, lang('Files.noFile'));
249251
}
250252
if (! $this->model->mayDelete($file)) {
251-
return $this->failure(403, lang('Permits.notPermitted'));
253+
return $this->failure(403, lang('Files.notPermitted'));
252254
}
253255

254256
if ($this->model->delete($fileId)) {
@@ -296,20 +298,21 @@ public function bulk(): ResponseInterface
296298
}
297299

298300
// Bulk export of some kind, match the handler
299-
if (! $handler = handlers('Exports')->where(['slug' => $action])->first()) {
300-
return $this->failure(400, 'No handler found for ' . $action);
301+
try {
302+
$handler = ExporterFactory::find($action);
303+
} catch (RuntimeException $e) {
304+
return $this->failure(400, 'No export handler found for ' . $action);
301305
}
302-
303-
$export = new $handler();
306+
$exporter = new $handler();
304307

305308
foreach ($fileIds as $fileId) {
306309
if ($file = $this->model->find($fileId)) {
307-
$export->setFile($file->object->setBasename($file->filename));
310+
$exporter->setFile($file->object->setBasename($file->filename));
308311
}
309312
}
310313

311314
try {
312-
$result = $export->process();
315+
$result = $exporter->process();
313316
} catch (ExportsException $e) {
314317
return $this->failure(400, $e->getMessage());
315318
}
@@ -328,7 +331,7 @@ public function upload()
328331
{
329332
// Check for create permission
330333
if (! $this->model->mayCreate()) {
331-
return $this->failure(403, lang('Permits.notPermitted'));
334+
return $this->failure(403, lang('Files.notPermitted'));
332335
}
333336

334337
// Verify upload succeeded
@@ -394,7 +397,13 @@ public function upload()
394397
$data['clientname'] ??= $upload->getClientName();
395398

396399
// Accept the file
397-
$file = $this->model->createFromPath($path ?? $upload->getRealPath(), $data);
400+
try {
401+
$file = $this->model->createFromPath($path ?? $upload->getRealPath(), $data);
402+
} catch (Throwable $e) {
403+
log_message('error', $e->getMessage());
404+
405+
return $this->failure(400, $e->getMessage());
406+
}
398407

399408
// Trigger the Event with the new File
400409
Events::trigger('upload', $file);
@@ -411,22 +420,21 @@ public function upload()
411420
/**
412421
* Processes Export requests.
413422
*
414-
* @param string $slug The slug to match to Exports attribute
415423
* @param int|string $fileId
416424
*/
417-
public function export(string $slug, $fileId): ResponseInterface
425+
public function export(string $handlerId, $fileId): ResponseInterface
418426
{
419427
// Match the export handler
420-
$handler = handlers('Exports')->where(['slug' => $slug])->first();
421-
if (empty($handler)) {
422-
alert('warning', 'No handler found for ' . $slug);
428+
try {
429+
$handler = ExporterFactory::find($handlerId);
430+
} catch (RuntimeException $e) {
431+
alert('warning', 'No export handler found for ' . $handlerId);
423432

424433
return redirect()->back();
425434
}
426435

427436
// Load the file
428-
$file = $this->model->find($fileId);
429-
if (empty($file)) {
437+
if (empty($fileId) || null === $file = $this->model->find($fileId)) {
430438
alert('warning', lang('Files.noFile'));
431439

432440
return redirect()->back();
@@ -442,21 +450,22 @@ public function export(string $slug, $fileId): ResponseInterface
442450

443451
// Create the record
444452
model(ExportModel::class)->insert([
445-
'handler' => $slug,
453+
'handler' => $handlerId,
446454
'file_id' => $file->id,
447455
'user_id' => user_id(),
448456
]);
449457

450458
// Pass to the handler
451-
$export = new $handler($file->object);
452-
$response = $export->setFilename($file->filename)->process();
459+
$exporter = new $handler($file->object);
460+
$exporter->setFilename($file->filename);
453461

454-
// If the handler returned a response then we're done
455-
if ($response instanceof ResponseInterface) {
456-
return $response;
462+
try {
463+
$result = $exporter->process();
464+
} catch (ExportsException $e) {
465+
return $this->failure(400, $e->getMessage());
457466
}
458467

459-
return redirect()->back();
468+
return $result;
460469
}
461470

462471
/**
@@ -466,9 +475,13 @@ public function export(string $slug, $fileId): ResponseInterface
466475
*/
467476
public function thumbnail($fileId): ResponseInterface
468477
{
469-
$path = ($file = $this->model->find($fileId)) ? $file->getThumbnail() : File::locateDefaultThumbnail();
478+
$path = ($file = $this->model->find($fileId))
479+
? $file->getThumbnail()
480+
: File::locateDefaultThumbnail();
470481

471-
return $this->response->setHeader('Content-type', 'image/jpeg')->setBody(file_get_contents($path));
482+
return $this->response
483+
->setHeader('Content-type', 'image/jpeg')
484+
->setBody(file_get_contents($path));
472485
}
473486

474487
/**
@@ -510,9 +523,26 @@ protected function setData(array $data, bool $overwrite = false): self
510523
*/
511524
protected function setDefaults(): self
512525
{
526+
// Get bulk support and index Exporters by the extension(s) they support
527+
$bulks = [];
528+
$exporters = [];
529+
530+
foreach (ExporterFactory::findAll() as $handler) {
531+
$attributes = $handler::attributes();
532+
533+
if ($attributes['bulk']) {
534+
$bulks[] = $handler;
535+
}
536+
537+
foreach ($attributes['extensions'] as $extension) {
538+
$exporters[$extension][] = $attributes;
539+
}
540+
}
541+
513542
$this->setData([
514543
'source' => 'index',
515544
'layout' => 'files',
545+
'model' => $this->model,
516546
'files' => null,
517547
'selected' => explode(',', $this->request->getVar('selected') ?? ''),
518548
'userId' => null,
@@ -522,8 +552,8 @@ protected function setDefaults(): self
522552
'page' => $this->request->getVar('page'),
523553
'pager' => null,
524554
'access' => $this->model->mayAdmin() ? 'manage' : 'display',
525-
'exports' => $this->getExports(),
526-
'bulks' => handlers()->where(['bulk' => 1])->findAll(),
555+
'exports' => $exporters,
556+
'bulks' => $bulks,
527557
]);
528558

529559
// Add preferences
@@ -563,27 +593,4 @@ protected function setPreferences(): self
563593

564594
return $this;
565595
}
566-
567-
/**
568-
* Gets Export handlers indexed by the extension they support.
569-
*
570-
* @return array<string, array>
571-
*/
572-
protected function getExports(): array
573-
{
574-
$exports = [];
575-
576-
foreach (handlers('Exports')->findAll() as $class) {
577-
$attributes = handlers()->getAttributes($class);
578-
579-
// Add the class name for easy access later
580-
$attributes['class'] = $class;
581-
582-
foreach (explode(',', $attributes['extensions']) as $extension) {
583-
$exports[$extension][] = $attributes;
584-
}
585-
}
586-
587-
return $exports;
588-
}
589596
}

src/Entities/File.php

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ class File extends Entity
1414
'updated_at',
1515
'deleted_at',
1616
];
17+
protected $attributes = [
18+
'thumbnail' => '',
19+
];
1720

1821
/**
1922
* Resolved path to the default thumbnail
@@ -92,38 +95,13 @@ public function getObject(): ?FileObject
9295
}
9396
}
9497

95-
/**
96-
* Returns class names of Exports applicable to this file's extension
97-
*
98-
* @param bool $asterisk Whether to include generic "*" extensions
99-
*
100-
* @return string[]
101-
*/
102-
public function getExports($asterisk = true): array
103-
{
104-
$exports = [];
105-
106-
if ($extension = $this->getExtension()) {
107-
$exports = handlers('Exports')->where(['extensions has' => $extension])->findAll();
108-
}
109-
110-
if ($asterisk) {
111-
$exports = array_merge(
112-
$exports,
113-
handlers('Exports')->where(['extensions' => '*'])->findAll()
114-
);
115-
}
116-
117-
return $exports;
118-
}
119-
12098
/**
12199
* Returns the path to this file's thumbnail, or the default from config.
122100
* Should always return a path to a valid file to be safe for img_data()
123101
*/
124102
public function getThumbnail(): string
125103
{
126-
$path = config('Files')->getPath() . 'thumbnails' . DIRECTORY_SEPARATOR . ($this->attributes['thumbnail'] ?? '');
104+
$path = config('Files')->getPath() . 'thumbnails' . DIRECTORY_SEPARATOR . $this->attributes['thumbnail'];
127105

128106
if (! is_file($path)) {
129107
$path = self::locateDefaultThumbnail();

src/Language/en/Files.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
return [
66
// Exceptions
7+
'notPermitted' => 'You do not have permission to do that.',
78
'noAuth' => 'Missing dependency: authentication function user_id()',
89
'dirFail' => 'Unable to create storage directory: {0}',
910
'chunkDirFail' => 'Unable to create directory for chunk uploads: {0}',

0 commit comments

Comments
 (0)