diff --git a/www/.env.sample b/www/.env.sample index 265065ce..f9775ac5 100644 --- a/www/.env.sample +++ b/www/.env.sample @@ -14,3 +14,9 @@ METRIKA_HAWK_TOKEN = '' # Ads tokens for carbon CARBON_SERVE= CARBON_PLACEMENT= + +# AWS Keys +AWS_ACCESS_KEY_ID = '' +AWS_SECRET_ACCESS_KEY = '' +AWS_BUCKET = '' +AWS_REGION = 'us-east-1' diff --git a/www/application/classes/Controller/Base/Ajax.php b/www/application/classes/Controller/Base/Ajax.php index a499d24b..710a3dd5 100644 --- a/www/application/classes/Controller/Base/Ajax.php +++ b/www/application/classes/Controller/Base/Ajax.php @@ -2,11 +2,9 @@ class Controller_Base_Ajax extends Controller_Base_preDispatch { - - /** - * Constants means action we did with transfered file - */ + * Constants means action we did with transfered file + */ const TRANSPORT_ACTION_PROFILE_PHOTO = 1; const TRANSPORT_ACTION_ARTICLE_COVER = 2; @@ -15,6 +13,8 @@ class Controller_Base_Ajax extends Controller_Base_preDispatch */ public $UPLOAD_MAX_SIZE = '30M'; + private $mediaDir = 'upload/users'; + public function before() { @@ -47,8 +47,8 @@ public function action_file_uploader() /** Target id */ $id = (int)Arr::get($_POST, 'id', 0); - /** Uploaded files */ - $files = Arr::get($_FILES, 'files'); + /** Uploaded file */ + $file = Arr::get($_FILES, 'files'); /** Array will be passed to JS transport module */ $response = array('success' => 0); @@ -56,7 +56,7 @@ public function action_file_uploader() /** * Check for correct parametres */ - $dataValidationError = $this->getTransportValidationError($action, $id, $files); + $dataValidationError = $this->getTransportValidationError($action, $id, $file); if ($dataValidationError) { $response['error_description'] = $dataValidationError; @@ -64,19 +64,18 @@ public function action_file_uploader() switch ($action) { case self::TRANSPORT_ACTION_PROFILE_PHOTO: - - $file = $this->methods->saveImage($files, 'upload/users/'); - $filename = Arr::get($file, 'name'); + + $filename = Model_Methods::saveMedia($file, $this->mediaDir); if ($filename) { /** Update user */ - $this->user->photo = '/upload/users/b_' . $filename; + $this->user->photo = $filename; $this->user->update(); /** Return success information. @uses client-side callback.saveProfilePhoto handler */ $response['success'] = 1; - $response['callback'] = 'codex.profile.uploadPhotoSuccess("/upload/users/b_'.$filename.'")'; + $response['callback'] = 'codex.profile.uploadPhotoSuccess("'.$this->user->photo.'")'; } else { $response['error_description'] = 'File wasn\'t saved'; } diff --git a/www/application/classes/Controller/Transport.php b/www/application/classes/Controller/Transport.php index 0d9964f8..2e58052a 100644 --- a/www/application/classes/Controller/Transport.php +++ b/www/application/classes/Controller/Transport.php @@ -5,15 +5,14 @@ class Controller_Transport extends Controller_Base_preDispatch private $transportResponse = array( 'success' => 0 ); - - private $type = null; + private $file = null; /** * Where all image files will be stored * Can be overridden, for example, for storing in a temporary dir */ - private $mediaDir = 'upload/redactor_images/'; + private $mediaDir = 'upload/editor'; private const MAX_MEDIA_SIZE = '30M'; @@ -34,26 +33,18 @@ public function action_file_uploader() goto finish; } + /** + * Download file from URL + */ $this->file = $this->methods->getFile($url); - $imageData = $this->saveEditorMedia(); - - if ($imageData) { - $this->transportResponse['success'] = 1; - $this->transportResponse['file'] = array( - 'url' => '/' . $this->mediaDir . 'o_' . Arr::get($imageData, 'name'), - 'width' => Arr::get($imageData, 'width', 0), - 'height' => Arr::get($imageData, 'height', 0) - ); - } - goto finish; + } else { + /** + * Get file from $_FILES + */ + $this->file = Arr::get($_FILES, 'media'); } - /** - * Get file from $_FILES and detect its type - */ - $this->file = Arr::get($_FILES, 'media'); - - if ( ! $this->file || ! Upload::not_empty($this->file) || ! Upload::valid($this->file)) { + if ( ! $this->file || ! Upload::valid($this->file) ) { $this->transportResponse['message'] = 'File is missing or damaged'; goto finish; } @@ -63,16 +54,14 @@ public function action_file_uploader() goto finish; } - $imageData = $this->saveEditorMedia($this->file); - - if ($imageData) { + try { + $fileUrl = Model_Methods::saveMedia($this->file, $this->mediaDir); + $this->transportResponse['success'] = 1; - $this->transportResponse['file'] = array( - 'url' => '/' . $this->mediaDir . Arr::get($imageData, 'name'), - 'width' => Arr::get($imageData, 'width', 0), - 'height' => Arr::get($imageData, 'height', 0) + $this->transportResponse['file'] = array( + 'url' => $fileUrl ); - } else { + } catch (Exception $e) { $this->transportResponse['message'] = 'Something went wrong while saving the file'; } @@ -81,36 +70,4 @@ public function action_file_uploader() $this->response->body(@json_encode($this->transportResponse)); } - private function saveEditorMedia() - { - $mediaType = $this->detectMediaType(); - - switch ($mediaType) { - case 'photo': - $imageData = $this->methods->saveImage($this->file, $this->mediaDir); - break; - case 'video': - $imageData = $this->methods->saveVideo($this->file, $this->mediaDir); - break; - default: - $this->transportResponse['message'] = 'Unsupported file type'; - $imageData = false; - break; - } - - return $imageData; - } - - private function detectMediaType() { - $isPhoto = Upload::type($this->file, array('jpg', 'jpeg', 'png')); - $isVideo = Upload::type($this->file, array('mp4', 'mov', 'gif')); - - if ($isPhoto) { - return 'photo'; - } elseif ($isVideo) { - return 'video'; - } else { - return NULL; - } - } } diff --git a/www/application/classes/Model/Methods.php b/www/application/classes/Model/Methods.php index 97202619..af2a781f 100755 --- a/www/application/classes/Model/Methods.php +++ b/www/application/classes/Model/Methods.php @@ -61,8 +61,6 @@ public function num_decline($num, $nominative, $genitive_singular, $genitive_plu } } - - public function save_cover($cover) { $new_name = bin2hex(openssl_random_pseudo_bytes(5)); @@ -80,8 +78,27 @@ public function save_cover($cover) } } + /** + * Save uploaded media to S3 + */ + public static function saveMedia($file, $folder) + { + try { + $filename = Model_S3::upload($file, $folder); + + // Delete the temporary file + unlink($file['tmp_name']); + + return $filename; + } catch (Exception $e) { + return false; + } + } + /** + * DEPRECATED + * * Save uploaded image * @param resource $file - uploaded file * @param string $path - path where image will be saved @@ -97,47 +114,13 @@ public function saveImage($file, $path) return false; } - if (!is_dir($path)) { - mkdir($path); - } - if (isset($file['tmp_name'])) { - $filename = bin2hex(openssl_random_pseudo_bytes(16)) . '.jpg'; - $image = Image::factory($file['tmp_name']); - $image->save($path . $filename); - - $originalWidth = $image->width; - $originalHeight = $image->height; - - foreach ($this->IMAGE_SIZES_CONFIG as $prefix => $sizes) { - $isSquare = !!$sizes[0]; - $width = $sizes[1]; - $height = !$isSquare ? $sizes[2] : $width; - $image->background('#fff'); - - // Вырезание квадрата - if ($isSquare) { - if ($image->width >= $image->height) { - $image->resize(null, $height, true); - } else { - $image->resize($width, null, true); - } - $image->crop($width, $height); - } else { - if ($image->width > $width || $image->height > $height) { - $image->resize($width, $height, true); - } - } - - $image->save($path . $prefix . '_' . $filename); - } + $filename = Model_S3::upload($file, 'images'); // Delete the temporary file unlink($file['tmp_name']); return array( - 'width' => $originalWidth, - 'height' => $originalHeight, 'name' => $filename ); } @@ -146,6 +129,8 @@ public function saveImage($file, $path) } /** + * DEPRECATED + * * Save uploaded video * @param resource $file - uploaded file * @param string $path - path where video will be saved @@ -154,15 +139,15 @@ public function saveImage($file, $path) */ public function saveVideo($file, $path) { - if (!is_dir($path)) { - mkdir($path); + if (!Upload::type($file, array('mp4', 'mov'))) { + return false; } if (isset($file['tmp_name'])) { - $ext = pathinfo($file['name'], PATHINFO_EXTENSION); - $filename = bin2hex(openssl_random_pseudo_bytes(16)) . '.' . $ext; - - move_uploaded_file($file['tmp_name'], $path . $filename); + $filename = Model_S3::upload($file, 'images'); + + // Delete the temporary file + unlink($file['tmp_name']); return array( 'name' => $filename @@ -172,6 +157,9 @@ public function saveVideo($file, $path) return false; } + /** + * Download file by url + */ public function getFile($url) { $tempName = tempnam('/tmp', 'tmp_files'); diff --git a/www/application/classes/Model/S3.php b/www/application/classes/Model/S3.php new file mode 100644 index 00000000..9492be25 --- /dev/null +++ b/www/application/classes/Model/S3.php @@ -0,0 +1,82 @@ + 'latest', + 'region' => $_ENV['AWS_REGION'], + 'credentials' => [ + 'key' => $_ENV['AWS_ACCESS_KEY_ID'], + 'secret' => $_ENV['AWS_SECRET_ACCESS_KEY'], + ], + ]); + + $filename = self::generateName($file); + + $s3->putObject([ + 'Bucket' => $_ENV['AWS_BUCKET'], + 'Key' => $folder . '/' . $filename, + 'Body' => fopen($file['tmp_name'], 'rb'), + 'ContentType' => $file['type'], + ]); + + $url = self::composeObjectURL($filename, $folder); + + return $url; + } + + /** + * Generates a random name for the file + * + * @param array $file - The file to be uploaded + * + * @return string - The generated file name + */ + private static function generateName($file) + { + $ext = self::getExtension($file); + $filename = bin2hex(openssl_random_pseudo_bytes(16)) . '.' . $ext; + + return $filename; + } + + /** + * Get the extension of the file + * + * @param array $file - The file to be uploaded + * + * @return string - The file extension + */ + private static function getExtension($file) { + $ext = pathinfo($file['name'], PATHINFO_EXTENSION); + + return $ext; + } + + /** + * Compose the URL for the uploaded file + * + * @param string $filename - The generated file name + * @param string $folder - The folder where the file was uploaded + * + * @return string - The URL of the uploaded file + */ + private static function composeObjectURL($filename, $folder) + { + $url = $_ENV['STATIC_HOST'] . '/' . $folder . '/' . $filename; + + return $url; + } +} diff --git a/www/composer.json b/www/composer.json index 0a65723e..cb35f0eb 100755 --- a/www/composer.json +++ b/www/composer.json @@ -7,6 +7,7 @@ "codex-team/hawk.php": "^2.1", "pavelzotikov/social-covers-generator": "dev-first-version", "ext-SimpleXML": "*", - "stichoza/google-translate-php": "v4.1.7" + "stichoza/google-translate-php": "v4.1.7", + "aws/aws-sdk-php": "^3.256" } } diff --git a/www/composer.lock b/www/composer.lock index 8fdfb000..ed7a7601 100644 --- a/www/composer.lock +++ b/www/composer.lock @@ -4,8 +4,152 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dccf9546ba802d95667f7bb7b9f71f78", + "content-hash": "e96043ca5c19ba7924400ebab1a6107d", "packages": [ + { + "name": "aws/aws-crt-php", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/awslabs/aws-crt-php.git", + "reference": "3942776a8c99209908ee0b287746263725685732" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/3942776a8c99209908ee0b287746263725685732", + "reference": "3942776a8c99209908ee0b287746263725685732", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|^5.4.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "AWS SDK Common Runtime Team", + "email": "aws-sdk-common-runtime@amazon.com" + } + ], + "description": "AWS Common Runtime for PHP", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "crt", + "sdk" + ], + "support": { + "issues": "https://github.com/awslabs/aws-crt-php/issues", + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.0.2" + }, + "time": "2021-09-03T22:57:30+00:00" + }, + { + "name": "aws/aws-sdk-php", + "version": "3.256.2", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "a72094f7d968bdc743839e309087d51f868ba26c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a72094f7d968bdc743839e309087d51f868ba26c", + "reference": "a72094f7d968bdc743839e309087d51f868ba26c", + "shasum": "" + }, + "require": { + "aws/aws-crt-php": "^1.0.2", + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "guzzlehttp/promises": "^1.4.0", + "guzzlehttp/psr7": "^1.8.5 || ^2.3", + "mtdowling/jmespath.php": "^2.6", + "php": ">=5.5" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "composer/composer": "^1.10.22", + "dms/phpunit-arraysubset-asserts": "^0.4.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "paragonie/random_compat": ">= 2", + "phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3 || ^4.0", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Aws\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "support": { + "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", + "issues": "https://github.com/aws/aws-sdk-php/issues", + "source": "https://github.com/aws/aws-sdk-php/tree/3.256.2" + }, + "time": "2023-01-11T20:12:50+00:00" + }, { "name": "codex-team/editor.js", "version": "v2.0.7", @@ -631,6 +775,67 @@ ], "time": "2021-07-23T07:42:52+00:00" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/9b87907a81b87bc76d19a7fb2d61e61486ee9edb", + "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb", + "shasum": "" + }, + "require": { + "php": "^5.4 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^7.5.15" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "files": [ + "src/JmesPath.php" + ], + "psr-4": { + "JmesPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.6.1" + }, + "time": "2021-06-14T00:11:39+00:00" + }, { "name": "pavelzotikov/social-covers-generator", "version": "dev-first-version", @@ -1560,6 +1765,89 @@ ], "time": "2021-02-19T12:13:01+00:00" }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, { "name": "symfony/yaml", "version": "v2.8.52", diff --git a/www/public/build/release.json b/www/public/build/release.json index a8a3e581..9b0fedee 100644 --- a/www/public/build/release.json +++ b/www/public/build/release.json @@ -1 +1 @@ -{"release":"d0306d9565948f8ad52d","date":1673468892276} \ No newline at end of file +{"release":"d0306d9565948f8ad52d","date":1673565266122} \ No newline at end of file