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
Related
I am trying to upload files to google drive using PHP code. I can't get API response whatever it is. Incase of successful upload response should be 200 and different error codes. I just want to get receive error codes. Can anyone help me with it. Bellow I have pasted my complete code. I am using function like check if folder already exists and if it does place file in it otherwise just create new folder and place file in it. I am trying to make my code fail safe, if an error is encountered it saves the particular file and error code. right now it is just returning critical error and breaks AJAX.
function getClient(){
$authdata = array (
'web' =>
array (
'client_id' => get_option("csc_gdrive_clientid"),
'project_id' => get_option("csc_gdrive_projectid"),
'auth_uri' => "https://accounts.google.com/o/oauth2/auth",
'token_uri' => "https://oauth2.googleapis.com/token",
'auth_provider_x509_cert_url' => "https://www.googleapis.com/oauth2/v1/certs",
'client_secret' => get_option("csc_gdrive_clientsecret"),
),
);
include( plugin_dir_path( __FILE__ ) . '../vendor/autoload.php');
$client = new Google_Client();
$client->setApplicationName('Google Drive API PHP Quickstart');
$client->setScopes(Google_Service_Drive::DRIVE);
$client->setAuthConfig($authdata);
$client->setAccessType('offline');
// $client->setPrompt('select_account consent');
$client->setRedirectUri(get_option("csc_gdrive_redirecturi"));
// Load previously authorized token from a file, if it exists.
// The file key_config.txt stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = plugin_dir_path( __FILE__ ) .'111key_config.txt';
if (!file_exists($tokenPath)) {
return;
} else{
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
if (file_exists($tokenPath)){
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
$authCode = get_option('csc_gdrive_token');
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
}
}
return $client;
}enter code here
function create_folder( $folder_name, $parent_folder_id=null ){
//$folder_list = $this->check_folder_exists( $folder_name );
// if folder does not exists
//if( count( $folder_list ) == 0 ){
$client = $this->getClient();
if($client){
$service = new Google_Service_Drive( $client );
$folder = new Google_Service_Drive_DriveFile();
$folder->setName( $folder_name );
$folder->setMimeType('application/vnd.google-apps.folder');
if( !empty( $parent_folder_id ) ){
$folder->setParents( [ $parent_folder_id ] );
}
$result = $service->files->create( $folder );
$folder_id = null;
if( isset( $result['id'] ) && !empty( $result['id'] ) ){
$folder_id = $result['id'];
}
return $folder_id;
}
//}
//return $folder_list[0]['id'];
}
// This will check folders and sub folders by name
function check_folder_exists( $folder_name, $parent_folder_id=null){
$client = $this->getClient();
if($client){
$service = new Google_Service_Drive( $client );
if ($parent_folder_id){
$parameters['q'] = "mimeType='application/vnd.google-apps.folder' and name='$folder_name' and trashed=false and '$parent_folder_id' in parents";
} else {
$parameters['q'] = "mimeType='application/vnd.google-apps.folder' and name='$folder_name' and trashed=false";
}
$files = $service->files->listFiles($parameters);
$op = [];
foreach( $files as $k => $file ){
$op[] = $file;
}
return $op;
}
}
// This will insert file into drive and returns boolean values.
function insert_file_to_drive( $file_path, $file_name, $parent_file_id = null ){
$client = $this->getClient();
if($client){
$service = new Google_Service_Drive( $client );
$file = new Google_Service_Drive_DriveFile();
$file->setName( $file_name );
if( !empty( $parent_file_id ) ){
$file->setParents( [ $parent_file_id ] );
}
$result = $service->files->create(
$file,
array(
'data' => file_get_contents($file_path.'/'.$file_name),
'mimeType' => 'application/octet-stream',
)
);
$is_success = false;
if( isset( $result['name'] ) && !empty( $result['name'] ) ){
$is_success = true;
}
return $is_success;
}
}
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.
I need to pass the gerenrated PHPExcel output to a function that will send it via Email. My current code is this:
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
$objWriter->save('php://output');
//Send Email here
$this->ciEngine->load->library( 'Services\Notifications\PushReports\Survey\Email' );
$emailResult = $this->ciEngine->Email->emailSurvey();
**UPDATE
I'm adding the emailSurvey's code for reference.
namespace Services\Notifications\PushReports\Survey;
use \Services\ServiceBase;
class Email
extends ServiceBase
{
public function emailSurvey( $countryConfig, $appointment )
{
if( empty( $appointment ) ) {
return false;
}
$this->ciEngine->load->library( 'email' );
$receipient = $this->ciEngine->config->config['push-report-receipient'];
$sender = $this->ciEngine->config->config['push-report-sender'];
if( !empty($receipient) && !empty($sender) ){
$this->ciEngine->email->initialize( $this->ciEngine->config->config );
$this->ciEngine->email->from( $sender );
$this->ciEngine->email->to( $receipient );
$this->ciEngine->email->subject( $this->ciEngine->config->config['push-report-subject'] );
ob_start();
$this->ciEngine->load->view( 'emails/surveyPush' );
$mailBody = ob_get_contents();
ob_end_clean();
$this->ciEngine->email->message( $mailBody );
return $this->ciEngine->email->send();
}
}
}
How could I pass it to the emailSurvey function?
I am writ plugin for WordPress. It sends a link to the images on the PHP file (Ajax).
The array consists of 10 photos.
obj.photos.push({ img_url: img_url, source: source, photo_text: photo_text, tag: tag });
var data_insert = {
action: 'instapin_insert',
data: obj
};
$.ajax({
type: 'POST',
url: '/wp-content/plugins/instapin/ajax.php',
data: data_insert,
});
Next, the file takes a picture and the script loads to the cloud with Google (Google Drive).
<?php
session_start();
session_write_close();
require_once('../../../wp-config.php');
global $service;
if(class_exists('INSTAPIN'))
{
$instapin = new INSTAPIN();
}
$google_drive = get_option('instapin_drive');
$photos_array = $_POST['data']['photos'];
// Создаём объект записи
$instapin_post = array(
'post_title' => 'Название записи',
'post_content' => '',
'post_status' => 'draft',
'post_author' => 1,
'post_category' => array(0)
);
// Вставляем запись в базу данных
$post_id = wp_insert_post( $instapin_post );
if($google_drive == 'on'){
$folder = get_option('google_upload_folder');
$found = false;
$files = $service->files->listFiles();
foreach ($files['items'] as $item) {
if ($item['title'] == $post_id) {
$folder_insert_photo = $item['id'];
$found = true;
break;
}
}
if($found == false){
$file = new Google_Service_Drive_DriveFile();
//Setup the Folder to Create
$file->setTitle($post_id);
$file->setDescription('Photo folder: post_id - ' . $post_id);
$file->setMimeType('application/vnd.google-apps.folder');
//Set the ProjectsFolder Parent
$parent = new Google_Service_Drive_ParentReference();
$parent->setId( $folder );
$file->setParents(array($parent));
//create the ProjectFolder in the Parent
$createdFile = $service->files->insert($file, array(
'mimeType' => 'application/vnd.google-apps.folder',
));
$folder_insert_photo = $createdFile->id;
$instapin->insertPermission($service, $folder_insert_photo, 'id', 'anyone', 'reader');
}
$count = 1;
$photo = new Google_Service_Drive_DriveFile();
$parent_folder = new Google_Service_Drive_ParentReference();
foreach($photos_array as $item){
if($count == 40){
break;
}
$img_url = strtok($item['img_url'], '?');
$image_url = str_replace("s150x150/", "", $img_url);
$image_data = file_get_contents($image_url); // Get image data
$filename = basename($image_url); // Create image file name
$wp_filetype = wp_check_filetype( $filename, null );
$MimeType = $wp_filetype['type'];
$seo_name = $item['tag'] . '_' . $count . '.jpg';
//Setup the Folder to Create
$photo->setTitle($seo_name);
$photo->setDescription('Photo: ' . $image_url);
$photo->setMimeType($MimeType);
$parent_folder->setId( $folder_insert_photo );
$photo->setParents(array($parent_folder));
// Try to upload the file, you can add the parameters e.g. if you want to convert a .doc to editable google format, add 'convert' = 'true'
$createdPhoto = $service->files->insert($photo, array(
'data' => $image_data,
'mimeType' => $MimeType,
'uploadType'=> 'multipart'
));
$instapin->insertPermission($service, $createdPhoto->id, 'id', 'anyone', 'reader');
$filePath = "GoogleDrive/{$folder_insert_photo}/{$seo_name}";
$fullFile = "https://googledrive.com/host/{$folder_insert_photo}/{$seo_name}";
print_r($fullFile);
$count++;
}
}
?>
But when I send an AJAX post to upload 10 photos, all admin panel and front. Nothing can be done and go until the download is complete (the script works).
But when I post to download 10 photos, all admin panel and front crashes (hangs). Nothing can be done and go until the upload is finished (the script works).
How to make this multi-tasking? So the admin panel and front continue to work, and the script is working in the background.
I have a very small script that uploads and / or update files (wrote this about 1 year ago, borrowed 80% of the lines from the examples)
No major changes in the code since march (using 1.0.0-alpha) but mid-may the file updates stopped working, raising an Internal Server Error , i upgraded to 1.0.4-beta with no success :
PHP Fatal error: Uncaught exception 'Google_Service_Exception' with message 'Error calling PUT https://www.googleapis.com/upload/drive/v2/ [...] (500) Internal Error' in
google-api-php-client-1.0.4-beta/src/Google/Http/REST.php:80
code:
$client->setDefer(true);
$request = $service->files->update($update_id,$file);
$media = new Google_Http_MediaFileUpload(
$client,
$request,
'text/plain',
null,
true,
$chunkSizeBytes
);
$media->setFileSize(filesize($csvfile))
$status = false;
$handle = fopen($csvfile, "rb");
while (!$status && !feof($handle)) {
$chunk = fread($handle, $chunkSizeBytes);
$status = $media->nextChunk($chunk);
}
File inserts (HTTP POST) are still working (using the same code for uploading the chunks)
any ideas ?
I can update a file ( previously created or copied from a template ) with this code :
(...)
require_once 'src/Google/Client.php';
require_once 'src/Google/Service/Oauth2.php';
require_once 'src/Google/Service/Drive.php';
(...)
$client = new Google_Client();
// set scopes ...
(...)
$GDrive_service = new Google_Service_Drive($client);
(...)
// then I set parameters
(...)
$newTitle = $title ;
$newDescription = $description ;
$newMimeType = 'text/csv' ;
$newFileName = $filename ;
$service = $GDrive_service ;
(...)
$updatedFile = updateFile($service, $fileId, $newTitle, $newDescription, $newMimeType, $newFileName, $newRevision) ;
function updateFile($service, $fileId, $newTitle, $newDescription, $newMimeType, $newFileName, $newRevision) {
try {
// First retrieve the file from the API.
$file = $service->files->get($fileId);
// File's new metadata.
$file->setTitle($newTitle);
$file->setDescription($newDescription);
$file->setMimeType($newMimeType);
// File's new content.
$data = file_get_contents($newFileName);
$convert = 'true' ;
$additionalParams = array(
'uploadType' => 'multipart',
'newRevision' => $newRevision,
'data' => $data,
'mimeType' => $newMimeType,
'convert' => $convert,
);
// Send the request to the API.
$updatedFile = $service->files->update($fileId, $file, $additionalParams);
return $updatedFile;
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
}
(...)
// this is info of the updated file
$fileId = $updatedFile->getId() ;
// link of file
$cF_link = $updatedFile->alternateLink ;
// pdf version
$enllacos = $updatedFile->exportLinks ;
$cF_PDF_link = $enllacos['application/pdf'] ;
(...)
This code is working with the 1.0.5-beta php client
Sergi