diff --git a/.php_cs.cache b/.php_cs.cache new file mode 100644 index 00000000..2eece6cf --- /dev/null +++ b/.php_cs.cache @@ -0,0 +1 @@ +{"php":"7.2.20","version":"2.15.0","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":true,"class_definition":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_constants":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"encoding":true,"full_opening_tag":true},"hashes":{"vendor\/flextype-components\/arr\/Arr.php":3460238454}} \ No newline at end of file diff --git a/api.md b/api.md new file mode 100644 index 00000000..80f4e3ac --- /dev/null +++ b/api.md @@ -0,0 +1,31 @@ + +Warning: PHP Startup: Unable to load dynamic library 'curl' (tried: /usr/local/Cellar/php@7.2/7.2.20/lib/php/20170718/curl (dlopen(/usr/local/Cellar/php@7.2/7.2.20/lib/php/20170718/curl, 9): image not found), /usr/local/Cellar/php@7.2/7.2.20/lib/php/20170718/curl.so (dlopen(/usr/local/Cellar/php@7.2/7.2.20/lib/php/20170718/curl.so, 9): image not found)) in Unknown on line 0 +## Table of contents + +- [\Flextype\Component\Arr\Arr](#class-flextypecomponentarrarr) + +
+ +### Class: \Flextype\Component\Arr\Arr + +| Visibility | Function | +|:-----------|:---------| +| public static | average(array $array, int/\integer $decimals) : int/\Flextype\Component\Arr\double
Returns the average value of the current array. | +| public static | createFromJson(\string $json, \boolean $assoc=true, \integer $depth=512, int/\integer $options) : array
Create an new Array from JSON string. | +| public static | createFromString(\string $str, \string $delimiter=null, \string $regEx=null) : array
Create an new Array object via string. | +| public static | delete(array $array, \string $path) : mixed
Deletes an array value using "dot notation". | +| public static | dot(array $array, \string $prepend=`''`) : array
Flatten a multi-dimensional associative array with dots. | +| public static | first(array $array) : mixed The first element
Returns the first element of an array | +| public static | get(array $array, \string $path, mixed $default=null) : mixed
Returns value from array using "dot notation". If the key does not exist in the array, the default value will be returned instead. | +| public static | isAssoc(array $array) : bool
Returns TRUE if the array is associative and FALSE if not. | +| public static | keyExists(array $array, mixed $path) : bool
Checks if the given dot-notated key exists in the array. | +| public static | last(array $array) : mixed The last element
Returns the last element of an array | +| public static | overwrite(array $array1, array $array2) : array
Overwrites an array with values from input arrays. Keys that do not exist in the first array will not be added! | +| public static | random(array $array) : mixed
Returns a random value from an array. | +| public | reverse(array $array, \boolean $preserve_keys=false) : array
Return an array with elements in reverse order. Non-numeric keys are not affected by this setting and will always be preserved. | +| public static | set(array $array, \string $path, mixed $value) : void
Sets an array value using "dot notation". | +| public static | size(array $array, int/\integer $mode) : void
Counts all elements in an array. COUNT_RECURSIVE (or 1), count will recursively count the array. This is particularly useful for counting all the elements of a multidimensional array. count does not detect infinite recursion. | +| public static | sort(array $array, \string $field, \string $direction=`'ASC'`, \Flextype\Component\Arr\const $method) : array
Sorts a multi-dimensional array by a certain field path | +| public static | toJson(array $array, int/\integer $options, \integer $depth=512) : string The JSON string
Converts an array to a JSON string | +| public static | undot(array $array) : array
Expands a dot notation array into a full multi-dimensional array. | + diff --git a/composer.json b/composer.json index ccadc2dc..94e11c0e 100755 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "doctrine/cache": "~1.10.0", "doctrine/collections": "~1.6.4", - "flextype-components/arr" : "1.2.5", + "flextype-components/arr" : "1.2.8", "flextype-components/cookie" : "1.2.0", "flextype-components/date" : "1.1.0", "flextype-components/filesystem" : "2.0.6", @@ -77,8 +77,8 @@ ] }, "require-dev": { - "doctrine/coding-standard": "~6.0.0", - "victorjonsson/markdowndocs": "dev-master", + "doctrine/coding-standard": "7.0.2", + "victorjonsson/markdowndocs": "^1.3", "phpstan/phpstan": "^0.11.19", "symfony/var-dumper": "^4.4" } diff --git a/src/flextype/bootstrap.php b/src/flextype/bootstrap.php index 3454c915..65fb57c1 100755 --- a/src/flextype/bootstrap.php +++ b/src/flextype/bootstrap.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace Flextype; +use Flextype\Component\Arr\Arr; use Flextype\Component\Filesystem\Filesystem; use Flextype\Component\Registry\Registry; use Flextype\Component\Session\Session; @@ -131,7 +132,12 @@ include_once 'dependencies.php'; include_once 'endpoints/access/access.php'; include_once 'endpoints/delivery/entries.php'; include_once 'endpoints/delivery/registry.php'; +<<<<<<< HEAD include_once 'endpoints/delivery/config.php'; +======= +include_once 'endpoints/delivery/media/files.php'; +include_once 'endpoints/delivery/media/folders.php'; +>>>>>>> 428-files-api include_once 'endpoints/management/entries.php'; include_once 'endpoints/management/config.php'; include_once 'endpoints/images/images.php'; diff --git a/src/flextype/config/settings.yaml b/src/flextype/config/settings.yaml index 600d0258..0c2204a0 100644 --- a/src/flextype/config/settings.yaml +++ b/src/flextype/config/settings.yaml @@ -236,6 +236,17 @@ cors: expose: [] credentials: false +# Media +media: + accept_file_types: 'gif, jpg, jpeg, png, ico, zip, tgz, txt, md, doc, docx, pdf, epub, xls, xlsx, ppt, pptx, mp3, ogg, wav, m4a, mp4, m4v, ogv, wmv, avi, webm, svg' + max_file_size: 5000000 + safe_names: true + image_width: 1600 + image_height: 0 + image_quality: 70 + max_image_width: null + max_image_height: null + # Content APIs api: delivery: @@ -245,7 +256,11 @@ api: registry: enabled: true default_token: +<<<<<<< HEAD config: +======= + files: +>>>>>>> 428-files-api enabled: true default_token: management: diff --git a/src/flextype/core/Entries/Entries.php b/src/flextype/core/Entries/Entries.php index a208cca9..25c47959 100755 --- a/src/flextype/core/Entries/Entries.php +++ b/src/flextype/core/Entries/Entries.php @@ -14,6 +14,7 @@ use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Expr\Comparison; use Flextype\Component\Filesystem\Filesystem; use Flextype\Component\Session\Session; +use Flextype\Component\Arr\Arr; use Ramsey\Uuid\Uuid; use function array_merge; use function count; @@ -391,6 +392,9 @@ class Entries // For each founded entry we should create $entries array. $entry = $this->fetch($uid); + // Flatten a multi-dimensional entries array with dots. + $entry = Arr::dot($entry); + // Add entry into the entries $entries[$uid] = $entry; @@ -483,6 +487,11 @@ class Entries // Gets a native PHP array representation of the collection. $entries = $entries->toArray(); + // Magic is here... dot and undot for entries array + // 1. Flatten a multi-dimensional entries array with dots. + // 2. Restore entries array with dots into correct multi-dimensional entries array + $entries = Arr::undot(Arr::dot($entries)); + // Restore error_reporting error_reporting($oldErrorReporting); diff --git a/src/flextype/core/Media/MediaFiles.php b/src/flextype/core/Media/MediaFiles.php new file mode 100644 index 00000000..46a23409 --- /dev/null +++ b/src/flextype/core/Media/MediaFiles.php @@ -0,0 +1,328 @@ +flextype = $flextype; + } + + /** + * Create a media file + * + * @param array $file Raw file data (multipart/form-data). + * @param string $folder The folder you're targetting. + * + * @access public + */ + public function create(array $file, string $folder) + { + $upload_folder = PATH['project'] . '/uploads/' . $folder . '/'; + $upload_metadata_folder = PATH['project'] . '/uploads/.meta/' . $folder . '/'; + + if (! Filesystem::has($upload_folder)) { + Filesystem::createDir($upload_folder); + } + + if (! Filesystem::has($upload_metadata_folder)) { + Filesystem::createDir($upload_metadata_folder); + } + + $accept_file_types = $this->flextype['registry']->get('flextype.settings.media.accept_file_types'); + $max_file_size = $this->flextype['registry']->get('flextype.settings.media.max_file_size'); + $safe_names = $this->flextype['registry']->get('flextype.settings.media.safe_names'); + $max_image_width = $this->flextype['registry']->get('flextype.settings.media.max_image_width'); + $max_image_height = $this->flextype['registry']->get('flextype.settings.media.max_image_height'); + + $exact = false; + $chmod = 0644; + $filename = null; + $exif_data = []; + + // + // Tests if a successful upload has been made. + // + if (isset($file['error']) + and isset($file['tmp_name']) + and $file['error'] === UPLOAD_ERR_OK + and is_uploaded_file($file['tmp_name'])) { + // + // Tests if upload data is valid, even if no file was uploaded. + // + if (isset($file['error']) + and isset($file['name']) + and isset($file['type']) + and isset($file['tmp_name']) + and isset($file['size'])) { + // + // Test if an uploaded file is an allowed file type, by extension. + // + if (strpos($accept_file_types, strtolower(pathinfo($file['name'], PATHINFO_EXTENSION))) !== false) { + // + // Validation rule to test if an uploaded file is allowed by file size. + // + if (($file['error'] != UPLOAD_ERR_INI_SIZE) + and ($file['error'] == UPLOAD_ERR_OK) + and ($file['size'] <= $max_file_size)) { + // + // Validation rule to test if an upload is an image and, optionally, is the correct size. + // + if (in_array(mime_content_type($file['tmp_name']), ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) { + function validateImage($file, $max_image_width, $max_image_height, $exact) + { + try { + // Get the width and height from the uploaded image + list($width, $height) = getimagesize($file['tmp_name']); + } catch (ErrorException $e) { + // Ignore read errors + } + if (empty($width) or empty($height)) { + // Cannot get image size, cannot validate + return false; + } + if (!$max_image_width) { + // No limit, use the image width + $max_image_width = $width; + } + if (!$max_image_height) { + // No limit, use the image height + $max_image_height = $height; + } + if ($exact) { + // Check if dimensions match exactly + return ($width === $max_image_width and $height === $max_image_height); + } else { + // Check if size is within maximum dimensions + return ($width <= $max_image_width and $height <= $max_image_height); + } + return false; + } + if (validateImage($file, $max_image_width, $max_image_height, $exact) === false) { + return false; + } + } + if (!isset($file['tmp_name']) or !is_uploaded_file($file['tmp_name'])) { + // Ignore corrupted uploads + return false; + } + if ($filename === null) { + // Use the default filename + $filename = $file['name']; + } + if ($safe_names === true) { + // Remove spaces from the filename + $filename = $this->flextype['slugify']->slugify(pathinfo($filename)['filename']) . '.' . pathinfo($filename)['extension']; + } + if (!is_dir($upload_folder) or !is_writable(realpath($upload_folder))) { + throw new \RuntimeException("Directory {$upload_folder} must be writable"); + } + // Make the filename into a complete path + $filename = realpath($upload_folder) . DIRECTORY_SEPARATOR . $filename; + if (move_uploaded_file($file['tmp_name'], $filename)) { + // Set permissions on filename + chmod($filename, $chmod); + + if (in_array(mime_content_type($filename), ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) { + // open an image file + $img = Image::make($filename); + + // now you are able to resize the instance + if ($this->flextype['registry']->get('plugins.admin.settings.entries.media.upload_images_width') > 0 && $this->flextype['registry']->get('plugins.admin.settings.entries.media.upload_images_height') > 0) { + $img->resize($this->flextype['registry']->get('plugins.admin.settings.entries.media.upload_images_width'), $this->flextype['registry']->get('plugins.admin.settings.entries.media.upload_images_height'), function($constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + }); + } elseif ($this->flextype['registry']->get('plugins.admin.settings.entries.media.upload_images_width') > 0) { + $img->resize($this->flextype['registry']->get('plugins.admin.settings.entries.media.upload_images_width'), null, function($constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + }); + } elseif ($this->flextype['registry']->get('plugins.admin.settings.entries.media.upload_images_height') > 0) { + $img->resize(null, $this->flextype['registry']->get('plugins.admin.settings.entries.media.upload_images_height'), function($constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + }); + } + + // finally we save the image as a new file + $img->save($filename, $this->flextype['registry']->get('plugins.admin.settings.entries.media.upload_images_quality')); + + // destroy + $img->destroy(); + + $headers = exif_read_data($filename); + + $exif_data = []; + + foreach ($headers['COMPUTED'] as $header => $value) { + $exif_data[$header] = $value; + } + } + + $metadata = ['title' => substr(basename($filename), 0, strrpos(basename($filename), '.')), + 'description' => '', + 'type' => mime_content_type($filename), + 'filesize' => Filesystem::getSize($filename), + 'uploaded_on' => time(), + 'exif' => $exif_data]; + + Filesystem::write($upload_metadata_folder . basename($filename) . '.yaml', + $this->flextype['serializer']->encode($metadata, 'yaml')); + + + // Return new file path + return $filename; + } + } + } + } + } + + return false; + } + + /** + * Fetch single file + * + * @param string $directory The directory to list. + * + * @return array A list of file metadata. + */ + public function fetchsingle(string $id) : array + { + $result = []; + + if (Filesystem::has($this->flextype['media_files_meta']->getFileMetaLocation($id))) { + $result = $this->flextype['serializer']->decode(Filesystem::read($this->flextype['media_files_meta']->getFileMetaLocation($id)), 'yaml'); + + $result['filename'] = pathinfo(str_replace("/.meta", "", $this->flextype['media_files_meta']->getFileMetaLocation($id)))['filename']; + $result['basename'] = explode(".", basename($this->flextype['media_files_meta']->getFileMetaLocation($id)))[0]; + $result['extension'] = ltrim(strstr($id, '.'), '.'); + $result['dirname'] = pathinfo(str_replace("/.meta", "", $this->flextype['media_files_meta']->getFileMetaLocation($id)))['dirname']; + + $result['url'] = 'project/uploads/' . $id; + + if ($this->flextype['registry']->has('flextype.settings.url') && $this->flextype['registry']->get('flextype.settings.url') != '') { + $full_url = $this->flextype['registry']->get('flextype.settings.url'); + } else { + $full_url = Uri::createFromEnvironment(new Environment($_SERVER))->getBaseUrl(); + } + + $result['full_url'] = $full_url . '/project/uploads/' . $id; + } + + return $result; + } + + /** + * Fetch files collection + * + * @param string $folder The directory to list. + * + * @return array A list of file metadata. + */ + public function fetchCollection(string $folder) : array + { + $result = []; + + foreach (Filesystem::listContents($this->flextype['media_folders_meta']->getDirMetaLocation($folder)) as $file) { + $result[$file['basename']] = $this->flextype['serializer']->decode(Filesystem::read($file['path']), 'yaml'); + + $result[$file['basename']]['filename'] = pathinfo(str_replace("/.meta", "", $this->flextype['media_files_meta']->getFileMetaLocation($file['basename'])))['filename']; + $result[$file['basename']]['basename'] = explode(".", basename($this->flextype['media_files_meta']->getFileMetaLocation($file['basename'])))[0]; + $result[$file['basename']]['extension'] = ltrim(strstr($file['basename'], '.'), '.'); + $result[$file['basename']]['dirname'] = pathinfo(str_replace("/.meta", "", $file['path']))['dirname']; + + $result[$file['basename']]['url'] = 'project/uploads/' . $folder . '/' . $file['basename']; + + if ($this->flextype['registry']->has('flextype.settings.url') && $this->flextype['registry']->get('flextype.settings.url') != '') { + $full_url = $this->flextype['registry']->get('flextype.settings.url'); + } else { + $full_url = Uri::createFromEnvironment(new Environment($_SERVER))->getBaseUrl(); + } + + $result[$file['basename']]['full_url'] = $full_url . '/project/uploads/' . $folder . '/' . $file['basename']; + } + + return $result; + } + + /** + * Rename file + * + * @param string $id Unique identifier of the file. + * @param string $new_id New Unique identifier of the file. + * + * @return bool True on success, false on failure. + * + * @access public + */ + public function rename(string $id, string $new_id) : bool + { + if (!Filesystem::has($this->getFileLocation($new_id)) && !Filesystem::has($this->flextype['media_files_meta']->getFileMetaLocation($new_id))) { + if (rename($this->getFileLocation($id), $this->getFileLocation($new_id)) && rename($this->flextype['media_files_meta']->getFileMetaLocation($id), $this->flextype['media_files_meta']->getFileMetaLocation($new_id))) { + return true; + } else { + return false; + } + } else { + return false; + } + } + + /** + * Delete file + * + * @param string $id Unique identifier of the file. + * + * @return bool True on success, false on failure. + * + * @access public + */ + public function delete(string $id) + { + Filesystem::delete($this->getFileLocation($id)); + Filesystem::delete($this->flextype['media_files_meta']->getFileMetaLocation($id)); + } + + /** + * Get file location + * + * @param string $id Unique identifier of the file. + * + * @return string entry file location + * + * @access public + */ + public function getFileLocation(string $id) : string + { + return PATH['project'] . '/uploads/' . $id; + } +} diff --git a/src/flextype/core/Media/MediaFilesMeta.php b/src/flextype/core/Media/MediaFilesMeta.php new file mode 100644 index 00000000..69180ec8 --- /dev/null +++ b/src/flextype/core/Media/MediaFilesMeta.php @@ -0,0 +1,101 @@ +flextype['serializer']->decode(Filesystem::read($this->getFileMetaLocation($id)), 'yaml'); + + if (Arr::keyExists($file_data, $field)) { + Arr::set($file_data, $field, $value); + return Filesystem::write($this->getFileMetaLocation($id), $this->flextype['serializer']->encode($file_data, 'yaml')); + } + + return false; + } + + /** + * Add file meta information + * + * @param string $id Unique identifier of the file. + * @param string $field Field name + * @param string $value Field value + * + * @return bool True on success, false on failure. + * + * @access public + */ + public function addMeta(string $id, string $field, string $value) : bool + { + $file_data = $this->flextype['serializer']->decode(Filesystem::read($this->getFileMetaLocation($id)), 'yaml'); + + if (!Arr::keyExists($file_data, $field)) { + Arr::set($file_data, $field, $value); + return Filesystem::write($this->getFileMetaLocation($id), $this->flextype['serializer']->encode($file_data, 'yaml')); + } + + return false; + } + + /** + * Delete file meta information + * + * @param string $id Unique identifier of the file. + * @param string $field Field name + * + * @return bool True on success, false on failure. + * + * @access public + */ + public function deleteMeta(string $id, string $field) : bool + { + $file_data = $this->flextype['serializer']->decode(Filesystem::read($this->getFileMetaLocation($id)), 'yaml'); + + if (Arr::keyExists($file_data, $field)) { + Arr::delete($file_data, $field); + return Filesystem::write($this->getFileMetaLocation($id), $this->flextype['serializer']->encode($file_data, 'yaml')); + } + + return false; + } + + /** + * Get file meta location + * + * @param string $id Unique identifier of the file. + * + * @return string entry file location + * + * @access public + */ + public function getFileMetaLocation(string $id) : string + { + return PATH['project'] . '/uploads/.meta/' . $id . '.yaml'; + } +} diff --git a/src/flextype/core/Media/MediaFolders.php b/src/flextype/core/Media/MediaFolders.php new file mode 100644 index 00000000..05f396d5 --- /dev/null +++ b/src/flextype/core/Media/MediaFolders.php @@ -0,0 +1,110 @@ +flextype = $flextype; + } + + /** + * Create folder + * + * @param string $id Unique identifier of the folder. + * + * @return bool True on success, false on failure. + * + * @access public + */ + public function create(string $id) : bool + { + if (!Filesystem::has($this->getDirLocation($id)) && !Filesystem::has($this->flextype['media_folders_meta']->getDirMetaLocation($id))) { + if (Filesystem::createDir($this->getDirLocation($id)) && Filesystem::createDir($this->flextype['media_folders_meta']->getDirMetaLocation($id))) { + return true; + } else { + return false; + } + } else { + return false; + } + } + + /** + * Rename folder + * + * @param string $id Unique identifier of the folder. + * @param string $new_id New Unique identifier of the folder. + * + * @return bool True on success, false on failure. + * + * @access public + */ + public function rename(string $id, string $new_id) : bool + { + if (!Filesystem::has($this->getDirLocation($new_id)) && !Filesystem::has($this->flextype['media_folders_meta']->getDirMetaLocation($new_id))) { + if (rename($this->getDirLocation($id), $this->getDirLocation($new_id)) && rename($this->flextype['media_folders_meta']->getDirMetaLocation($id), $this->flextype['media_folders_meta']->getDirMetaLocation($new_id))) { + return true; + } else { + return false; + } + } else { + return false; + } + } + + /** + * Delete dir + * + * @param string $id Unique identifier of the file. + * + * @return bool True on success, false on failure. + * + * @access public + */ + public function delete(string $id) + { + Filesystem::deleteDir($this->getDirLocation($id)); + Filesystem::deleteDir($this->flextype['media_folders_meta']->getDirMetaLocation($id)); + } + + /** + * Get files directory location + * + * @param string $id Unique identifier of the folder. + * + * @return string entry directory location + * + * @access public + */ + public function getDirLocation(string $id) : string + { + return PATH['project'] . '/uploads/' . $id; + } +} diff --git a/src/flextype/core/Media/MediaFoldersMeta.php b/src/flextype/core/Media/MediaFoldersMeta.php new file mode 100644 index 00000000..fa8ea7fe --- /dev/null +++ b/src/flextype/core/Media/MediaFoldersMeta.php @@ -0,0 +1,27 @@ + ['type' => 'Error', 'id' => 'AccessTokenInvalid'], 'message' => 'The access token you sent could not be found or is invalid.']; +$api_sys_messages['NotFound'] = ['sys' => ['type' => 'Error', 'id' => 'NotFound'], 'message' => 'The resource could not be found.']; + +/** + * Validate delivery media files token + */ +function validate_delivery_media_files_token($token) : bool +{ + return Filesystem::has(PATH['project'] . '/tokens/delivery/media/files/' . $token . '/token.yaml'); +} + +/** + * Fetch media file(s) + * + * endpoint: GET /api/delivery/media/files + * + * Query: + * path - [REQUIRED] - Unique identifier of the file path. + * token - [REQUIRED] - Valid Content Delivery API token for Entries. + * + * Returns: + * An array of entry item objects. + */ +$app->get('/api/delivery/media/files', function (Request $request, Response $response) use ($flextype, $api_sys_messages) { + + // Get Query Params + $query = $request->getQueryParams(); + + // Set variables + $path = $query['path']; + $token = $query['token']; + + if ($flextype['registry']->get('flextype.settings.api.delivery.media.files.enabled')) { + + // Validate delivery token + if (validate_delivery_media_files_token($token)) { + $delivery_files_token_file_path = PATH['project'] . '/tokens/delivery/media/files/' . $token. '/token.yaml'; + + // Set delivery token file + if ($delivery_files_token_file_data = $flextype['serializer']->decode(Filesystem::read($delivery_files_token_file_path), 'yaml')) { + if ($delivery_files_token_file_data['state'] === 'disabled' || + ($delivery_files_token_file_data['limit_calls'] !== 0 && $delivery_files_token_file_data['calls'] >= $delivery_files_token_file_data['limit_calls'])) { + return $response->withJson($api_sys_messages['AccessTokenInvalid'], 401); + } + + // Create files array + $files = []; + + // Get list if file or files for specific folder + if (is_dir($path)) { + $files = $flextype['media_files']->fetchCollection($path); + } else { + $files = $flextype['media_files']->fetchSingle($path); + } + + // Write response data + $response_data['data'] = $files; + + // Set response code + $response_code = count($response_data['data']) > 0 ? 200 : 404; + + // Update calls counter + Filesystem::write($delivery_files_token_file_path, $flextype['serializer']->encode(array_replace_recursive($delivery_files_token_file_data, ['calls' => $delivery_files_token_file_data['calls'] + 1]), 'yaml')); + + if ($response_code == 404) { + + // Return response + return $response + ->withJson($api_sys_messages['NotFound'], $response_code); + } + + // Return response + return $response + ->withJson($response_data, $response_code); + } + + return $response + ->withJson($api_sys_messages['AccessTokenInvalid'], 401); + } + + return $response + ->withJson($api_sys_messages['AccessTokenInvalid'], 401); + } + + return $response + ->withJson($api_sys_messages['AccessTokenInvalid'], 401); +}); diff --git a/src/flextype/endpoints/management/media/files.php b/src/flextype/endpoints/management/media/files.php new file mode 100644 index 00000000..d2dfb32a --- /dev/null +++ b/src/flextype/endpoints/management/media/files.php @@ -0,0 +1,106 @@ + ['type' => 'Error', 'id' => 'AccessTokenInvalid'], 'message' => 'The access token you sent could not be found or is invalid.']; +$api_sys_messages['NotFound'] = ['sys' => ['type' => 'Error', 'id' => 'NotFound'], 'message' => 'The resource could not be found.']; + +/** + * Validate delivery media files token + */ +function validate_delivery_media_files_token($token) : bool +{ + return Filesystem::has(PATH['project'] . '/tokens/delivery/media/files/' . $token . '/token.yaml'); +} + +/** + * Fetch media file(s) + * + * endpoint: GET /api/delivery/media/files + * + * Query: + * path - [REQUIRED] - Unique identifier of the file path. + * token - [REQUIRED] - Valid Content Delivery API token for Entries. + * + * Returns: + * An array of entry item objects. + */ +$app->get('/api/delivery/media/files', function (Request $request, Response $response) use ($flextype, $api_sys_messages) { + + // Get Query Params + $query = $request->getQueryParams(); + + // Set variables + $path = $query['path']; + $token = $query['token']; + + if ($flextype['registry']->get('flextype.settings.api.delivery.media.files.enabled')) { + + // Validate delivery token + if (validate_delivery_media_files_token($token)) { + $delivery_files_token_file_path = PATH['project'] . '/tokens/delivery/media/files/' . $token. '/token.yaml'; + + // Set delivery token file + if ($delivery_files_token_file_data = $flextype['serializer']->decode(Filesystem::read($delivery_files_token_file_path), 'yaml')) { + if ($delivery_files_token_file_data['state'] === 'disabled' || + ($delivery_files_token_file_data['limit_calls'] !== 0 && $delivery_files_token_file_data['calls'] >= $delivery_files_token_file_data['limit_calls'])) { + return $response->withJson($api_sys_messages['AccessTokenInvalid'], 401); + } + + // Create files array + $files = []; + + // Get list if file or files for specific folder + if (is_dir($path)) { + $files = $flextype['media_files']->fetchCollection($path); + } else { + $files = $flextype['media_files']->fetchSingle($path); + } + + // Write response data + $response_data['data'] = $files; + + // Set response code + $response_code = count($response_data['data']) > 0 ? 200 : 404; + + // Update calls counter + Filesystem::write($delivery_files_token_file_path, $flextype['serializer']->encode(array_replace_recursive($delivery_files_token_file_data, ['calls' => $delivery_files_token_file_data['calls'] + 1]), 'yaml')); + + if ($response_code == 404) { + + // Return response + return $response + ->withJson($api_sys_messages['NotFound'], $response_code); + } + + // Return response + return $response + ->withJson($response_data, $response_code); + } + + return $response + ->withJson($api_sys_messages['AccessTokenInvalid'], 401); + } + + return $response + ->withJson($api_sys_messages['AccessTokenInvalid'], 401); + } + + return $response + ->withJson($api_sys_messages['AccessTokenInvalid'], 401); +}); diff --git a/src/flextype/endpoints/management/media/folders.php b/src/flextype/endpoints/management/media/folders.php new file mode 100644 index 00000000..e69de29b