1 | <?php |
||||
2 | /** |
||||
3 | * AssetPond plugin for Craft CMS 3.x |
||||
4 | * |
||||
5 | * Instant FilePond server that works with Craft Assets. |
||||
6 | * |
||||
7 | * @link https://workingconcept.com |
||||
8 | * @copyright Copyright (c) 2019 Working Concept |
||||
9 | */ |
||||
10 | |||||
11 | namespace workingconcept\assetpond\services; |
||||
12 | |||||
13 | use craft\models\VolumeFolder; |
||||
14 | use craft\web\UploadedFile; |
||||
15 | use workingconcept\assetpond\AssetPond; |
||||
16 | |||||
17 | use Craft; |
||||
18 | use craft\elements\Asset; |
||||
19 | use craft\helpers\Assets; |
||||
20 | use craft\base\Component; |
||||
21 | use craft\errors\AssetException; |
||||
22 | |||||
23 | /** |
||||
24 | * @author Working Concept |
||||
25 | * @package AssetPond |
||||
26 | * @since 1.0.0 |
||||
27 | */ |
||||
28 | class Server extends Component |
||||
29 | { |
||||
30 | // Public Methods |
||||
31 | // ========================================================================= |
||||
32 | |||||
33 | /** |
||||
34 | * Saves uploaded files as Assets. |
||||
35 | * |
||||
36 | * @param array $files file contents |
||||
37 | * @param array $meta file metadata |
||||
38 | * @param int $volumeId folder ID for upload |
||||
39 | * |
||||
40 | * @return bool|int Asset ID or false |
||||
41 | */ |
||||
42 | public function handleFileTransfer($files, $meta, $volumeId = null) |
||||
0 ignored issues
–
show
|
|||||
43 | { |
||||
44 | if (count($files) === 0) |
||||
45 | { |
||||
46 | return false; |
||||
47 | } |
||||
48 | |||||
49 | try |
||||
50 | { |
||||
51 | $assets = Craft::$app->getAssets(); |
||||
52 | |||||
53 | if ($volumeId === null) |
||||
54 | { |
||||
55 | $volumeId = AssetPond::$plugin->getSettings()->defaultVolumeId; |
||||
56 | } |
||||
57 | |||||
58 | $folder = $assets->getRootFolderByVolumeId($volumeId); |
||||
59 | |||||
60 | // TODO: handle metadata, which can direct tranforms, etc. |
||||
61 | |||||
62 | // only save the first file for now, since others would be variants |
||||
63 | $file = $files[0]; |
||||
64 | |||||
65 | $asset = $this->_getNewAssetFromFile( |
||||
66 | $file['tmp_name'], |
||||
67 | $file['name'], |
||||
68 | $folder |
||||
69 | ); |
||||
70 | |||||
71 | $result = Craft::$app->getElements()->saveElement($asset); |
||||
72 | |||||
73 | if ( ! $result) |
||||
74 | { |
||||
75 | // TODO: handle errors |
||||
76 | $errors = $asset->getFirstErrors(); |
||||
0 ignored issues
–
show
|
|||||
77 | return false; |
||||
78 | } |
||||
79 | |||||
80 | return $asset->id; |
||||
81 | } |
||||
82 | catch (\Throwable $e) |
||||
83 | { |
||||
84 | Craft::error('An error occurred when saving an Asset: ' . $e->getMessage(), __METHOD__); |
||||
85 | Craft::$app->getErrorHandler()->logException($e); |
||||
86 | return false; |
||||
87 | } |
||||
88 | } |
||||
89 | |||||
90 | /** |
||||
91 | * Deletes an Asset. |
||||
92 | * |
||||
93 | * @param int $assetId |
||||
94 | * @return bool |
||||
95 | * @throws |
||||
96 | */ |
||||
97 | public function handleRevertFileTransfer($assetId): bool |
||||
98 | { |
||||
99 | $asset = Craft::$app->getAssets()->getAssetById($assetId); |
||||
100 | |||||
101 | if (! $asset) |
||||
0 ignored issues
–
show
|
|||||
102 | { |
||||
103 | // doesn't exist |
||||
104 | return false; |
||||
105 | } |
||||
106 | |||||
107 | //$this->_requirePermissionByAsset('deleteFilesAndFoldersInVolume', $asset); |
||||
108 | |||||
109 | try { |
||||
110 | Craft::$app->getElements()->deleteElement($asset); |
||||
111 | } catch (AssetException $exception) { |
||||
112 | //return $this->asErrorJson($exception->getMessage()); |
||||
113 | return false; |
||||
114 | } |
||||
115 | |||||
116 | return true; |
||||
117 | } |
||||
118 | |||||
119 | /** |
||||
120 | * @param int $assetId |
||||
121 | * @return Asset|false |
||||
122 | */ |
||||
123 | public function handleRestoreFileTransfer($assetId) |
||||
124 | { |
||||
125 | $asset = Craft::$app->getAssets()->getAssetById($assetId); |
||||
126 | |||||
127 | if (! $asset) |
||||
0 ignored issues
–
show
|
|||||
128 | { |
||||
129 | // doesn't exist |
||||
130 | return false; |
||||
131 | } |
||||
132 | |||||
133 | return $asset; |
||||
134 | } |
||||
135 | |||||
136 | /** |
||||
137 | * @param int $assetId |
||||
138 | * @return Asset|false |
||||
139 | */ |
||||
140 | public function handleLoadLocalFile($assetId) |
||||
141 | { |
||||
142 | $asset = Craft::$app->getAssets()->getAssetById($assetId); |
||||
143 | |||||
144 | if (! $asset) |
||||
0 ignored issues
–
show
|
|||||
145 | { |
||||
146 | // doesn't exist |
||||
147 | return false; |
||||
148 | } |
||||
149 | |||||
150 | return $asset; |
||||
151 | } |
||||
152 | |||||
153 | /** |
||||
154 | * Decodes base64-encoded files and turns them into Assets. |
||||
155 | * https://github.com/pqina/filepond-plugin-file-encode |
||||
156 | * |
||||
157 | * @param array $files |
||||
158 | * @param int $volumeId |
||||
159 | * |
||||
160 | * Each item in the array should be in the following format: |
||||
161 | * { |
||||
162 | * "id": "iuhv2cpsu", |
||||
163 | * "name": "picture.jpg", |
||||
164 | * "type": "image/jpeg", |
||||
165 | * "size": 20636, |
||||
166 | * "metadata" : {...} |
||||
167 | * "data": "/9j/4AAQSkZJRgABAQEASABIAA..." |
||||
168 | * } |
||||
169 | * |
||||
170 | * @throws |
||||
171 | * @return array |
||||
172 | */ |
||||
173 | public function handleBase64EncodedFilePost($files, $volumeId = null): array |
||||
174 | { |
||||
175 | if ($volumeId === null) |
||||
176 | { |
||||
177 | $volumeId = AssetPond::$plugin->getSettings()->defaultVolumeId; |
||||
178 | } |
||||
179 | |||||
180 | $folder = Craft::$app->getAssets()->getRootFolderByVolumeId($volumeId); |
||||
181 | $result = []; |
||||
182 | |||||
183 | foreach ($files as $file) |
||||
184 | { |
||||
185 | if ($asset = $this->base64FilePostToAsset($file, $folder)) |
||||
186 | { |
||||
187 | $result[] = $asset; |
||||
188 | } |
||||
189 | } |
||||
190 | |||||
191 | return $result; |
||||
192 | } |
||||
193 | |||||
194 | /** |
||||
195 | * @param object $file uploaded, decoded file object |
||||
196 | * @param VolumeFolder $folder destination folder for Asset |
||||
197 | * @return Asset|false |
||||
198 | */ |
||||
199 | public function base64FilePostToAsset($file, $folder) |
||||
200 | { |
||||
201 | if ( ! $tempFile = self::base64FilePostToUploadedFile($file)) |
||||
0 ignored issues
–
show
$file of type object is incompatible with the type string expected by parameter $data of workingconcept\assetpond...ilePostToUploadedFile() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
202 | { |
||||
203 | return false; |
||||
204 | } |
||||
205 | |||||
206 | try |
||||
207 | { |
||||
208 | $filename = Assets::prepareAssetName($tempFile->name); |
||||
209 | |||||
210 | $asset = $this->_getNewAssetFromFile( |
||||
211 | $tempFile->tempName, |
||||
212 | $filename, |
||||
213 | $folder |
||||
214 | ); |
||||
215 | |||||
216 | if (Craft::$app->getElements()->saveElement($asset)) |
||||
217 | { |
||||
218 | return $asset; |
||||
219 | } |
||||
220 | } |
||||
221 | catch (\Throwable $e) |
||||
222 | { |
||||
223 | Craft::error('An error occurred when saving an Asset: ' . $e->getMessage(), __METHOD__); |
||||
224 | Craft::$app->getErrorHandler()->logException($e); |
||||
225 | |||||
226 | return false; |
||||
227 | } |
||||
228 | |||||
229 | return false; |
||||
230 | } |
||||
231 | |||||
232 | /** |
||||
233 | * Saves base64-encoded file data as a local file and returns UploadedFile. |
||||
234 | * |
||||
235 | * @param string $data |
||||
236 | * |
||||
237 | * @return UploadedFile|false |
||||
238 | */ |
||||
239 | public static function base64FilePostToUploadedFile($data) |
||||
240 | { |
||||
241 | // suppress error messages assuming file objects are valid |
||||
242 | $file = @json_decode($data, false); |
||||
243 | |||||
244 | // skip files that failed to decode or don't have data |
||||
245 | if ( ! is_object($file) || empty($file->data)) |
||||
246 | { |
||||
247 | return false; |
||||
248 | } |
||||
249 | |||||
250 | $tempFilename = uniqid(pathinfo($file->name, PATHINFO_FILENAME), true) . '.' . pathinfo($file->name, PATHINFO_EXTENSION); |
||||
251 | $tempFilePath = Craft::$app->getPath()->tempPath . DIRECTORY_SEPARATOR . $tempFilename; |
||||
252 | |||||
253 | // write the decoded data |
||||
254 | file_put_contents($tempFilePath, \base64_decode($file->data)); |
||||
255 | |||||
256 | $tempFile = new UploadedFile([ |
||||
257 | 'name' => $file->name, |
||||
258 | 'type' => $file->type, |
||||
259 | 'size' => $file->size, |
||||
260 | 'tempName' => $tempFilePath, |
||||
261 | ]); |
||||
262 | |||||
263 | return $tempFile; |
||||
264 | } |
||||
265 | |||||
266 | /* |
||||
267 | function handleTransferIdsPost($ids) |
||||
268 | { |
||||
269 | foreach ($ids as $id) { |
||||
270 | |||||
271 | // create transfer wrapper around upload |
||||
272 | $transfer = FilePond\get_transfer(TRANSFER_DIR, $id); |
||||
273 | |||||
274 | // transfer not found |
||||
275 | if (!$transfer) continue; |
||||
276 | |||||
277 | // move files |
||||
278 | $files = $transfer->getFiles(defined('TRANSFER_PROCESSOR') ? TRANSFER_PROCESSOR : null); |
||||
279 | foreach($files as $file) { |
||||
280 | FilePond\move_file($file, UPLOAD_DIR); |
||||
281 | } |
||||
282 | // remove transfer directory |
||||
283 | FilePond\remove_transfer_directory(TRANSFER_DIR, $id); |
||||
284 | } |
||||
285 | } |
||||
286 | */ |
||||
287 | |||||
288 | |||||
289 | // Private Methods |
||||
290 | // ========================================================================= |
||||
291 | |||||
292 | /** |
||||
293 | * Returns a new Asset Element prepped with the provided temporary |
||||
294 | * file path, target filename, and target folder. |
||||
295 | * |
||||
296 | * @param $filepath |
||||
297 | * @param $filename |
||||
298 | * @param $folder |
||||
299 | * |
||||
300 | * @return Asset |
||||
301 | */ |
||||
302 | private function _getNewAssetFromFile($filepath, $filename, $folder): Asset |
||||
303 | { |
||||
304 | $asset = new Asset(); |
||||
305 | |||||
306 | $asset->tempFilePath = $filepath; |
||||
307 | $asset->filename = $filename; |
||||
308 | $asset->newFolderId = $folder->id; |
||||
309 | $asset->volumeId = $folder->volumeId; |
||||
310 | $asset->avoidFilenameConflicts = true; |
||||
311 | $asset->setScenario(Asset::SCENARIO_CREATE); |
||||
312 | |||||
313 | return $asset; |
||||
314 | } |
||||
315 | |||||
316 | } |
||||
317 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.