fine-uploader PHP Server Side Merge - fine-uploader

I'm been experimenting with Fine Uploader. I am really interested in the chunking and resume features, but I'm experiencing difficulties putting the files back together server side;
What I've found is that I have to allow for a blank file extension on the server side to allow the upload of the chunks, otherwise the upload will fail with unknown file type. It uploads the chunks fine with file names such as "blob" and "blob63" (no file extension) however is does not merge them back at completion of upload.
Any help or pointers would be appreciated.
$('#edit-file-uploader').fineUploader({
request: {
endpoint: 'upload.php'
},
multiple: false,
validation:{
allowedExtentions: ['stl', 'obj', '3ds', 'zpr', 'zip'],
sizeLimit: 104857600 // 100mb * 1024 (kb) * 1024 (bytes)
},
text: {
uploadButton: 'Select File'
},
autoUpload: false,
chunking: {
enabled: true
},
callbacks: {
onComplete: function(id, fileName, responseJSON) {
if (responseJSON.success) {
/** some code here **??
}
}
});
And this is the server side script (PHP):
// list of valid extensions, ex. array("stl", "xml", "bmp")
$allowedExtensions = array("stl", "");
// max file size in bytes
$sizeLimit = null;
$uploader = new qqFileUploader($allowedExtensions, $sizeLimit);
// Call handleUpload() with the name of the folder, relative to PHP's getcwd()
$result = $uploader->handleUpload('uploads/');
// to pass data through iframe you will need to encode all html tags
echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
/******************************************/
/**
* Handle file uploads via XMLHttpRequest
*/
class qqUploadedFileXhr {
/**
* Save the file to the specified path
* #return boolean TRUE on success
*/
public function save($path) {
$input = fopen("php://input", "r");
$temp = tmpfile();
$realSize = stream_copy_to_stream($input, $temp);
fclose($input);
if ($realSize != $this->getSize()){
return false;
}
$target = fopen($path, "w");
fseek($temp, 0, SEEK_SET);
stream_copy_to_stream($temp, $target);
fclose($target);
return true;
}
/**
* Get the original filename
* #return string filename
*/
public function getName() {
return $_GET['qqfile'];
}
/**
* Get the file size
* #return integer file-size in byte
*/
public function getSize() {
if (isset($_SERVER["CONTENT_LENGTH"])){
return (int)$_SERVER["CONTENT_LENGTH"];
} else {
throw new Exception('Getting content length is not supported.');
}
}
}
/**
* Handle file uploads via regular form post (uses the $_FILES array)
*/
class qqUploadedFileForm {
/**
* Save the file to the specified path
* #return boolean TRUE on success
*/
public function save($path) {
return move_uploaded_file($_FILES['qqfile']['tmp_name'], $path);
}
/**
* Get the original filename
* #return string filename
*/
public function getName() {
return $_FILES['qqfile']['name'];
}
/**
* Get the file size
* #return integer file-size in byte
*/
public function getSize() {
return $_FILES['qqfile']['size'];
}
}
/**
* Class that encapsulates the file-upload internals
*/
class qqFileUploader {
private $allowedExtensions;
private $sizeLimit;
private $file;
private $uploadName;
/**
* #param array $allowedExtensions; defaults to an empty array
* #param int $sizeLimit; defaults to the server's upload_max_filesize setting
*/
function __construct(array $allowedExtensions = null, $sizeLimit = null){
if($allowedExtensions===null) {
$allowedExtensions = array();
}
if($sizeLimit===null) {
$sizeLimit = $this->toBytes(ini_get('upload_max_filesize'));
}
$allowedExtensions = array_map("strtolower", $allowedExtensions);
$this->allowedExtensions = $allowedExtensions;
$this->sizeLimit = $sizeLimit;
$this->checkServerSettings();
if(!isset($_SERVER['CONTENT_TYPE'])) {
$this->file = false;
} else if (strpos(strtolower($_SERVER['CONTENT_TYPE']), 'multipart/') === 0) {
$this->file = new qqUploadedFileForm();
} else {
$this->file = new qqUploadedFileXhr();
}
}
/**
* Get the name of the uploaded file
* #return string
*/
public function getUploadName(){
if( isset( $this->uploadName ) )
return $this->uploadName;
}
/**
* Get the original filename
* #return string filename
*/
public function getName(){
if ($this->file)
return $this->file->getName();
}
/**
* Internal function that checks if server's may sizes match the
* object's maximum size for uploads
*/
private function checkServerSettings(){
$postSize = $this->toBytes(ini_get('post_max_size'));
$uploadSize = $this->toBytes(ini_get('upload_max_filesize'));
if ($postSize < $this->sizeLimit || $uploadSize < $this->sizeLimit){
$size = max(1, $this->sizeLimit / 1024 / 1024) . 'M';
die(json_encode(array('error'=>'increase post_max_size and upload_max_filesize to ' . $size)));
}
}
/**
* Convert a given size with units to bytes
* #param string $str
*/
private function toBytes($str){
$val = trim($str);
$last = strtolower($str[strlen($str)-1]);
switch($last) {
case 'g': $val *= 1024;
case 'm': $val *= 1024;
case 'k': $val *= 1024;
}
return $val;
}
/**
* Handle the uploaded file
* #param string $uploadDirectory
* #param string $replaceOldFile=true
* #returns array('success'=>true) or array('error'=>'error message')
*/
function handleUpload($uploadDirectory, $replaceOldFile = FALSE){
if (!is_writable($uploadDirectory)){
return array('error' => "Server error. Upload directory isn't writable.");
}
if (!$this->file){
return array('error' => 'No files were uploaded.');
}
$size = $this->file->getSize();
if ($size == 0) {
return array('error' => 'File is empty');
}
if ($size > $this->sizeLimit) {
return array('error' => 'File is too large');
}
$pathinfo = pathinfo($this->file->getName());
$filename = $pathinfo['filename'];
//$filename = md5(uniqid());
$ext = #$pathinfo['extension']; // hide notices if extension is empty
if($this->allowedExtensions && !in_array(strtolower($ext), $this->allowedExtensions)){
$these = implode(', ', $this->allowedExtensions);
return array('error' => 'File has an invalid extension, it should be one of '. $these . '.');
}
$ext = ($ext == '') ? $ext : '.' . $ext;
if(!$replaceOldFile){
/// don't overwrite previous files that were uploaded
while (file_exists($uploadDirectory . DIRECTORY_SEPARATOR . $filename . $ext)) {
$filename .= rand(10, 99);
}
}
$this->uploadName = $filename . $ext;
if ($this->file->save($uploadDirectory . DIRECTORY_SEPARATOR . $filename . $ext)){
return array('success'=>true);
} else {
return array('error'=> 'Could not save uploaded file.' .
'The upload was cancelled, or server error encountered');
}
}
}

In order to handle chunked requests, you MUST store each chunk separately in your filesystem.
How you name these chunks or where you store them is up to you, but I suggest you name them using the UUID provided by Fine Uploader and append the part number parameter included with each chunked request. After the last chunk has been sent, combine all chunks into one file, with the proper name, and return a standard success response as described in the Fine Uploader documentation. The original name of the file is, by default, passed in a qqfilename parameter with each request. This is also discussed in the docs and the blog.
It doesn't look like you've made any attempt to handle chunks server-side. There is a PHP example in the Widen/fine-uploader-server repo that you can use. Also, the documentation has a "server-side" section that explains how to handle chunking in detail. I'm guessing you did not read this. Have a look.) in the Widen/fine-uploader-server repo that you can use. Also, the documentation has a "server-side" section that explains how to handle chunking in detail. I'm guessing you did not read this. Have a look.
Note that, starting with Fine Uploader 3.8 (set to release VERY soon) you will be able to delegate all server-side upload handling to Amazon S3, as Fine Uploader will provide tight integration with S3 that sends all of your files directly to your bucket from the browser without you having to worry about constructing a policy document, making REST API calls, handling responses from S3, etc. I mention this as using S3 means that you never have to worry about handling things like chunked requests on your server again.

Related

How to queue upload to s3 using Laravel?

I'm dispatching a job to queue my video file, the files are being stored on s3.
Everything is working except if I upload a video file for example that's 20mb, when I look in my bucket it says the file is 120b. So this makes me think that I'm uploading the path and filename as a string instead of the file object.
And for some reason, when I try getting the file using the Storage::get() or File::get() and dd the result, it shows a bunch or random and crazy characters.
It seems like I can only get these weird characters, or a string, I can't get the file object for some reason.
In my controller I'm also storing it in the public disk (I will delete the file later in my Jobs/UploadVideos.php file).
CandidateProfileController.php:
$candidateProfile = new CandidateProfile();
$candidateProfile->disk = config('site.upload_disk');
// Video One
if($file = $request->file('video_one')) {
$file_path = $file->getPathname();
$name = time() . $file->getClientOriginalName();
$name = preg_replace('/\s+/', '-', $name);
$file->storePubliclyAs('videos', $name, 'public');
$candidateProfile->video_one = $name;
}
if($candidateProfile->save()) {
// dispatch a job to handle the image manipulation
$this->dispatch(new UploadVideos($candidateProfile));
return response()->json($candidateProfile, 200);
} else {
return response()->json([
'message' => 'Some error occurred, please try again.',
'status' => 500
], 500);
}
Jobs/UploadVideos.php:
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $candidateprofile;
public $timeout = 120;
public $tries = 5;
/**
* Create a new job instance.
*
* #param CandidateProfile $candidateProfile
*/
public function __construct(CandidateProfile $candidateProfile)
{
$this->candidateprofile = $candidateProfile;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$disk = $this->candidateprofile->disk;
$filename = $this->candidateprofile->video_one;
$original_file = storage_path() . '/videos/' . $filename;
try {
// Video One
Storage::disk($disk)
->put('videos/'.$filename, $original_file, 'public');
// Update the database record with successful flag
$this->candidateprofile->update([
'upload_successful' => true
]);
} catch(\Exception $e){
Log::error($e->getMessage());
}
}
File Storage docs
The 2nd parameter for put() should be the contents of the file not the path to the file. Also, unless you've updated the public disk in your config/filesystem.php, the video isn't going to be stored in storage_path() . '/videos/...'.
To get this to work you should just need to update your Job code:
$filename = 'videos/' . $this->candidateprofile->video_one;
Storage::disk($this->candidateprofile->disk)
->put($filename, Storage::disk('public')->get($filename), 'public');
$this->candidateprofile->update([
'upload_successful' => true,
]);
Also, wrapping your code in a try/catch will mean that the Job won't retry as it will technically never fail.

Laravel blank projects.. so many files! Can it be reduced somehow?

Last time I used Laravel was a long time ago and I decided to get back to it.
Now coming from CodeIgniter, which was a powerful framework in its own time, I was happy to upload projects to websites as the "system" folder containing the framework files contained only 121 files.
The problem with composer-based solutions however is that a tiny project can become huge, much bigger than a very large scale CodeIgniter project back in the days. All the dependencies have test folders, documentation, and tons of modules when sometimes only one method is used.
I gasped when creating an empty Laravel project using the instructions from the official documentation and seeing the "vendor" folder containing over 8,000 files!! (Not counting the folders) And it's doing nothing yet.. That is when using the --prefer-dist flag by the way. And I know about the --no-dev argument, which still has 5,000+ files. My point was that there is no way all these files are used, especially when using the distribution channel.
So my question is if there is a way to have a more selective empty Laravel project, as servers often have limited Inodes and 8,000 files + folders for every project makes you reach the limit really quickly (and upload takes forever if you can't install composer on your server).
Composer can remove extraneous files.
In your project's composer.json, specify the files you don't want with either the archive and/or exclude-files-from-classmaps configuration values, then use composer's archive command to create a zip. Upload the zip and expand on the server, or expand locally and transfer the now smaller package.
$ cat composer.json
...
{
"archive": {
"exclude": ["!vendor", "/test/*", "/*.jpg" ]
}
}
$ php composer.phar archive --format=zip --file=<filename-without-extension>
Those files matched by archive will not be present, at all, in your zip. Those files matched by exclude-files-from-classmaps will be present in the filesystem, but invisible to the autoloader.
I had a Same Situation Before few Days so i have created the console command to delete the unused files in the vendor Directory
Step :1
php artisan make:command CleanVendorFolderCommand
Step: 2
Copy the current code and paste int in the Command Class
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
use FilesystemIterator;
class CleanVendorFolderCommand extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'clean:vendor {--o : Verbose Output} {--dry : Runs in dry mode without deleting files.}';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Cleans up useless files from vendor folder.';
protected $patterns =
[
'test',
'tests',
'.github',
'README',
'CHANGELOG',
'FAQ',
'CONTRIBUTING',
'HISTORY',
'UPGRADING',
'UPGRADE',
'demo',
'example',
'examples',
'.doc',
'readme',
'changelog',
'composer',
'.git',
'.gitignore',
'*.md',
'.*.yml',
'*.yml',
'*.txt',
'*.dist',
'LICENSE',
'AUTHORS',
'.eslintrc',
'ChangeLog',
'.gitignore',
'.editorconfig',
'*.xml',
'.npmignore',
'.jshintrc',
'Makefile',
'.keep',
];
/**
* List of File and Folders Patters Going To Be Excluded
*
* #return void
*/
protected $excluded =
[
/**List of Folders*/
'src',
/**List of Files*/
'*.php',
'*.stub',
'*.js',
'*.json',
];
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$patterns = array_diff($this->patterns, $this->excluded);
$directories = $this->expandTree(base_path('vendor'));
$isDry = $this->option('dry');
foreach ($directories as $directory)
{
foreach ($patterns as $pattern)
{
$casePattern = preg_replace_callback('/([a-z])/i', [$this, 'prepareWord'], $pattern);
$files = glob($directory . '/' . $casePattern, GLOB_BRACE);
if (!$files)
{
continue;
}
$files = array_diff($files, $this->excluded);
foreach ($this->excluded as $excluded)
{
$key = $this->arrayFind($excluded, $files);
if ($key !== false)
{
$this->warn('SKIPPED: ' . $files[$key]);
unset($files[$key]);
}
}
foreach ($files as $file)
{
if (is_dir($file))
{
$this->warn('DELETING DIR: ' . $file);
if (!$isDry)
{
$this->delTree($file);
}
} else
{
$this->warn('DELETING FILE: ' . $file);
if (!$isDry)
{
#unlink($file);
}
}
}
}
}
$this->warn('Folder Cleanup Done!');
}
/**
* Recursively traverses the directory tree
*
* #param string $dir
* #return array
*/
protected function expandTree($dir)
{
$directories = [];
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file)
{
$directory = $dir . '/' . $file;
if (is_dir($directory))
{
$directories[] = $directory;
$directories = array_merge($directories, $this->expandTree($directory));
}
}
return $directories;
}
/**
* Recursively deletes the directory
*
* #param string $dir
* #return bool
*/
protected function delTree($dir) {
if (!file_exists($dir) || !is_dir($dir))
{
return false;
}
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $filename => $fileInfo)
{
if ($fileInfo->isDir())
{
#rmdir($filename);
} else {
#unlink($filename);
}
}
#rmdir($dir);
}
/**
* Prepare word
*
* #param string $matches
* #return string
*/
protected function prepareWord($matches)
{
return '[' . strtolower($matches[1]) . strtoupper($matches[1]) . ']';
}
protected function arrayFind($needle, array $haystack)
{
foreach ($haystack as $key => $value)
{
if (false !== stripos($value, $needle))
{
return $key;
}
}
return false;
}
protected function out($message)
{
if ($this->option('o') || $this->option('dry'))
{
echo $message . PHP_EOL;
}
}
}
TESTED ON
OS Name Microsoft Windows 10 Pro
Version 10.0.16299 Build 16299
Processor Intel(R) Core(TM) i3-7100U CPU # 2.40GHz, 2400 Mhz, 2 Core(s), 4 Logical Processor(s)
NOW THE TESTING PART
Before Size of vendor Folder
Size 57.0 MB (5,98,29,604 bytes)
Size on disk 75.2 MB (7,88,80,768 bytes)
Contains 12,455 Files, 2,294 Folders
Now Run the Command
php artisan clean:vendor
Size of vendor Folder after Running the command
Size 47.0 MB (4,93,51,781 bytes)
Size on disk 59.7 MB (6,26,76,992 bytes)
Contains 8,431 Files, 1,570 Folders
Hope it helps

Typo3 fluid image from external resource

is it possible to resize images in fluid from external resource. I have an extension with datas from SOAP. So image URL looks like http://www.example.com/url/of/image/imagename.jpg.
<f:image src="{data.url.image}" with="300" />
is not working.
Maybe an own ViewHelper which fetch the external image and save it to an temporary folder could help. After this you can modify the image.
Something like this (not tested):
<?php
namespace MyNamespaece\MyExt\ViewHelpers;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\ViewHelpers\ImageViewHelper;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder;
class ExternalImageViewHelper extends ImageViewHelper
{
const UPLOAD_DIRECTORY = 'externalImages';
const TEMP_PREFIX = 'MyExt';
/**
* ResourceFactory
*
* #var \TYPO3\CMS\Core\Resource\ResourceFactory
* #inject
*/
protected $resourceFactory = null;
/**
* Resizes a given image (if required) and renders the respective img tag
*
* #see https://docs.typo3.org/typo3cms/TyposcriptReference/ContentObjects/Image/
*
* #param string $src a path to a file, a combined FAL identifier or an uid (integer). If $treatIdAsReference is set, the integer is considered the uid of the sys_file_reference record. If you already got a FAL object, consider using the $image parameter instead
* #param string $width width of the image. This can be a numeric value representing the fixed width of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.
* #param string $height height of the image. This can be a numeric value representing the fixed height of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.
* #param integer $minWidth minimum width of the image
* #param integer $minHeight minimum height of the image
* #param integer $maxWidth maximum width of the image
* #param integer $maxHeight maximum height of the image
* #param boolean $treatIdAsReference given src argument is a sys_file_reference record
* #param FileInterface|AbstractFileFolder $image a FAL object
*
* #return string
* #throws \Exception
* #throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
* #throws \TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException
* #throws \TYPO3\CMS\Fluid\Core\ViewHelper\Exception
*/
public function render($src = null, $width = null, $height = null, $minWidth = null, $minHeight = null, $maxWidth = null, $maxHeight = null, $treatIdAsReference = false, $image = null)
{
if (filter_var($src, FILTER_VALIDATE_URL)) {
$storage = $this->resourceFactory->getDefaultStorage();
if (!$storage->hasFolder(self::UPLOAD_DIRECTORY)) {
$storage->createFolder(self::UPLOAD_DIRECTORY);
}
$externalFile = GeneralUtility::getUrl($src);
if ($externalFile) {
$tempFileName = tempnam(sys_get_temp_dir(), self::TEMP_PREFIX);
$handle = fopen($tempFileName, "w");
fwrite($handle, $externalFile);
fclose($handle);
$uploadFolder = $storage->getFolder(self::UPLOAD_DIRECTORY);
$file = $uploadFolder->addFile($tempFileName, basename(basename($src)), 'changeName');
$src = $file->getPublicUrl();
unlink($tempFileName);
} else {
throw new \Exception(sprintf('External URL % cannot accessed.', $src), 1473233519);
}
}
return parent::render($src, $width, $height, $minWidth, $minHeight, $maxWidth, $maxHeight, $treatIdAsReference, $image);
}
}
Please Note: This ViewHelper has no check if the image is allready fetched! So an check should be integrated. Otherwise this viewhelper fetch the image at each page refresh!
As mentioned in the comments I want to clarify that this ViewHelper should not be used in any production environment. It should only demonstrate how the way to such an viewhelper could be. Compiled templates are not supported. Also no needed check if the file already exists is implemented. Your hosting environment could be flooded with downloads and can break you file quota!
The short answer is: This is not possible.
The long answer is: Of course it is possible if you fetch the image first. There are various ways to do it:
At runtime as rpflamm suggested by using a ViewHelper
Do it in your controller/service when you fetch the SOAP call. IMO this would be the best way. Persist then the image and use the local path
If the images you fetch are not that big, of course a resizing via CSS is also an option
Working Version with cropping-feature for TYPO3 10.4 LTS:
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*
* Example: <xyz:externalImage filename="OPTINAL_FILENAME" src="EXTERNAL_URL" width="480c" height="270c" title="YOUR TITLE" alt="YOUR ALT" class="YOUR-CLASS" />
*/
namespace YourNamespaece\YourExtension\ViewHelpers;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Service\ImageService;
use TYPO3\CMS\Fluid\Core\Widget\Exception;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
class ExternalImageViewHelper extends AbstractTagBasedViewHelper {
/**
* #var string
*/
protected $tagName = 'img';
/**
* #var \TYPO3\CMS\Extbase\Service\ImageService
*/
protected $imageService;
/**
* #param \TYPO3\CMS\Extbase\Service\ImageService $imageService
*/
public function injectImageService(ImageService $imageService)
{
$this->imageService = $imageService;
}
const UPLOAD_DIRECTORY = 'externalImages';
const TEMP_PREFIX = 'diakonie_baukasten';
/**
* ResourceFactory
*
* #var ResourceFactory
* #TYPO3\CMS\Extbase\Annotation\Inject
*/
protected ResourceFactory $resourceFactory;
/**
* Initialize arguments.
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('alt', 'string', 'Specifies an alternate text for an image', false);
$this->registerArgument('filename', 'string', 'Override filename for local file.', false, '');
$this->registerArgument('src', 'string', 'a path to a file, a combined FAL identifier or an uid (int). If $treatIdAsReference is set, the integer is considered the uid of the sys_file_reference record. If you already got a FAL object, consider using the $image parameter instead', true, '');
$this->registerArgument('width', 'string', 'width of the image. This can be a numeric value representing the fixed width of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('height', 'string', 'height of the image. This can be a numeric value representing the fixed height of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('minWidth', 'int', 'minimum width of the image');
$this->registerArgument('minHeight', 'int', 'minimum height of the image');
$this->registerArgument('maxWidth', 'int', 'maximum width of the image');
$this->registerArgument('maxHeight', 'int', 'maximum height of the image');
$this->registerArgument('absolute', 'bool', 'Force absolute URL', false, false);
}
/**
* Resizes a given image (if required) and renders the respective img tag
*
* #see https://docs.typo3.org/typo3cms/TyposcriptReference/ContentObjects/Image/
*
* #return string Rendered tag
* #throws \Exception
*/
public function render()
{
$src = (string)$this->arguments['src'];
$filename = (string)$this->arguments['filename'];
if ($src === '') {
throw new Exception('You must either specify a string src', 1382284106);
}
// A URL was given as src, this is kept as is, and we can only scale
if ($src !== '' && preg_match('/^(https?:)?\/\//', $src)) {
if (filter_var($src, FILTER_VALIDATE_URL)) {
$storage = $this->resourceFactory->getDefaultStorage();
if (!$storage->hasFolder(self::UPLOAD_DIRECTORY)) {
$storage->createFolder(self::UPLOAD_DIRECTORY);
}
$externalFile = GeneralUtility::getUrl($src);
if ($externalFile) {
$tempFileName = tempnam(sys_get_temp_dir(), self::TEMP_PREFIX);
$handle = fopen($tempFileName, "w");
fwrite($handle, $externalFile);
fclose($handle);
if ($filename !== '') {
$fileNameNoExtension = preg_replace("/\.[^.]+$/", "", $src);
$fileExtension = str_replace($fileNameNoExtension,"", $src);
$src = $filename.$fileExtension;
}
$uploadFolder = $storage->getFolder(self::UPLOAD_DIRECTORY);
$file = $uploadFolder->addFile($tempFileName, basename(basename($src)), 'replace');
$src = $file->getPublicUrl();
unlink($tempFileName);
$image = $this->imageService->getImage($src, null, false);
$processingInstructions = [
'width' => $this->arguments['width'],
'height' => $this->arguments['height'],
'minWidth' => $this->arguments['minWidth'],
'minHeight' => $this->arguments['minHeight'],
'maxWidth' => $this->arguments['maxWidth'],
'maxHeight' => $this->arguments['maxHeight'],
'crop' => null,
];
$processedImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions);
$imageUri = $this->imageService->getImageUri($processedImage, $this->arguments['absolute']);
$this->tag->addAttribute('src', $imageUri);
$this->tag->addAttribute('width', $processedImage->getProperty('width'));
$this->tag->addAttribute('height', $processedImage->getProperty('height'));
// The alt-attribute is mandatory to have valid html-code, therefore add it even if it is empty
if (empty($this->arguments['alt'])) {
$this->tag->addAttribute('alt', $image->hasProperty('alternative') ? $image->getProperty('alternative') : '');
}
// Add title-attribute from property if not already set and the property is not an empty string
$title = (string)($image->hasProperty('title') ? $image->getProperty('title') : '');
if (empty($this->arguments['title']) && $title !== '') {
$this->tag->addAttribute('title', $title);
}
return $this->tag->render();
} else {
throw new \Exception(sprintf('External URL % cannot accessed.', $src), 1473233519);
}
}
}
}
}

codeigniter load external config file

I'm working with CodeIgniter and I’d like to load one or more config files located in an external folder, shared by different CI installation.
Is it possible?
I tried to extend the Loader Class, and call the new method:
$this -> load -> external_config(‘MY\EXTERNAL\PATH’);
Load is successful, but i can’t retrieve config items in my controller, because in MY_Loader class the core\Loader config property is not visible, and i can’t merge it with the new loaded values.
This is my code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Loader extends CI_Loader {
/**
* List of all loaded config files
*
* #var array
*/
var $is_config_loaded = array();
/**
* List of all loaded config values
*
* #var array
*/
//var $ext_config = array();
function __construct(){
parent::__construct();
}
/**
* Loads an external config file
*
* #param string
* #param bool
* #param bool
* #return void
*/
public function external_config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
{
$file = ($file == '') ? 'config' : str_replace('.php', '', $file);
$found = FALSE;
$loaded = FALSE;
$check_locations = defined('ENVIRONMENT')
? array(ENVIRONMENT.'/'.$file, $file)
: array($file);
foreach ($check_locations as $location)
{
$file_path = $file.'.php';
if (in_array($file_path, $this->is_config_loaded, TRUE))
{
$loaded = TRUE;
continue 2;
}
if (file_exists($file_path))
{
$found = TRUE;
break;
}
}
if ($found === FALSE)
{
if ($fail_gracefully === TRUE)
{
return FALSE;
}
show_error('The configuration file '.$file.'.php does not exist.');
}
else{
include($file_path);
if ( ! isset($config) OR ! is_array($config))
{
if ($fail_gracefully === TRUE)
{
return FALSE;
}
show_error('Your '.$file_path.' file does not appear to contain a valid configuration array.');
}
if ($use_sections === TRUE)
{
if (isset($this->ext_config[$file]))
{
$this->ext_config[$file] = array_merge($this->ext_config[$file], $config);
}
else
{
$this->ext_config[$file] = $config;
}
}
else
{
$this->ext_config = array_merge($this->ext_config, $config);
}
$this->is_config_loaded[] = $file_path;
unset($config);
$loaded = TRUE;
log_message('debug', 'Config file loaded: '.$file_path);
}
return TRUE;
}
}
I found in core\Config.php a property
var $_config_paths = array(APPPATH);
With the paths in wich look for config files.
How can i add paths to the array without chance the core classe code?
Any ideas?
Thank you very much!!!
CodeIgniter makes assumptions about the config file being loaded, its location, its file ext etc. You may load other files that is: from the same directory (support seems to be limited to this usage scenario).
However, you can (looking at the chapter https://ellislab.com/codeigniter/user-guide/libraries/config.html (Setting a Config Item)) iterate your file values and set them like so
(todo: add some checks if file exists, is readable, no decoding errors etcetera)
$myConfigValues = json_decode(file_get_contents($configKeyValuesFromOuterSpace));
foreach($myConfigValues as $key => $value){
$CI->config->set_item('item_name', 'item_value');
}
As for the exposure of the config object, in the loader you may anytime examine its public parts in more or less subtle manners, by using the code igniter singleton instance retrieval method:
$CI =& get_instance();
die(print_r($CI->config,true));
This will be a CI_Config object. You may also create a MY_Config extension.

Generate password_hash which matches Magento Go API

I am trying to authenticate a user through the Magento Go SOAP API and having problems generating a matching hash. According to the docs the password_hash contains password:salt however when I md5 it's not matching the password_hash.
Example:
1) I changed my password through admin control panel to 'testtest'
2) Run the following code:
$client = new SoapClient('http://XXXX.gostorego.com/api/v2_soap/?wsdl');
$session = $client->login($api_user, $api_pass);
$params = array('filter'=>array(array('key'=>'email','value'=>'user#domain.com')));
$data = $client->customerCustomerList($session, $params);
echo '<pre>CUSTOMER: '.print_r($data, true).'</pre>';
if (count($data)) {
$hash = explode(':',$data[0]->password_hash);
$salt = $hash[1];
echo '<pre>HASH PARTS:'.print_r($hash, true).'</pre>';
echo '<br>' .md5($salt.$password);
}
3) password_hash is f35604820826428dd7633b91cd6078f4075c9bfa1a37db7bc70f563475ad8495:qK
4) MD5 is 0b04a656c770ba2f10b5918f94529cd8
I've never done this with with Magento Go (and I'm not sure it's supported/possible) but the hash string
f35604820826428dd7633b91cd6078f4075c9bfa1a37db7bc70f563475ad8495:qK
is too long to be a MD5 hash of a string. That's a 64 byte hash (plus the :, plus the salt qK). My guess is it's SHA256, but that's a guess based on character length.
On the backend, Both Md5 and SHA are being supported, with newer support leaning towards the SHA (in enterprise).
If your password were: 12341234
The DB Hash would infact be similar to: cdb757ce51af9749d2fabea4cf71dc72a1ec7b8721e5f8de83020f574ca3c5f1:TR
And is indeed SHA256.
However, the remote connection should be "https:" over SSL for the WSDL file and you should be entering your SOAP API key in normal/plain text.
ie:
$username = "myUsername"; //ie. yourApiUsername
$password = "myUserPass"; //ie. 12341234
If you want to replicate their hashing for your own internal purposes, you need to look at their methods: class Mage_Core_Model_Encryption
public function hash($data)
{
return md5($data);
}
/**
* Validate hash against hashing method (with or without salt)
*
* #param string $password
* #param string $hash
* #return bool
* #throws Exception
*/
public function validateHash($password, $hash)
{
$hashArr = explode(':', $hash);
switch (count($hashArr)) {
case 1:
return $this->hash($password) === $hash;
case 2:
return $this->hash($hashArr[1] . $password) === $hashArr[0];
}
Mage::throwException('Invalid hash.');
}
Enterprise:
public function hash($data, $version = self::HASH_VERSION_LATEST)
{
if (self::HASH_VERSION_MD5 === $version) {
return md5($data);
}
return hash('sha256', $data);
}
/**
* Validate hash by specified version
*
* #param string $password
* #param string $hash
* #param int $version
* #return bool
*/
public function validateHashByVersion($password, $hash, $version = self::HASH_VERSION_LATEST)
{
// look for salt
$hashArr = explode(':', $hash, 2);
if (1 === count($hashArr)) {
return $this->hash($password, $version) === $hash;
}
list($hash, $salt) = $hashArr;
return $this->hash($salt . $password, $version) === $hash;
}

Resources