/
Media.php
172 lines (149 loc) · 5.03 KB
/
Media.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
<?php
namespace Kirby\Cms;
use Kirby\Data\Data;
use Kirby\Filesystem\Dir;
use Kirby\Filesystem\F;
use Kirby\Toolkit\Str;
use Throwable;
/**
* Handles all tasks to get the Media API
* up and running and link files correctly
*
* @package Kirby Cms
* @author Bastian Allgeier <bastian@getkirby.com>
* @link https://getkirby.com
* @copyright Bastian Allgeier
* @license https://getkirby.com/license
*/
class Media
{
/**
* Tries to find a file by model and filename
* and to copy it to the media folder.
*
* @param \Kirby\Cms\Model|null $model
* @param string $hash
* @param string $filename
* @return \Kirby\Cms\Response|false
*/
public static function link(Model $model = null, string $hash, string $filename)
{
if ($model === null) {
return false;
}
// fix issues with spaces in filenames
$filename = urldecode($filename);
// try to find a file by model and filename
// this should work for all original files
if ($file = $model->file($filename)) {
// check if the request contained an outdated media hash
if ($file->mediaHash() !== $hash) {
// if at least the token was correct, redirect
if (Str::startsWith($hash, $file->mediaToken() . '-') === true) {
return Response::redirect($file->mediaUrl(), 307);
} else {
// don't leak the correct token, render the error page
return false;
}
}
// send the file to the browser
return Response::file($file->publish()->mediaRoot());
}
// try to generate a thumb for the file
return static::thumb($model, $hash, $filename);
}
/**
* Copy the file to the final media folder location
*
* @param \Kirby\Cms\File $file
* @param string $dest
* @return bool
*/
public static function publish(File $file, string $dest): bool
{
// never publish risky files (e.g. HTML, PHP or Apache config files)
FileRules::validFile($file, false);
$src = $file->root();
$version = dirname($dest);
$directory = dirname($version);
// unpublish all files except stuff in the version folder
Media::unpublish($directory, $file, $version);
// copy/overwrite the file to the dest folder
return F::copy($src, $dest, true);
}
/**
* Tries to find a job file for the
* given filename and then calls the thumb
* component to create a thumbnail accordingly
*
* @param \Kirby\Cms\Model|string $model
* @param string $hash
* @param string $filename
* @return \Kirby\Cms\Response|false
*/
public static function thumb($model, string $hash, string $filename)
{
$kirby = App::instance();
// assets
if (is_string($model) === true) {
$root = $kirby->root('media') . '/assets/' . $model . '/' . $hash;
// parent files for file model that already included hash
} elseif (is_a($model, '\Kirby\Cms\File')) {
$root = dirname($model->mediaRoot());
// model files
} else {
$root = $model->mediaRoot() . '/' . $hash;
}
try {
$thumb = $root . '/' . $filename;
$job = $root . '/.jobs/' . $filename . '.json';
$options = Data::read($job);
if (empty($options) === true) {
return false;
}
if (is_string($model) === true) {
$source = $kirby->root('index') . '/' . $model . '/' . $options['filename'];
} else {
$source = $model->file($options['filename'])->root();
}
try {
$kirby->thumb($source, $thumb, $options);
F::remove($job);
return Response::file($thumb);
} catch (Throwable $e) {
F::remove($thumb);
return Response::file($source);
}
} catch (Throwable $e) {
return false;
}
}
/**
* Deletes all versions of the given file
* within the parent directory
*
* @param string $directory
* @param \Kirby\Cms\File $file
* @param string|null $ignore
* @return bool
*/
public static function unpublish(string $directory, File $file, string $ignore = null): bool
{
if (is_dir($directory) === false) {
return true;
}
// get both old and new versions (pre and post Kirby 3.4.0)
$versions = array_merge(
glob($directory . '/' . crc32($file->filename()) . '-*', GLOB_ONLYDIR),
glob($directory . '/' . $file->mediaToken() . '-*', GLOB_ONLYDIR)
);
// delete all versions of the file
foreach ($versions as $version) {
if ($version === $ignore) {
continue;
}
Dir::remove($version);
}
return true;
}
}