We are using Woocommerce for our ecom website and in order to automatically generate government-approved invoices for customers we use a certified online invoicing software.
I am making an API request to this invoicing software in order to retrieve the generated invoice document from their database, this is the code:
// On Order complete > Get document ID from order > access Moloni invoicing API > get document link GETPDFLINK > Sanitize the string and get Hash > generate the final document link > access it and download the PDF
function download_moloni_document_id( $order_id, $order ) {
// Retreive from the Database table moloni_api the access token from column main_token
global $wpdb;
$table_name = "db_invoicing_api";
$retrieve_data = $wpdb->get_results( "SELECT * FROM $table_name WHERE id = 1" );
foreach ($retrieve_data as $retrieved_data) {
$maintoken = $retrieved_data->main_token;
}
// Get document ID from the order
$documentid = get_post_meta($order->id, '_moloni_sent', true);
// Connect to moloni API and getpdflink
$url = "https://api.moloni.pt/v1/documents/getPDFLink/?access_token=$maintoken";
$postData = array(
'company_id' => '12345',
'document_id' => $documentid );
$arguments = array(
'method' => 'POST',
'headers' => array(
'Content-type: application/x-www-form-urlencoded'
),
'body' => $postData,
);
$response = wp_remote_post( $url, $arguments );
if ( is_wp_error( $response ) ) {
$error_message = $response->get_error_message();
return "Something went wrong: $error_message";
} else {
echo '<pre>';
var_dump( wp_remote_retrieve_body( $response ) );
echo '</pre>';
// jsondecode the string received by the API to remove backslashes, get and parse the URL and extract the HASH key
$response2 = wp_remote_retrieve_body($response);
parse_str(parse_url(json_decode($response2, true)['url'], PHP_URL_QUERY), $result);
$hash = $result['h'];
// Assemble the Invoice HTML download URL with the Hash and document ID
$fileUrlpdf = "https://www.moloni.pt/downloads/index.php?action=getDownload&h=$hash&d=$documentid&e=$loginmail&i=1&t=n";
$pdforderid = $order->id;
// Save the file with document id name to server location
$saveTopdf = "ftp://myserver/INVOICES/evo-$pdforderid.PDF";
file_put_contents(
$saveTopdf,
file_get_contents($fileUrlpdf)
);
} }
add_action( 'woocommerce_order_status_completed', 'download_moloni_document_id', 20, 2 );
In the end you can see I use
file_put_contents(
$saveTopdf,
file_get_contents($fileUrlpdf) );
in order to visit the link, retrieve the PDF and download it.
This works well then the link is generated successfully and sends to a direct download of the invoice PDF
The problem I have is is, sometimes there is an issue and the invoice PDF is not generated, this makes it so that the final link $fileUrlpdf does not lead to a download but instead to a web page with an error message saying something like "No Documents Found" which leads this code to download a PDF of dozens of pages containing the source code/HTML of that page. This is a problem because the PDFs (invoices) are later automatically printed by our system, so we sometimes end up with hundreds of pages of HTML code instead of the invoices.
I have tried to solve this in the following way through conditions:
to check if the download/PDF file exists
if (file_exists($fileUrlpdf)) {
file_put_contents(
$saveTopdf,
file_get_contents($fileUrlpdf)); }
one that would check the $response array for the error message and not proceed (because
$invalid = 'No Documents Found'; if (strpos($response, $valid) !== false) {echo 'No Documents found'; else { *download the PDF* } }
I have also considered the possibility of
checking if the page $fileUrlpdf would contain "No Documents Found", to not download it but I haven't been able to figure this one out either.
Bear with me as you can see my experience with PHP is limited so I would like to ask, what would be the best practice here? What approach would you suggest?
Thank you very much in advance for the attention and advice.
Either look at using the API to fetch the invoice/document and handle errors that way moloni.pt/dev
Or you could look at checking the Content-Type of the response headers as shown below
get_headers
$fileUrlpdf = "https://www.moloni.pt/downloads/index.php?action=getDownload&h=$hash&d=$documentid&e=$loginmail&i=1&t=n";
$fileHeaders = get_headers($fileUrlpdf, true)
if($fileHeaders['Content-Type'] === 'application/pdf') {
// PDF response
}
I am trying to tweet an image using php. I have read the documentation and followed a some tutorials. I don't have any problem when sending a message; however, it does not work with images. I can not find where my mistake is, can anyone help?? I would deeply appreciate it.
<?php
$comments = $_POST['comment'];
if (isset($_POST['Twitter']))
{
require_once('twitter/codebird-php/src/codebird.php');
\Codebird\Codebird::setConsumerKey("xxxxxx","xxxxxx");
$cb = \Codebird\Codebird::getInstance();
$cb->setToken("xxxxxx", "xxxxxxxxxx");
$params = array(
'status' => $comments,
'media[]' => "/images/image1.jpg"
);
$reply = $cb->statuses_update($params);
echo "You have posted your message succesfully";
}
else{
echo "You havent posted anythingh";
}
?>
You need to supply Twitter the fully qualified URI to your image.
You have the following code:
$params = array(
'status' => $comments,
'media[]' => "/images/image1.jpg"
);
Unfortunately, that's not a complete URL to an image, it's a relative URL. Instead, you need to provide something along the lines of this:
$params = array(
'status' => $comments,
'media[]' => "http://www.example.com/images/image1.jpg"
);
In addition, according to the Twitter API v1 documentation, you need to use statuses/update_with_media instead of statuses/update.
If you are using Twitter API v1, Twitter also recommends using v1.1 instead.
Hi I'm making a web service in cakephp for an android app. I am getting the request and the respose is being send but the response is not visible on the client's end. My code is as shown below. Can there be some other method to send the response.
public function AndroidApp() {
if (isset($_POST["myHttpData"])) {
$coupon = trim($_POST["myHttpData"]);
$couponId = $this->Code->find('all', array(
'conditions' => array(
'Code.coupon_code' => $coupon,
'Code.status' => 'Used'
),
'fields' => array('Code.id')));
$studentAssessmentId = $this->StudentAssessment->find('all', array(
'conditions' => array(
'StudentAssessment.code_id' => $couponId[0]['Code']['id'],
'StudentAssessment.status' => 'Complete'
),
'fields' => array('StudentAssessment.id')));
$scores = $this->AssessmentScore->find('all', array(
'conditions' => array(
'AssessmentScore.student_assessment_id' => $studentAssessmentId[0]['StudentAssessment']['id']
),
'fields' => array('AssessmentScore.score')));
$json = array();
$assessment_data = array();
//debug($scores);
$i = 0;
foreach ($scores as $score) {
$assessment_data[$i] = array("score" => $score['AssessmentScore']['score']);
$i+=1;
}
header('Content-type: application/json');
$json['success'] = $assessment_data;
$android = json_encode($json);
} else {
$json['error'] = "Sorry, no score is available for this coupon code!";
$android = json_encode($json);
}
echo $android;
code smell, non-cakephp standards
First of all, as mentioned in comments by others, you're not using the CakePHP request/response objects. Because of this, you're overly complicating things. See the documentation here;
http://book.cakephp.org/2.0/en/controllers/request-response.html
http://book.cakephp.org/2.0/en/controllers/request-response.html#dealing-with-content-types
And
http://book.cakephp.org/2.0/en/views/json-and-xml-views.html
The $scores loop to reformat the query results is probably redundant if you replace the find('all') with find('list'), using 'score' as display field. See the documentation here http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#find-list
bugs
There also seems to be some bugs in your code;
the content-type header is only sent if $_POST["myHttpData"] is present.
you're only checking if $_POST["myHttpData"] is present, not if it actually contains any data (empty)
you're not checking if the various queries return a result. This will cause errors in your code if a query did not return anything! For example, you assume that $couponId[0]['Code']['id'] is present (but it won't be if the coupon-code was not found)
possible answer
Apart from these issues, the most probable cause for your problem is that you did not disable 'autoRender'. Therefore CakePHP will also render the view after you've output your JSON, causing a malformed JSON response.
public function AndroidApp() {
$this->autoRender = false;
// rest of your code here
}
Anyone knows how to create new post with photo attached in WordPress using XMLRPC?
I am able to create new post and upload new picture separately, but looks like there is no way to attach the uploaded photo to the created post?
Below is the codes I'm currently using.
<?php
DEFINE('WP_XMLRPC_URL', 'http://www.blog.com/xmlrpc.php');
DEFINE('WP_USERNAME', 'username');
DEFINE('WP_PASSWORD', 'password');
require_once("./IXR_Library.php");
$rpc = new IXR_Client(WP_XMLRPC_URL);
$status = $rpc->query("system.listMethods"); // method name
if(!$status){
print "Error (".$rpc->getErrorCode().") : ";
print $rpc->getErrorMessage()."\n";
exit;
}
$content['post_type'] = 'post'; // post title
$content['title'] = 'Post Title '.date("F j, Y, g:i a"); // post title
$content['categories'] = array($response[1]['categoryName']); // psot categories
$content['description'] = '<p>Hello World!</p>'; // post body
$content['mt_keywords'] = 'tag keyword 1, tag keyword 2, tag keyword 3'; // post tags
$content['mt_allow_comments'] = 1; // allow comments
$content['mt_allow_pings'] = 1; // allow pings
$content['custom_fields'] = array(array('key'=>'Key Name', 'value'=>'Value One')); // custom fields
$publishBool = true;
if(!$rpc->query('metaWeblog.newPost', '', WP_USERNAME, WP_PASSWORD, $content, $publishBool)){
die('An error occurred - '.$rpc->getErrorCode().":".$rpc->getErrorMessage());
}
$postID = $rpc->getResponse();
echo 'POST ID: '.$postID.'<br/>';
if($postID){ // if post has successfully created
$fs = filesize(dirname(__FILE__).'/image.jpg');
$file = fopen(dirname(__FILE__).'/image.jpg', 'rb');
$filedata = fread($file, $fs);
fclose($file);
$data = array(
'name' => 'image.jpg',
'type' => 'image/jpg',
'bits' => new IXR_Base64($filedata),
false // overwrite
);
$status = $rpc->query(
'metaWeblog.newMediaObject',
$postID,
WP_USERNAME,
WP_PASSWORD,
$data
);
echo print_r($rpc->getResponse()); // Array ( [file] => image.jpg [url] => http://www.blog.com/wp-content/uploads/2011/09/image.jpg [type] => image/jpg )
}
?>
I've been involved in WordPress sites (my current employer uses 3 of these) and posting stuff daily and by the bulk has forced me to use what I do best-- scripts!
They're PHP-based and are quick and easy to use and deploy. And security? Just use .htaccess to secure it.
As per research, XMLRPC when it comes to files is one thing wordpress really sucks at. Once you upload a file, you can't associate that attachment to a particular post! I know, it's annoying.
So I decided to figure it out for myself. It took me a week to sort it out. You will need 100% control over your publishing client that is XMLRPC compliant or this won't mean anything to you!
You will need, from your WordPress installation:
class-IXR.php, located in /wp-admin/includes
class-wp-xmlrpc-server.php, located in /wp-includes
class-IXR.php will be needed if you craft your own posting tool, like me. They have the correctly-working base64 encoder. Don't trust the one that comes with PHP.
You also need to be somewhat experienced in programming to be able to relate to this. I will try to be clearer.
Modify class-wp-xmlrpc-server.php
Download this to your computer, through ftp. Backup a copy, just in case.
Open the file in a text editor. If it doesn't come formatted, (typically it should, else, it's unix-type carriage breaks they are using) open it elsewhere or use something like ultraedit.
Pay attention to the mw_newMediaObject function. This is our target. A little note here; WordPress borrows functionality from blogger and movabletype. Although WordPress also has a unique class sets for xmlrpc, they choose to keep functionality common so that they work no matter what platform is in use.
Look for the function mw_newMediaObject($args). Typically, this should be in line 2948. Pay attention to your text editor's status bar to find what line number you are in. If you can't find it still, look for it using the search/find function of your text editor.
Scroll down a little and you should have something that looks like this:
$name = sanitize_file_name( $data['name'] );
$type = $data['type'];
$bits = $data['bits'];
After the $name variable, we will add something. See below.
$name = sanitize_file_name( $data['name'] );
$post = $data['post']; //the post ID to attach to.
$type = $data['type'];
$bits = $data['bits'];
Note the new $post variable. This means whenever you will make a new file upload request, a 'post' argument will now be available for you to attach.
How to find your post number depends on how you add posts with an xmlrpc-compliant client. Typically, you should obtain this as a result from posting. It is a numeric value.
Once you've edited the above, it's time to move on to line 3000.
// Construct the attachment array
// attach to post_id 0
$post_id = 0;
$attachment = array(
'post_title' => $name,
'post_content' => '',
'post_type' => 'attachment',
'post_parent' => $post_id,
'post_mime_type' => $type,
'guid' => $upload[ 'url' ]
);
So here's why no image is associated to any post! It is always defaulted to 0 for the post_parent argument!
That's not gonna be the case anymore.
// Construct the attachment array
// attach to post_id 0
$post_id = $post;
$attachment = array(
'post_title' => $name,
'post_content' => '',
'post_type' => 'attachment',
'post_parent' => $post_id,
'post_mime_type' => $type,
'guid' => $upload[ 'url' ]
);
$post_id now takes up the value of $post, which comes from the xmlrpc request. Once this is committed to the attachment, it will be associated to whatever post you desire!
This can be improved. A default value can be assigned so things don't get broken if no value is entered. Although in my side, I put the default value on my client, and no one else is accessing the XMLRPC interface but me.
With the changes done, save your file and re-upload it in the same path where you found it. Again, make sure to make backups.
Be wary of WordPress updates that affects this module. If that happens, you need to reapply this edit again!
Include class-IXR.php in your PHP-type editor. If you're using something else, well, I can't help you there. :(
Hope this helps some people.
When you post, WordPress will scan at the post for IMG tags.
If WP finds the image, it's loaded in it's media library. If there's an image in the body, it will automatically attached it to the post.
Basically you have to:
post the media (image) first
Grab its URL
include the URL of the image with a IMG tag in the body of your post.
then create the post
Here is some sample code. It needs error handling, and some more documentation.
$admin ="***";
$userid ="****";
$xmlrpc = 'http://localhost/web/blog/xmlrpc.php';
include '../blog/wp-includes/class-IXR.php';
$client = new IXR_Client($xmlrpc);
$author = "test";
$title = "Test Posting";
$categories = "chess,coolbeans";
$body = "This is only a test disregard </br>";
$tempImagesfolder = "tempImages";
$img = "1338494719chessBoard.jpg";
$attachImage = uploadImage($tempImagesfolder,$img);
$body .= "<img src='$attachImage' width='256' height='256' /></a>";
createPost($title,$body,$categories,$author);
/*
*/
function createPost($title,$body,$categories,$author){
global $username, $password,$client;
$authorID = findAuthor($author); //lookup id of author
/*$categories is a list seperated by ,*/
$cats = preg_split('/,/', $categories, -1, PREG_SPLIT_NO_EMPTY);
foreach ($cats as $key => $data){
createCategory($data,"","");
}
//$time = time();
//$time += 86400;
$data = array(
'title' => $title,
'description' => $body,
'dateCreated' => (new IXR_Date(time())),
//'dateCreated' => (new IXR_Date($time)), //publish in the future
'mt_allow_comments' => 0, // 1 to allow comments
'mt_allow_pings' => 0,// 1 to allow trackbacks
'categories' => $cats,
'wp_author_id' => $authorID //id of the author if set
);
$published = 0; // 0 - draft, 1 - published
$res = $client->query('metaWeblog.newPost', '', $username, $password, $data, $published);
}
/*
*/
function uploadImage($tempImagesfolder,$img){
global $username, $password,$client;
$filename = $tempImagesfolder ."/" . $img;
$fs = filesize($filename);
$file = fopen($filename, 'rb');
$filedata = fread($file, $fs);
fclose($file);
$data = array(
'name' => $img,
'type' => 'image/jpg',
'bits' => new IXR_Base64($filedata),
false //overwrite
);
$res = $client->query('wp.uploadFile',1,$username, $password,$data);
$returnInfo = $client->getResponse();
return $returnInfo['url']; //return the url of the posted Image
}
/*
*/
function findAuthor($author){
global $username, $password,$client;
$client->query('wp.getAuthors ', 0, $username, $password);
$authors = $client->getResponse();
foreach ($authors as $key => $data){
// echo $authors[$key]['user_login'] . $authors[$key]['user_id'] ."</br>";
if($authors[$key]['user_login'] == $author){
return $authors[$key]['user_id'];
}
}
return "not found";
}
/*
*/
function createCategory($catName,$catSlug,$catDescription){
global $username, $password,$client;
$res = $client->query('wp.newCategory', '', $username, $password,
array(
'name' => $catName,
'slug' => $catSlug,
'parent_id' => 0,
'description' => $catDescription
)
);
}
After calling the method metaWeblog.newMediaObject, we need to edit the image entry on the database to add a parent (the previously created post with metaWeblog.newPost).
If we try with metaWeblog.editPost, it throws an error 401, which indicates that
// Use wp.editPost to edit post types other than post and page.
if ( ! in_array( $postdata[ 'post_type' ], array( 'post', 'page' ) ) )
return new IXR_Error( 401, __( 'Invalid post type' ) );
The solution is to call wp.editPost, which takes the following arguments:
$blog_id = (int) $args[0];
$username = $args[1];
$password = $args[2];
$post_id = (int) $args[3];
$content_struct = $args[4];
So, just after newMediaObject, we do:
$status = $rpc->query(
'metaWeblog.newMediaObject',
$postID,
WP_USERNAME,
WP_PASSWORD,
$data
);
$response = $rpc->getResponse();
if( isset($response['id']) ) {
// ATTACH IMAGE TO POST
$image['post_parent'] = $postID;
if( !$rpc->query('wp.editPost', '1', WP_USERNAME, WP_PASSWORD, $response['id'], $image)) {
die( 'An error occurred - ' . $rpc->getErrorCode() . ":" . $rpc->getErrorMessage() );
}
echo 'image: ' . $rpc->getResponse();
// SET FEATURED IMAGE
$updatePost['custom_fields'] = array( array( 'key' => '_thumbnail_id', 'value' => $response['id'] ) );
if( !$rpc->query( 'metaWeblog.editPost', $postID, WP_USERNAME, WP_PASSWORD, $updatePost, $publishBool ) ) {
die( 'An error occurred - ' . $rpc->getErrorCode() . ":" . $rpc->getErrorMessage() );
}
echo 'update: ' . $rpc->getResponse();
}
I've used the Incutio XML-RPC Library for PHP to test and the rest of the code is exactly as in the question.
Here's some sample code to attach an image from a path not supported by WordPress (wp-content)
<?php
function attach_wordpress_images($productpicture,$newid)
{
include('../../../../wp-load.php');
$upload_dir = wp_upload_dir();
$dirr = $upload_dir['path'].'/';
$filename = $dirr . $productpicture;
# print "the path is : $filename \n";
# print "Filnamn: $filename \n";
$uploads = wp_upload_dir(); // Array of key => value pairs
# echo $uploads['basedir'] . '<br />';
$productpicture = str_replace('/uploads','',$productpicture);
$localfile = $uploads['basedir'] .'/' .$productpicture;
# echo "Local path = $localfile \n";
if (!file_exists($filename))
{
echo "hittade inte $filename !";
die ("no image for flaska $id $newid !");
}
if (!copy($filename, $localfile))
{
wp_delete_post($newid);
echo "Failed to copy the file $filename to $localfile ";
die("Failed to copy the file $filename to $localfile ");
}
$wp_filetype = wp_check_filetype(basename($localfile), null );
$attachment = array(
'post_mime_type' => $wp_filetype['type'],
'post_title' => preg_replace('/\.[^.]+$/', '', basename($localfile)),
'post_content' => '',
'post_status' => 'inherit'
);
$attach_id = wp_insert_attachment( $attachment, $localfile, $newid );
// you must first include the image.php file
// for the function wp_generate_attachment_metadata() to work
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attach_data = wp_generate_attachment_metadata( $attach_id, $localfile );
wp_update_attachment_metadata( $attach_id, $attach_data );
}
?>
I had to do this several months ago. It is possible but not only is it hacky and undocumented I had to dig through wordpress source to figure it out. What I wrote up way back then:
One thing that was absolutely un-documented was a method to attach an image to a post. After some digging I found attach_uploads() which is a function that wordpress calls every time a post is created or edited over xml-rpc. What it does is search through the list of un-attached media objects and see if the new/edited post contains a link to them. Since I was trying to attach images so that the theme’s gallery would use them I didn’t necessarily want to link to the images within the post, nor did I want to edit wordpress. So what I ended up doing was including the image url within an html comment. -- danieru.com
Like I said messy but I searched high and low for a better method and I'm reasonably sure that none exists.
As of Wordpress 3.5, newmediaobject now recognizes the hack semi-natively.
it is no longer necessary to hack class-wp-xmlrpc-server.php.
Instead, your xml-rpc client needs to send the post number to a variable called post_id. (Previously it was just the variable 'post')
Hope that helps someone out.