I am creating a plugin which makes an API call to a book keeping service and I am integrating it with WooCommerce.
After the order is created I am using woocommerce_email_order_details hook to execute my API call using $response = wp_remote_post( $post_url );, where $post_url is modified so that it all works with the API.
Within this response is also a pdf which I am saving to a folder in the uploads folder using WP_Filesystem() API. This part looks like this:
$response = wp_remote_post( $post_url );
if ( is_wp_error( $response ) ) {
$error_code = wp_remote_retrieve_response_code( $response );
$error_message = wp_remote_retrieve_response_message( $response );
return new WP_Error( $error_code, $error_message );
}
$body = json_decode( $response['body'] );
$pdf_link = esc_url( $body->order->pdf );
$pdf_name = esc_html( $body->order->order_number );
$pdf_get = wp_remote_get( $pdf_link );
if ( is_wp_error( $pdf_get ) ) {
$error_code = wp_remote_retrieve_response_code( $pdf_get );
$error_message = wp_remote_retrieve_response_message( $pdf_get );
return new WP_Error( $error_code, $error_message );
}
$pdf_contents = $pdf_get['body'];
$pdf_name = 'order-' . $pdf_name . '.pdf';
$upload_dir = wp_upload_dir();
$new_dir = $upload_dir['basedir'] . '/orders/' . date( 'Y' ) . '/' . date( 'm' );
if ( ! file_exists( $new_dir ) ) {
wp_mkdir_p( $new_dir );
}
global $wp_filesystem;
if ( empty( $wp_filesystem ) ) {
require_once( ABSPATH . '/wp-admin/includes/file.php' );
WP_Filesystem();
}
$wp_filesystem->put_contents(
$new_dir . '/' . $pdf_name,
$pdf_contents,
FS_CHMOD_FILE // predefined mode settings for WP files.
);
I've tested this with a live link on my local server and it all works.
One thing I don't think I can do is escape the contents of a pdf before saving it because that would break it. Even though this is something that the API of the book keeping service sends me, I don't want to assume anything and take it for granted.
What I am most interested in is: is this safe? Are there any possible vectors of attack here, because I wouldn't want to put my client at a risk.
Related
Can anyone help me add a pdf to a Xero Invoice using the official (non calcanai) PHP SDK. I'm connected with oAuth2 and have previously created a draft invoice.
I then update the invoice to Authorised and try to add an attachment to the invoice. At this point I am not getting any luck, the attachments is sent but comes back in the respose as null. The docs are not at all useful and do not give any examples of adding an attachment or even minimal fields. So this is what I have:
...
$attachments = $apiResponse->getInvoices()[0]->getAttachments();
$attachment = new XeroAPI\XeroPHP\Models\Accounting\Attachment;
$attachment->setFileName( $filename )
->setIncludeOnline( true )
->setMimeType( 'application/pdf' );
$attachments[] = $attachment;
$apiResponse->getInvoices()[0]->setAttachments( $attachments );
$apiResult = $accountingApi->updateOrCreateInvoices( $xeroTenantId, $apiAuth );
if ( !$apiResult->getInvoices()[0]->getHasAttachments() ) {
$errors[] = 'Pdf file was not added to Xero.';
}
$errorList = array();
$errList = $apiResult->getInvoices()[0]->getValidationErrors()[0];
if ( !is_null( $errList ) ) {
$errorList = $errList;
}
foreach ( $errorList as $err ) {
$errors[] = $err->getMessage();
}
if ( count( $errors ) == 0 ) {
$result['message'] = 'New Invoice Authorised: ' . $xeroInvoice->getReference();
$result['apiResult'] = $apiResult;
$result['success'] = true;
} else {
$result['message'] = print_r( $errors );
$result['success'] = false;
}
...
any ideas?
thanks
* CODE AFTER *
public function attachPDF( $xeroTenantId, $accountingApi, $invoice ) {
$filename = 'AUTH#' . sprintf( '%08d', $invoice->id ) . '.pdf';
$guid = $invoice->invoice_id;
$content = $invoice->getPDF( \Mpdf\Output\Destination::FILE, true, $filename );
$handle = fopen( $filename, "r" );
$contents = fread( $handle, filesize( $filename ) );
fclose( $handle );
unlink( $filename );
return $accountingApi->createInvoiceAttachmentByFileName( $xeroTenantId, $guid, $filename, $contents, true );
}
Adding an attachment requires two API calls.
// Create or Get your Invoice ID, then create Attachment
$invoices = $apiInstance->getInvoices($xeroTenantId);
$guid = $invoices->getInvoices()[0]->getInvoiceId();
// file in the same dir.
$filename = "./helo-heros.jpg";
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);
$result = $apiInstance->createInvoiceAttachmentByFileName($xeroTenantId,$guid,"helo-heros.jpg",$contents);
Anyone else who has this same issue it is due to the filename having a # in it (AUTH#00000123.pdf in my example) the updated code based on sidneys answer is also the way forward
I would like to exclude added email attachments for customer reset password and customer new account emails, or limit adding some of attachment to Woocommerce order emails only (and exclude attachments for emails send to admin). Is it possible?
add_filter( 'woocommerce_email_attachments', 'doc_to_email', 10, 3);
function doc_to_email ( $attachments , $id, $object ) {
$attachments = array();
array_push($attachments, get_home_path() . '/doc/Zasady_ochrany_osobnich_udaju.pdf' );
if( !$id == array( 'customer_reset_password', 'customer_new_account') ) {
array_push($attachments, get_home_path() . '/doc/VOP.pdf' );
array_push($attachments, get_home_path() . '/doc/Reklamacni_rad.pdf' );
array_push($attachments, get_home_path() . '/doc/Reklamacni_protokol.pdf' );
array_push($attachments, get_home_path() . '/doc/Formular_pro_odstoupeni_od_smlouvy.pdf' );
}
return $attachments;
}
Thanks you
The following code will exclude email attachements from all admin email notifications and some attachements from specific email notifications:
add_filter( 'woocommerce_email_attachments', 'custom_email_attachments', 20, 3 );
function custom_email_attachments ( $attachments = [] , $email_id, $order ) {
// HERE define customer and admin excluded email Ids
$excluded_customer_email_ids = array( 'customer_reset_password', 'customer_new_account' );
$excluded_admin_email_ids = array( 'new_order', 'cancelled_order', 'failed_order' );
// Excluding attachements from admin email notifications
if( in_array( $email_id, $excluded_admin_email_ids ) )
return [];
$file_path = get_home_path() . '/doc/';
$attachments[] = $file_path . 'Zasady_ochrany_osobnich_udaju.pdf';
// Excluding some customer email notifications
if( ! in_array( $email_id, $excluded_customer_email_ids ) ) {
$attachments[] = $file_path . 'VOP.pdf';
$attachments[] = $file_path . 'Reklamacni_rad.pdf';
$attachments[] = $file_path . 'Reklamacni_protokol.pdf';
$attachments[] = $file_path . 'Formular_pro_odstoupeni_od_smlouvy.pdf';
}
return $attachments;
}
Code goes in function.php file of your active child theme (or active theme). It should works.
i want change file upload name and add the name of user that upload it.
This my function.php
if ( ! function_exists( 'upload_user_file' ) ) :
function upload_user_file( $file = array(), $title = false ) {
require_once ABSPATH.'wp-admin/includes/admin.php';
$file_return = wp_handle_upload($file, array('test_form' => false));
if(isset($file_return['error']) || isset($file_return['upload_error_handler'])){
return false;
}else{
$user = wp_get_current_user();
$username = $user->display_lastname;
$filename = $username . $file_return['file'];
return $filename;
$attachment = array(
'post_mime_type' => $file_return['type'],
'post_content' => '',
'post_type' => 'attachment',
'post_status' => 'inherit',
'guid' => $file_return['url']
);
if($title){
$attachment['post_title'] = $title;
}
$attachment_id = wp_insert_attachment( $attachment, $filename );
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attachment_data = wp_generate_attachment_metadata( $attachment_id, $filename );
wp_update_attachment_metadata( $attachment_id, $attachment_data );
if( 0 < intval( $attachment_id ) ) {
return $attachment_id;
}
}
return false;
}
endif;
i try with $filename = $username . $file_return['file']; but don't work.
wp_handle_upload accepts an array of overrides, and one of the arguments is unique_filename_callback where you can specify a custom function to rename the file.
Try something like this:
1: Add a function to functions.php to rename the file as you want it, e.g.
function my_custom_filename($dir, $name, $ext){
$user = wp_get_current_user();
/* You wanted to add display_lastname, but its not required by WP so might not exist.
If it doesn't use their username instead: */
$username = $user->display_lastname;
if (!$username) $username = $user->user_login;
$newfilename = $username ."_". $name; /* prepend username to filename */
/* any other code you need to do, e.g. ensure the filename is unique, remove spaces from username etc */
return $newfilename;
}
2: Then in your upload_user_file(), specify your custom callback function in the wp_handle_upload overrides, e.g.
$overrides = array(
'test_form' => false, /* this was in your existing override array */
'unique_filename_callback' => 'my_custom_filename'
);
/* pass your overrides array into wp_handle_upload */
$file_return = wp_handle_upload($file,$overrides);
UPDATE:
To get the new filename in the upload_user_file function, you can get it from the "url" in the $file_return array that's returned by wp_handle_upload:
$newfilename = basename($file_return["url"]);
I would like to get the mp4 links of my own videos for my own website. So I've created a vimeo app and generated a personal access token with following scopes: private, public.
Now I tried to call the data of a video and this is the response: "invalid siganture". I dont't know what I'm doing wrong.
if ( ! defined( 'OPCT_VIMEO_CLIENT_ID' ) ) {
define( 'OPCT_VIMEO_CLIENT_ID', '1234' );
}
if ( ! defined( 'OPCT_VIMEO_CLIENT_SECRET' ) ) {
define( 'OPCT_VIMEO_CLIENT_SECRET', 'dfgdfgdfgE44rrfd/xsdfsdfsdGDFDFGdfgdfg/dfgdfgdfgdfgdf/' );
}
if ( ! defined( 'OPCT_VIMEO_ACCESS_TOKEN' ) ) {
define( 'OPCT_VIMEO_ACCESS_TOKEN', '1234' );
}
if ( ! class_exists( 'phpVimeo' ) ) {
include_once 'lib/vimeo.php';
}
try {
$vimeo = new phpVimeo( OPCT_VIMEO_CLIENT_ID, OPCT_VIMEO_CLIENT_SECRET );
$response = $vimeo->call( 'videos', array( 'video_id', '1234567890' ) );
$this->log( $response );
} catch ( Exception $e ) {
$this->log( 'Vimeo Error API Call: ' . $e->getMessage() );
}
The code you include is part of the old Vimeo library, which uses the old Vimeo API.
The old Vimeo API does not expose source files, so you will need to use the new library as found here: https://github.com/vimeo/vimeo.php
The code will look similar to the following in the new library (Note I have not tested this)
if ( ! defined( 'OPCT_VIMEO_CLIENT_ID' ) ) {
define( 'OPCT_VIMEO_CLIENT_ID', '1234' );
}
if ( ! defined( 'OPCT_VIMEO_CLIENT_SECRET' ) ) {
define( 'OPCT_VIMEO_CLIENT_SECRET', 'dfgdfgdfgE44rrfd/xsdfsdfsdGDFDFGdfgdfg/dfgdfgdfgdfgdf/' );
}
if ( ! defined( 'OPCT_VIMEO_ACCESS_TOKEN' ) ) {
define( 'OPCT_VIMEO_ACCESS_TOKEN', '1234' );
}
if ( ! class_exists( 'phpVimeo' ) ) {
include_once 'lib/vimeo.php';
}
try {
$vimeo = new Vimeo\Vimeo( OPCT_VIMEO_CLIENT_ID, OPCT_VIMEO_CLIENT_SECRET );
$response = $vimeo->request( '/videos/' . $video_id);
$this->log( $response );
} catch ( Exception $e ) {
$this->log( 'Vimeo Error API Call: ' . $e->getMessage() );
}
You will then find the video files in the $response variable.
I am trying to receive files from a webservice and save them into wordpress but I have following issues,
Some of the files will be uploaded but they are corrupted and I can't open them, although their webservice address is correct.
It shows the following errors (although it shows these errors but upload the files anyway, which is good :D but I am wondering why I am receiving them).
Notice: Constant ABSPATH already defined in /Applications/MAMP/htdocs/wordpress/wp-load.php on line 22
Fatal error: Call to undefined function wp_generate_attachment_metadata() in /Applications/MAMP/htdocs/wordpress/wp-content/plugins/myproject/Myclasses/retrieve_2334.php on line 95
My Code:
ini_set( 'display_errors', TRUE );
error_reporting( E_ALL );
require("/Applications/MAMP/htdocs/wordpress/wp-load.php");
$pos = strrpos($Address, "&f=");
$loc = substr($Address, $pos + 3);
$Address = "http://dev.piction.com/v60/" . $Address;
$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, 0);
curl_setopt($ch, CURLOPT_URL, $Address);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec($ch);
curl_close($ch);
$upload_dir = wp_upload_dir();
$location= $upload_dir['basedir']. "/" . $loc;
try{
$location= $upload_dir['path']. "/" . $loc;
$wp_filetype = wp_check_filetype($loc, null );
$fullpathfilename = $upload_dir['path'] . "/" . $loc;
$fileSaved = file_put_contents($location, $output);
if ( !$fileSaved ) {
throw new Exception("The file cannot be saved.");
}
$attachment = array(
'post_mime_type' => $wp_filetype['type'],
'post_title' => preg_replace('/\.[^.]+$/', '', $loc),
'post_content' => '',
'post_status' => 'inherit',
'guid' => $upload_dir['url'] . "/" . $loc
);
$attach_id = wp_insert_attachment( $attachment, $fullpathfilename, 0 );
if ( !$attach_id ) {
throw new Exception("Failed to save record into database.");
}
//require_once(ABSPATH . "wp-admin" . '/includes/image.php');
$attach_data = wp_generate_attachment_metadata( $attach_id, $fullpathfilename );
wp_update_attachment_metadata( $attach_id, $attach_data );
} catch (Exception $e) {
$error = '<div id="message" class="error"><p>' . $e->getMessage() . '</p></div>';
}
echo 'Photo is uploaded';
It does seem as if you're including a file twice, that would explain why you're getting the "already defined" errors.
Try removing the require() statement in the third line of your code and see if it works, as it seems as if that file is already being included somewhere else in your script.
Try changing to require_once("/Applications/MAMP/htdocs/wordpress/wp-load.php"); as you get the already defined notice it might indicate you have already included wp-load.php.
Also you need to uncomment the line //require ( ABSPATH . 'wp-admin/includes/image.php' ); to be able to use wp_generate_attachment_metadata()