362 lines
12 KiB
PHP
362 lines
12 KiB
PHP
|
|
<?php
|
||
|
|
/**
|
||
|
|
* For the full copyright and license information, please view the
|
||
|
|
* docs/licenses/LICENSE.txt file that was distributed with this source code.
|
||
|
|
*/
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
use PrestaShop\PrestaShop\Core\File\Exception\FileUploadException;
|
||
|
|
use PrestaShop\PrestaShop\Core\File\Exception\MaximumSizeExceededException;
|
||
|
|
use PrestaShop\PrestaShop\Core\File\FileUploader;
|
||
|
|
use Symfony\Component\HttpFoundation\File\File;
|
||
|
|
use Symfony\Component\HttpFoundation\Response;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This class is responsible for managing Attachement through webservice
|
||
|
|
*/
|
||
|
|
class WebserviceSpecificManagementAttachmentsCore implements WebserviceSpecificManagementInterface
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* @var WebserviceOutputBuilder
|
||
|
|
*/
|
||
|
|
protected $objOutput;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @var WebserviceRequest
|
||
|
|
*/
|
||
|
|
protected $wsObject;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The configuration parameters of the current resource
|
||
|
|
*
|
||
|
|
* @var array
|
||
|
|
*/
|
||
|
|
public $resourceConfiguration;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @var int
|
||
|
|
*/
|
||
|
|
protected $attachmentId;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @var array|null
|
||
|
|
*/
|
||
|
|
protected $displayFile;
|
||
|
|
|
||
|
|
/*
|
||
|
|
* ------------------------------------------------
|
||
|
|
* GETTERS & SETTERS
|
||
|
|
* ------------------------------------------------
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param WebserviceOutputBuilder $obj
|
||
|
|
*
|
||
|
|
* @return WebserviceSpecificManagementInterface
|
||
|
|
*/
|
||
|
|
public function setObjectOutput(WebserviceOutputBuilder $obj)
|
||
|
|
{
|
||
|
|
$this->objOutput = $obj;
|
||
|
|
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get Object Output
|
||
|
|
*/
|
||
|
|
public function getObjectOutput()
|
||
|
|
{
|
||
|
|
return $this->objOutput;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set Webservice Object
|
||
|
|
*
|
||
|
|
* @param WebserviceRequest $obj
|
||
|
|
*/
|
||
|
|
public function setWsObject(WebserviceRequest $obj)
|
||
|
|
{
|
||
|
|
$this->wsObject = $obj;
|
||
|
|
|
||
|
|
return $this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get Webservice Object
|
||
|
|
*/
|
||
|
|
public function getWsObject()
|
||
|
|
{
|
||
|
|
return $this->wsObject;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get content
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
public function getContent()
|
||
|
|
{
|
||
|
|
if ($this->displayFile) {
|
||
|
|
// if displayFile is set, present the file (download)
|
||
|
|
$this->getObjectOutput()->setHeaderParams('Content-Type', $this->displayFile['mime']);
|
||
|
|
$this->getObjectOutput()->setHeaderParams('Content-Length', $this->displayFile['file_size']);
|
||
|
|
$this->getObjectOutput()->setHeaderParams('Content-Disposition', 'attachment; filename="' . utf8_decode($this->displayFile['file_name']) . '"');
|
||
|
|
|
||
|
|
return file_get_contents($this->displayFile['file']);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Emulate non-specific management
|
||
|
|
$this->getWsObject()->setObjectSpecificManagement(null);
|
||
|
|
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Manage attachements
|
||
|
|
*
|
||
|
|
* @return bool
|
||
|
|
*/
|
||
|
|
public function manage()
|
||
|
|
{
|
||
|
|
$this->manageAttachments();
|
||
|
|
|
||
|
|
return $this->getWsObject()->getOutputEnabled();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* That part was inherited from WebserviceSpecificManagementImages (which uses deeper api path).
|
||
|
|
* Looping for 6 segments is excessive as only the [1] and [2] indices is used for attachments.
|
||
|
|
*
|
||
|
|
* The explanation for the mapping can be seen further down "Available cases api/...".
|
||
|
|
* If urlSegment[1] is set to 'file', binary operations are done (file upload/download)
|
||
|
|
* Otherwise default webservice operations are done (read/write Model information using XML/json).
|
||
|
|
*
|
||
|
|
* Examples:
|
||
|
|
* [GET] https://domain.tld/api/attachments/ give a response in XML/json with all attachments.
|
||
|
|
* [POST] https://domain.tld/api/attachments/ only creates model information (similar to any other default api), no file information.
|
||
|
|
* [POST] https://domain.tld/api/attachments/file creates an attachment AND uploads a file for it.
|
||
|
|
*
|
||
|
|
* [PUT] https://domain.tld/api/attachments/$id_attachment here urlSegment[1] is id_attachment, updates model information only.
|
||
|
|
* [PUT] https://domain.tld/api/attachments/file/$id_attachment here urlSegment[1] is 'file' and urlSegment[2] is id_attachment, updates file (binary) only.
|
||
|
|
*
|
||
|
|
* [GET] https://domain.tld/api/attachments/$id_attachment gives a response in XML/json for the attachment model information.
|
||
|
|
* [GET] https://domain.tld/api/attachments/file/$id_attachment downloads the id_attachment file
|
||
|
|
*/
|
||
|
|
public function manageAttachments()
|
||
|
|
{
|
||
|
|
if (isset($this->getWsObject()->urlSegment)) {
|
||
|
|
for ($i = 1; $i < 6; ++$i) {
|
||
|
|
if (count($this->getWsObject()->urlSegment) == $i) {
|
||
|
|
$this->getWsObject()->urlSegment[$i] = '';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->getWsObject()->urlSegment[0] != '') {
|
||
|
|
/** @var ObjectModel */
|
||
|
|
$object = new Attachment();
|
||
|
|
$this->getWsObject()->resourceConfiguration = $object->getWebserviceParameters();
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Available cases api/...:
|
||
|
|
*
|
||
|
|
* [Utilizes default webservice handling by emulating non-specific management]
|
||
|
|
* attachments/ ("attachment_list")
|
||
|
|
* GET (xml/json) (list of attachments)
|
||
|
|
* attachments/[1,+] ("attachment_description") (N-3)
|
||
|
|
* GET (xml/json)
|
||
|
|
* PUT (xml/json) (update)
|
||
|
|
* DELETE
|
||
|
|
*
|
||
|
|
* [Specific management for file upload/download}
|
||
|
|
* attachments/file/
|
||
|
|
* POST (bin) (create new attachment)
|
||
|
|
* POST (multipart) (create new attachment)
|
||
|
|
* attachments/file/[1,+] (file management)
|
||
|
|
* GET (bin) (download file)
|
||
|
|
* PUT (bin) (upload/update file)
|
||
|
|
* PUT (multipart) (upload/update file)
|
||
|
|
* DELETE
|
||
|
|
*/
|
||
|
|
if ($this->getWsObject()->urlSegment[1] == 'file') {
|
||
|
|
// File handling (upload/download)
|
||
|
|
switch ($this->getWsObject()->method) {
|
||
|
|
case 'GET':
|
||
|
|
case 'HEAD':
|
||
|
|
$this->displayFile = $this->executeFileGetAndHead();
|
||
|
|
break;
|
||
|
|
case 'POST':
|
||
|
|
case 'PUT':
|
||
|
|
case 'PATCH':
|
||
|
|
$this->executeFileAddAndEdit();
|
||
|
|
|
||
|
|
// Emulate get/head to return output
|
||
|
|
$this->getWsObject()->method = 'GET';
|
||
|
|
$this->getWsObject()->urlSegment[1] = $this->attachmentId;
|
||
|
|
$this->getWsObject()->urlSegment[2] = '';
|
||
|
|
$this->getWsObject()->executeEntityGetAndHead();
|
||
|
|
break;
|
||
|
|
case 'DELETE':
|
||
|
|
$attachment = new Attachment((int) $this->getWsObject()->urlSegment[2]);
|
||
|
|
$attachment->delete();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// Default handling via WebserviceRequest
|
||
|
|
switch ($this->getWsObject()->method) {
|
||
|
|
case 'GET':
|
||
|
|
case 'HEAD':
|
||
|
|
$this->getWsObject()->executeEntityGetAndHead();
|
||
|
|
break;
|
||
|
|
case 'PUT':
|
||
|
|
$this->getWsObject()->executeEntityPut();
|
||
|
|
break;
|
||
|
|
case 'PATCH':
|
||
|
|
$this->getWsObject()->executeEntityPatch();
|
||
|
|
break;
|
||
|
|
case 'DELETE':
|
||
|
|
$this->getWsObject()->executeEntityDelete();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Need to set an object for the WebserviceOutputBuilder object in any case
|
||
|
|
// because schema need to get webserviceParameters of this object
|
||
|
|
if (isset($object)) {
|
||
|
|
$this->getWsObject()->objects['empty'] = $object;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handles attachment file download
|
||
|
|
*
|
||
|
|
* @return array<string, string> File details
|
||
|
|
*
|
||
|
|
* @throws WebserviceException if attachment is not existing or file not available
|
||
|
|
*/
|
||
|
|
public function executeFileGetAndHead(): array
|
||
|
|
{
|
||
|
|
$attachmentId = (int) $this->getWsObject()->urlSegment[2];
|
||
|
|
$attachment = new Attachment($attachmentId);
|
||
|
|
if (empty($attachment->id)) {
|
||
|
|
throw new WebserviceException(
|
||
|
|
sprintf(
|
||
|
|
'Attachment %d not found',
|
||
|
|
$attachmentId
|
||
|
|
),
|
||
|
|
[
|
||
|
|
1,
|
||
|
|
Response::HTTP_INTERNAL_SERVER_ERROR,
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Physical file location
|
||
|
|
$file = _PS_DOWNLOAD_DIR_ . $attachment->file;
|
||
|
|
// Check if file exists
|
||
|
|
if (!file_exists($file)) {
|
||
|
|
throw new WebserviceException(
|
||
|
|
sprintf(
|
||
|
|
'Unable to load the attachment file for attachment %d',
|
||
|
|
$attachmentId
|
||
|
|
),
|
||
|
|
[
|
||
|
|
1,
|
||
|
|
Response::HTTP_INTERNAL_SERVER_ERROR,
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Return file details
|
||
|
|
return [
|
||
|
|
'file' => $file,
|
||
|
|
'mime' => $attachment->mime,
|
||
|
|
'file_name' => $attachment->file_name,
|
||
|
|
'file_size' => $attachment->file_size,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handles file upload
|
||
|
|
*
|
||
|
|
* Creates new attachment or replaces existing with a new file.
|
||
|
|
* [PUT] and [PATCH] update existing attachment file
|
||
|
|
* [POST] create new attachment
|
||
|
|
*/
|
||
|
|
public function executeFileAddAndEdit(): void
|
||
|
|
{
|
||
|
|
// Load attachment without checking the method, because of
|
||
|
|
// PUT which is cleared the $_FILES var in multipart/form context
|
||
|
|
$attachmentId = null;
|
||
|
|
if (isset($this->getWsObject()->urlSegment[2])) {
|
||
|
|
$attachmentId = (int) $this->getWsObject()->urlSegment[2];
|
||
|
|
}
|
||
|
|
|
||
|
|
$attachment = new Attachment($attachmentId);
|
||
|
|
|
||
|
|
$maximumSize = ((int) Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE')) * 1024 * 1024;
|
||
|
|
$uploader = new FileUploader(
|
||
|
|
_PS_DOWNLOAD_DIR_,
|
||
|
|
$maximumSize
|
||
|
|
);
|
||
|
|
|
||
|
|
if (isset($_FILES['file'])) {
|
||
|
|
// Standard HTTP upload
|
||
|
|
$fileToUpload = $_FILES['file'];
|
||
|
|
} else {
|
||
|
|
// Get data from binary
|
||
|
|
$fileToUpload = file_get_contents('php://input');
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
$file = $uploader->upload($fileToUpload);
|
||
|
|
if (!empty($attachment->id)) {
|
||
|
|
unlink(_PS_DOWNLOAD_DIR_ . $attachment->file);
|
||
|
|
}
|
||
|
|
|
||
|
|
$defaultLanguage = Configuration::get('PS_LANG_DEFAULT');
|
||
|
|
|
||
|
|
$attachment->file = $file['id'];
|
||
|
|
$attachment->file_name = $file['file_name'];
|
||
|
|
$attachment->mime = $file['mime_type'];
|
||
|
|
if ($attachment->name[$defaultLanguage] === null) {
|
||
|
|
$attachment->name[$defaultLanguage] = $_POST['name'] ?? $file['file_name'];
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!empty($attachment->id)) {
|
||
|
|
$attachment->update();
|
||
|
|
} else {
|
||
|
|
$attachment->add();
|
||
|
|
}
|
||
|
|
// Remember affected entity
|
||
|
|
$this->attachmentId = $attachment->id;
|
||
|
|
} catch (MaximumSizeExceededException $e) {
|
||
|
|
$this->getWsObject()->errors[] = $this->trans(
|
||
|
|
'The file you are trying to upload is %2$d KB, which is larger than the maximum size allowed of %1$d KB.',
|
||
|
|
[$maximumSize, $e->getMessage()],
|
||
|
|
'Admin.Notifications.Error'
|
||
|
|
);
|
||
|
|
} catch (FileUploadException $e) {
|
||
|
|
$this->getWsObject()->errors[] = $this->trans(
|
||
|
|
'Failed to copy the file.',
|
||
|
|
[],
|
||
|
|
'Admin.Catalog.Notification'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param string $message
|
||
|
|
* @param array $params
|
||
|
|
* @param string $domain
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
protected function trans(string $message, array $params, string $domain): string
|
||
|
|
{
|
||
|
|
return Context::getContext()->getTranslator()->trans($message, $params, $domain);
|
||
|
|
}
|
||
|
|
}
|