Issues (15)

src/services/Server.php (6 issues)

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
The parameter $meta is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

42
    public function handleFileTransfer($files, /** @scrutinizer ignore-unused */ $meta, $volumeId = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
The assignment to $errors is dead and can be removed.
Loading history...
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
$asset is of type craft\elements\Asset, thus it always evaluated to true.
Loading history...
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
$asset is of type craft\elements\Asset, thus it always evaluated to true.
Loading history...
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
$asset is of type craft\elements\Asset, thus it always evaluated to true.
Loading history...
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 ignore-type  annotation

201
        if ( ! $tempFile = self::base64FilePostToUploadedFile(/** @scrutinizer ignore-type */ $file))
Loading history...
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