media_sideload_image
WordPress have a function called media_sideload_image. It is used to upload an image and attach it to the media library.
I accepts image urls like this:
h**p://s.wordpress.org/style/images/wp-header-logo.png
Rewritten URLs
Some URLs on the web are rewritten, for example:
http://placekitten.com/100/100
Error message:
"Sorry, this file type is not permitted for security reasons."
The file type is a correct JPG-file but the file extension is missing.
Adding extra MIME types don't work, in my case
I tried this function but it does not help me, because it's the file extension that is not set.
add_filter('upload_mimes', 'add_custom_upload_mimes');
function add_custom_upload_mimes($existing_mimes){
$existing_mimes['jpeg'] = 'image/jpeg';
return $existing_mimes;
}
Question
How do I upload the URL h**p://placekitten.com/100/100 with media_sideload_image or alike to attach the image to the media library?
I read your question yesterday, when i need this solution.
I find a answer after 24 hours.
Here is Full solution
require_once(ABSPATH . 'wp-admin/includes/media.php');
require_once(ABSPATH . 'wp-admin/includes/file.php');
require_once(ABSPATH . 'wp-admin/includes/image.php');
$image_url = "http://domain.com/blog/23092839823";
$image_tmp = download_url($image_url);
if( is_wp_error( $image_tmp ) ){
echo "<br> Image Download Fail:";
}else {
$image_size = filesize($image_tmp);
$image_name = basename($image_url) . ".jpg"; // .jpg optional
//Download complete now upload in your project
$file = array(
'name' => $image_name, // ex: wp-header-logo.png
'type' => 'image/jpg',
'tmp_name' => $image_tmp,
'error' => 0,
'size' => $image_size
);
//This image/file will show on media page...
$thumb_id = media_handle_sideload( $file, $post_id, $desc);
set_post_thumbnail($post_id, $thumb_id); //optional
echo "<br> Image Save ";
}
Digging into core, it looks like you need the unfiltered_upload capability in order to upload files without an extension:
if ( ( !$type || !$ext ) && !current_user_can( 'unfiltered_upload' ) )
return $upload_error_handler( $file, __( 'Sorry, this file type is not permitted for security reasons.' ));
According to the Roles and Capabilities documentation:
This capability is not available to any role by default (including Super Admins). The capability needs to be enabled by defining the following constant:
define( 'ALLOW_UNFILTERED_UPLOADS', true );
With this constant defined, all roles on a single site install will be given the unfiltered_upload capability, but only Super Admins will be given the capability on a Multisite install.
Today I have faced the same problem, and come up with a bit dirty yet successful method to work around. As it turns out, media_sideload_image only checks for the .jpg (or any image) extension in the url, so if you add it to the end of your link, it shoud work.
So you only need to add something to the end of the url that won't change the output, for example:
http://placekitten.com/100/100?name=image.jpg
I can't say it works all the time, but it works here (TESTED). :)
Related
I would like add my own regular expression for validating the phone number. In my class-wc-validation.php I have changed the regular expression to my requirement.
public static function is_phone( $phone ) {
//if ( strlen( trim( preg_replace( '/[\s\#0-9_\-\+\(\)]/', '', $phone ) ) ) > 0 )
if ( strlen( trim( preg_replace( '/^[6789]\d{9}$/', '', $phone ) ) ) > 0 )
return false;
return true;
}
But the validation is not happening. What am I missing?
Was facing the same issue and followed what others had said here, but Woocommerce only sets the errors on validation after woocommerce_checkout_process hook.
But, in the latest Woocommerce 3.0 (not sure if this is in the 2.x version), we can use the woocommerce_after_checkout_validation hook and then look into the $data param if you are using the standard checkout fields or use $_POST if you have custom fields that aren't added in the standard Woocommerce way. An example of the code is:
public function validate($data,$errors) {
// Do your data processing here and in case of an
// error add it to the errors array like:
$errors->add( 'validation', __( 'Please input that correctly.' ));
}
add_action('woocommerce_after_checkout_validation', 'validate',10,2);
Hope that helps!
I have not seen your code that hooks these up to the woocommerce checkout flow.
please check their documentation on
woocommerce_checkout_process and woocommerce_checkout_order_processed
But in your case, I highly suggest that you hook it up on woocommerce_checkout_process
so put these codes below on your functions.php on your theme, or you create your own woocommerce plugins, and put it in the bootstrap code.
add_action('woocommerce_checkout_process', 'is_phone');
function is_phone() {
$phone_number = $_POST['---your-phone-field-name---'];
// your function's body above, and if error, call this wc_add_notice
wc_add_notice( __( 'Your phone number is wrong.' ), 'error' );
}
You should not edit plugin files, because if you update plugin all the
customization will be lost, rather you can use hook to achieve your
goal. You can use using woocommerce_checkout_process hook to do
this.
Here is the code:
add_action('woocommerce_checkout_process', 'wh_phoneValidateCheckoutFields');
function wh_phoneValidateCheckoutFields() {
$billing_phone = filter_input(INPUT_POST, 'billing_phone');
if (strlen(trim(preg_replace('/^[6789]\d{9}$/', '', $billing_phone))) > 0) {
wc_add_notice(__('Invalid <strong>Phone Number</strong>, please check your input.'), 'error');
}
}
Code goes in functions.php file of your active child theme (or theme). Or also in any plugin PHP files.
Please Note: By default WooCommerce use billing_phone field to take phone number, but if you have customized it then you can replace billing_phone with your field name.
Hope this helps!
In your question you're saying that validation rule is not working and I guess it's written in a wrong way. You can test it in online with regexp tools, e.g. Regex101 or others.
To answer more general on this topic, changing validation rules safely can be done this way:
Make a copy of class-wc-validation.php to your theme directory in your_theme_path/woocommerce/includes/class-wc-validation.php and make customization to the validation rules.
Then you should make a validation rule for the phone filed in checkout.js otherwise your field always will have green border despite it's invalid.
So my solution was to add custom regular expression validator to checkout.js about line 192:
if ( $parent.is( '.validate-phone' ) ) {
if ( $this.val() ) {
var pattern = new RegExp(/^([0-9\s\/\+\-\#\_\(\)]*)$/);
if ( ! pattern.test( $this.val() ) ) {
$parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-phone' );
validated = false;
}
}
}
And include your customized .js file (in functions.php)
add_action( 'wp_enqueue_scripts', 'my_checkoutjs_enqueue_scripts', 100 );
function gv_checkoutjs_enqueue_scripts() {
if ( is_checkout() ) {
wp_deregister_script( 'wc-checkout' );
wp_enqueue_script( 'wc-checkout', get_template_directory_uri() . '/js/modified_checkout.js', array( 'jquery', 'woocommerce', 'wc-country-select', 'wc-address-i18n' ) );
}}
Hope this helps!
I have recently added s3 as a storage to my laravel application. I use signed url which works perfectly with uploaded images, but it does not with pdfs. I receive access denied, for the pdfs. If I make the files public via S3 console, I can receive it.
I am uploading these files with this mehtid:
Storage::disk('s3')->put();
I have tried signing the url with these two methods:
$url = Storage::disk('s3')->temporaryUrl(
$path, Carbon::now()->addMinutes(5)
);
$s3 = \Storage::disk('s3');
$client = $s3->getDriver()->getAdapter()->getClient();
$expiry = "+10 minutes";
$command = $client->getCommand('GetObject', [
'Bucket' => \Config::get('filesystems.disks.s3.bucket'),
'Key' => $path,
]);
$request = $client->createPresignedRequest($command, $expiry);
return (string) $request->getUri();
Any help would be appriciated!
I found out the solution.
The problem was, when I gave the url to pdf.js, it automatically changed "&" chars in the url to "&" , and s3 didnt recognize this url. I solved it using js string replace method
var pdfUrl = '{{\App\Models\DataHelper::getImgUrl($note->file)}}'; //getting the singed url
pdfUrl = pdfUrl.replaceAll("&", '&');
showPDF(pdfUrl); //pdf.js
I am allowing users to upload any kind of file on my page, but there might be a clash in names of files. So, I want to rename the file automatically, so that anytime any file gets uploaded, in the database and in the folder after upload, the name of the file gets changed also when other user downloads the same file, renamed file will get downloaded.
I tried:
if (Input::hasFile('file')){
echo "Uploaded</br>";
$file = Input::file('file');
$file ->move('uploads');
$fileName = Input::get('rename_to');
}
But, the name gets changed to something like:
php5DEB.php
phpCFEC.php
What can I do to maintain the file in the same type and format and just change its name?
I also want to know how can I show the recently uploaded file on the page and make other users download it??
For unique file Name saving
In 5.3 (best for me because use md5_file hashname in Illuminate\Http\UploadedFile):
public function saveFile(Request $request) {
$file = $request->file('your_input_name')->store('your_path','your_disk');
}
In 5.4 (use not unique Str::random(40) hashname in Illuminate\Http\UploadedFile). I Use this code to ensure unique name:
public function saveFile(Request $request) {
$md5Name = md5_file($request->file('your_input_name')->getRealPath());
$guessExtension = $request->file('your_input_name')->guessExtension();
$file = $request->file('your_input_name')->storeAs('your_path', $md5Name.'.'.$guessExtension ,'your_disk');
}
Use this one
$file->move($destinationPath, $fileName);
You can use php core function rename(oldname,newName) http://php.net/manual/en/function.rename.php
Find this tutorial helpful.
file uploads 101
Everything you need to know about file upload is there.
-- Edit --
I modified my answer as below after valuable input from #cpburnz and #Moinuddin Quadri. Thanks guys.
First your storage driver should look like this in /your-app/config/filesystems.php
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'), // hence /your-app/storage/app/public
'visibility' => 'public',
],
You can use other file drivers like s3 but for my example I'm working on local driver.
In your Controller you do the following.
$file = request()->file('file'); // Get the file from request
$yourModel->create([
'file' => $file->store('my_files', 'public'),
]);
Your file get uploaded to /your-app/storage/app/public/my_files/ and you can access the uploaded file like
asset('storage/'.$yourModel->image)
Make sure you do
php artisan storage:link
to generate a simlink in your /your-app/public/ that points to /your-app/storage/app/public so you could access your files publicly. More info on filesystem - the public disk.
By this approach you could persists the same file name as that is uploaded. And the great thing is Laravel generates an unique name for the file so there could be no duplicates.
To answer the second part of your question that is to show recently uploaded files, as you persist a reference for the file in the database, you could access them by your database record and make it ->orderBy('id', 'DESC');. You could use whatever your logic is and order by descending order.
You can rename your uploaded file as you want . you can use either move or storeAs method with appropiate param.
$destinationPath = 'uploads';
$file = $request->file('product_image');
foreach($file as $singleFile){
$original_name = strtolower(trim($singleFile->getClientOriginalName()));
$file_name = time().rand(100,999).$original_name;
// use one of following
// $singleFile->move($destinationPath,$file_name); public folder
// $singleFile->storeAs('product',$file_name); storage folder
$fileArray[] = $file_name;
}
print_r($fileArray);
correct usage.
$fileName = Input::get('rename_to');
Input::file('photo')->move($destinationPath, $fileName);
at the top after namespace
use Storage;
Just do something like this ....
// read files
$excel = $request->file('file');
// rename file
$excelName = time().$excel->getClientOriginalName();
// rename to anything
$excelName = substr($excelName, strpos($excelName, '.c'));
$excelName = 'Catss_NSE_'.date("M_D_Y_h:i_a_").$excelName;
$excel->move(public_path('equities'),$excelName);
This guy collect the extension only:
$excelName = substr($excelName, strpos($excelName, '.c'));
This guy rename its:
$excelName = 'Catss_NSE_'.date("M_D_Y_h:i_a_").$excelName;
if I print the content of an instance of UploadedFile, this is what I get
array (
'opt_image_header' =>
Symfony\Component\HttpFoundation\File\UploadedFile::__set_state(array(
'test' => false,
'originalName' => 'triangle-in-the-mountains.jpg',
'mimeType' => 'image/jpeg',
'size' => 463833,
'error' => 0,
)
And this is how I get the uploaded file in the Controller. Before of moving it, I should resize it.
foreach($request->files as $uploadedFile){
$ext = '.' . $uploadedFile['opt_image_header']->guessExtension();
$filename = sha1(uniqid(mt_rand(), true)) . $ext;
$uploadedFile['opt_image_header']->move($path . '/images/', $filename);
}
so there's no the "tmp_name" that I'd need for resizing the image before of saving it.
Do I need to read it directly from the $_FILE array?
Use $uploadedFile->getRealPath()
Symfony\Component\HttpFoundation\File\UploadedFile extends Symfony\Component\HttpFoundation\File\File, which extends PHP's SplFileInfo, so UploadedFile inherits all methods from SplFileInfo.
Use $uploadedFile->getRealPath() for the absolute path for the file. You can also use other methods on it, such as getFilename() or getPathname(). For a complete list of available methods (of SplFileInfo), see the docs.
Symfony's File class adds some extra methods, such as move() and getMimeType(), and adds backward compatibility for getExtension() (which was not available before PHP 5.3.6). UploadedFile adds some extra methods on top of that, such as getClientOriginalName() and getClientSize(), which provide the same information as you would normally get from $_FILES['name'] and $_FILES['size'].
If you are uploading a file with Doctrine, take a look at Symfony Documentation Upload a file
If you want to upload a file without Doctrine, you can try something like:
foreach($request->files as $uploadedFile) {
$filename = $uploadedFile->get('Put_Input_Name_Here')->getClientOriginalName();
$file = $uploadedFile->move($distination_path, $filename);
}
If there was any issue for uploading file move() will throw an exception
UPDATED
According to get the temp path of the uploaded file to resize the image you can use getPath() function in the mentioned loop
$tmp_name = $uploadedFile->get('Put_Input_Name_Here')->getPath();
If you ask why, because the Symfony File class is extends SplFileInfo
Here's my situation: I have a bunch of HTML pages in sites/default/files/pdf. I want to serve them as is so I have links to them in my Drupal site. However, one of the requirements is that all the URLs of these HTML pages must not contain any extensions. In addition, I want it to act in such a way that when users go to example.com/sites/default/files/pdf/somehtmlfile, the URL will show as example.com/pdf/somehtmlfile and also when users visit example.com/pdf/somehtmlfile, example.com/sites/default/files/pdf/somehtmlfile will be served instead.
From my independent research, it seems that I should be using hook_url_inbound_alter() and hook_url_outbound_alter(). However, I seem to be doing something wrong because the URL does not change at all.
Below is my code. I created a module called html_extension_remover (not very imaginative name, I know). I have successfully activated the module and some debugging statements are run successfully, so I know that the module is running.
function html_extension_remover_url_outbound_alter(&$path, &$options, $original_path){
$pdf_regex = '|^sites/default/files/pdf(/.*)?|';
$pdf_new_path = 'pdf';
if (preg_match($pdf_regex,$path, $matches)) //rewrite all request to sites/default/files/pdf to pdf, looks nicer
if (count($matches)==1)
$path = $pdf_new_path;
else
$path = $pdf_new_path . $matches[1]; //append the rest of the URL, after the Regex match
if (strpos($path, $pdf_new_path)!=FALSE) //URL contains pdf, means viewing converted PDFs in pdf dir
if (strpos($path, '.htm')!=FALSE){ //if viewing .htm/.html file
$path = substr(0, strpos); //strip extension from URL
}
$pdf_new_path = 'sites/default/files/pdf';
$pdf_regex = '|^pdf(/.*)?|';
if (preg_match($pdf_regex, $path, $matches)){
if (count($matches)==1){
$path = $pdf_new_path;
}
else{
$path = $pdf_new_path.$matches[1].'.htm';
}
}
}
function html_extension_remover_url_inbound_alter(&$path, &$options, $original_path){
$pdf_new_path = 'sites/default/files/pdf';
$pdf_regex = '|^pdf(/.*)?|';
if (preg_match($pdf_regex, $path, $matches)){
if (count($matches)==1){
$path = $pdf_new_path;
}
else{
$path = $pdf_new_path.$matches[1].'.htm';
}
}
}
If I understand you correctly URL rewriting is not what you need. Why? Because mapping an external URL to some internal URL / alias is not going to help you serve the file.
What you need is a way to have an external URL process a request and return the file in question. Luckily Drupal 7 makes this pretty easy to do.
1.) Define a menu mapping in hook_menu()
function MODULE_menu() {
$items = array();
$items['pdf'] = array(
'title' => 'Map PDF',
'page callback' => 'MODULE_show_pdf',
'access callback' => TRUE,
'description' => 'TBD',
'type' => MENU_CALLBACK,
);
return ($items);
}
2.) Define your callback function
function MODULE_show_pdf($somehtmlfile = '') {
$stream_wrapper_uri = 'public://pdf/' . $somehtmlfile . '.pdf';
$stream_wrapper = file_create_url($stream_wrapper_uri);
$stream_headers = array(
'Content-Type' => file_get_mimetype($stream_wrapper_uri),
'Content-Length' => filesize($stream_wrapper_uri),
'Pragma' => 'no-cache',
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
'Expires' => '0',
'Accept-Ranges' => 'bytes'
);
file_transfer($stream_wrapper_uri, $stream_headers);
}
Some things to note:
There is no need to explicitly define somehtmlfile param in the menu. This allows you more flexibility in that you can simply define whatever params you would like this external URL to support simply by adjusting the params in the callback function.
When public stream wrapper dirs/files are sub-dir of: sites/default/files
It is assumed that although you have somehtmlfile in the URL that you really want to stream somehtmlfile.pdf (if you want to stream somehtmlfile.html then simply adjust the hard coded '.pdf' suffix)
file_transfer calls drupal_exit() as its last step which essentially ends request processing.
Make sure to flush your cache otherwise the above will not work as menu entries are cached and the external URL will fail to be found