DB query in Wordpress snippet not working [duplicate] - php

I know the path of the file and I like to get the attachment ID.
There's a function wp_get_attachment_url() which requires the ID to get the URL but I need it reverse (with path not URL though)

UPDATE: since wp 4.0.0 there's a new function that could do the job. I didn't tested it yet, but it's this:
https://developer.wordpress.org/reference/functions/attachment_url_to_postid/
OLD ANSWER: so far, the best solution I've found out there, is the following:
https://frankiejarrett.com/2013/05/get-an-attachment-id-by-url-in-wordpress/
I think It's the best for 2 reasons:
It does some integrity checks
[important!] it's domain-agnostic. This makes for safe site moving. To me, this is a key feature.

I used this cool snipped by pippinsplugins.com
Add this function in your functions.php file
// retrieves the attachment ID from the file URL
function pippin_get_image_id($image_url) {
global $wpdb;
$attachment = $wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE guid='%s';", $image_url ));
return $attachment[0];
}
Then use this code in your page or template to store / print / use the ID:
// set the image url
$image_url = 'http://yoursite.com/wp-content/uploads/2011/02/14/image_name.jpg';
// store the image ID in a var
$image_id = pippin_get_image_id($image_url);
// print the id
echo $image_id;
Original post here: https://pippinsplugins.com/retrieve-attachment-id-from-image-url/
Hope ti helps ;)
Francesco

Try attachment_url_to_postid function.
$rm_image_id = attachment_url_to_postid( 'http://example.com/wp-content/uploads/2016/05/castle-old.jpg' );
echo $rm_image_id;
More details

None of the other answers here appear to work properly or reliably for a file path. The answer using Pippin's function also is flawed, and doesn't really do things "the WordPress Way".
This function will support either a path OR a url, and relies on the built-in WordPress function attachment_url_to_postid to do the final processing properly:
/**
* Find the post ID for a file PATH or URL
*
* #param string $path
*
* #return int
*/
function find_post_id_from_path( $path ) {
// detect if is a media resize, and strip resize portion of file name
if ( preg_match( '/(-\d{1,4}x\d{1,4})\.(jpg|jpeg|png|gif)$/i', $path, $matches ) ) {
$path = str_ireplace( $matches[1], '', $path );
}
// process and include the year / month folders so WP function below finds properly
if ( preg_match( '/uploads\/(\d{1,4}\/)?(\d{1,2}\/)?(.+)$/i', $path, $matches ) ) {
unset( $matches[0] );
$path = implode( '', $matches );
}
// at this point, $path contains the year/month/file name (without resize info)
// call WP native function to find post ID properly
return attachment_url_to_postid( $path );
}

Cropped URLs
None of the previous answers supported ID lookup on attachment URLs that contain a crop.
e.g: /uploads/2018/02/my-image-300x250.jpg v.s. /uploads/2018/02/my-image.jpg
Solution
Micah at WP Scholar wrote a blog post and uploaded the code to this Gist. It handles both original and cropped URL lookup.
I included the code below as a reference but, if you find useful, I'd encourage you to leave a comment on his post or star the gist.
/**
* Get an attachment ID given a URL.
*
* #param string $url
*
* #return int Attachment ID on success, 0 on failure
*/
function get_attachment_id( $url ) {
$attachment_id = 0;
$dir = wp_upload_dir();
if ( false !== strpos( $url, $dir['baseurl'] . '/' ) ) { // Is URL in uploads directory?
$file = basename( $url );
$query_args = array(
'post_type' => 'attachment',
'post_status' => 'inherit',
'fields' => 'ids',
'meta_query' => array(
array(
'value' => $file,
'compare' => 'LIKE',
'key' => '_wp_attachment_metadata',
),
)
);
$query = new WP_Query( $query_args );
if ( $query->have_posts() ) {
foreach ( $query->posts as $post_id ) {
$meta = wp_get_attachment_metadata( $post_id );
$original_file = basename( $meta['file'] );
$cropped_image_files = wp_list_pluck( $meta['sizes'], 'file' );
if ( $original_file === $file || in_array( $file, $cropped_image_files ) ) {
$attachment_id = $post_id;
break;
}
}
}
}
return $attachment_id;
}
Another pro with this solution is that we leverage the WP_Query class instead of making a direct SQL query to DB.

Find IDs for resized images, PDFs and more
Like GFargo pointed out, most of the answers assume the attachment is an image. Also attachment_url_to_postid assumes a url (not a file path).
I believe this better answers the actual question when supplied a file (with path):
function getAttachmentIDFromFile($filepath)
{
$file = basename($filepath);
$query_args = array(
'post_status' => 'any',
'post_type' => 'attachment',
'fields' => 'ids',
'meta_query' => array(
array(
'value' => $file,
'compare' => 'LIKE',
),
)
);
$query = new WP_Query($query_args);
if ($query->have_posts()) {
return $query->posts[0]; //assume the first is correct; or process further if you need
}
return 0;
}

Based on the answer from #FrancescoCarlucci I could do some improvements.
Sometimes, for example when you edit an image in WordPress, it creates a copy from the original and adds the copys upload path as post meta (key _wp_attached_file) which is not respected by the answer.
Here the refined query that includes these edits:
function jfw_get_image_id($file_url) {
$file_path = ltrim(str_replace(wp_upload_dir()['baseurl'], '', $file_url), '/');
global $wpdb;
$statement = $wpdb->prepare("SELECT `ID` FROM `wp_posts` AS posts JOIN `wp_postmeta` AS meta on meta.`post_id`=posts.`ID` WHERE posts.`guid`='%s' OR (meta.`meta_key`='_wp_attached_file' AND meta.`meta_value` LIKE '%%%s');",
$file_url,
$file_path);
$attachment = $wpdb->get_col($statement);
if (count($attachment) < 1) {
return false;
}
return $attachment[0];
}

Related

Wordpress REST API - Write to JSON File?

I've been messing around with the Wordpress REST API, and created my custom endpoint, and getting the exact data I want. Basically I created an endpoint to receive all my post/pages/acf - Instead of calling the API on each page load, I just wanted to call the API once during my preloader.
However, when I call the API, all the logic runs, which then causes a loading time of 1 to 2 seconds. Is there a possibility that whenever I make an update on Wordpress, it will call my endpoint, and write a JSON file on the server, so data.json? This way, when I load my site, it can call that data.json, with absolutely no delay at all.
I'm not sure if this is possible but wanted to try asking here.
I found 3 different ways to address the question, all of them here on Stackoverflow, but I'll go with the one that Tanner started, just published in full:
function export_posts_in_json() {
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => -1,
);
$query = new WP_Query($args);
$posts = array();
while ($query->have_posts()): $query->the_post();
$posts[] = array(
'title' => get_the_title(),
'excerpt' => get_the_excerpt(),
'author' => get_the_author(),
// any extra field you might need
);
endwhile;
wp_reset_query();
$data = json_encode($posts);
$upload_dir = wp_get_upload_dir(); // set to save in the /wp-content/uploads folder
$file_name = date('Y-m-d') . '.json';
$save_path = $upload_dir['basedir'] . '/' . $file_name;
$f = fopen($save_path, "w"); //if json file doesn't gets saved, comment this and uncomment the one below
//$f = #fopen( $save_path , "w" ) or die(print_r(error_get_last(),true)); //if json file doesn't gets saved, uncomment this to check for errors
fwrite($f, $data);
fclose($f);
}
add_action('save_post', 'export_posts_in_json');
Original snippet here: https://wordpress.stackexchange.com/questions/232708/export-all-post-from-database-to-json-only-when-the-database-gets-updated
Hope this helps.
This method allow you to write a json from and external or internal API endpoint;
it is less sofisticated than the one above (destination folder wise), but uses the REST API so you can fetch the full posts object without having to specify all the fields:
// Export API Data to JSON, another method
add_action('publish_post', function ($ID, $post) {
$wp_uri = get_site_url();
$customApiEndpoint = '/wp-json/wp/v2/posts'; // or your custom endpoint
$url = $wp_uri . $customApiEndpoint; // outputs https://your-site.com/wp-json/wp/v2/posts
// $url = 'https://your-site.com/wp-json/wp/v2/posts'; // use this full path variable in case you want to use an absolute path
$response = wp_remote_get($url);
$responseData = json_encode($response); // saved under the wp root installation, can be customized to any folder
file_put_contents('your_api_data_backup.json', $responseData);
}, 10, 2);
inspired from https://stackoverflow.com/questions/46082213/wordpress-save-api-json-after-publish-a-post
You should be able to accomplish something along those lines. Check out the code below:
function export_posts_in_json () {
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => -1,
);
$query = new WP_Query( $args );
...
$data = json_encode($posts);
$folder = 'YOUR_EXPORT_PATH_HERE';
$file_name = date('Y-m-d') . '.json';
file_put_contents($folder.$file_name, $data);
}
add_action( 'save_post', 'export_posts_in_json' );
This should save a json file every time a post is made. I'm sure you can modify it to export all the data you need for your site.

Import remote image to wordpress, then set to custom field

I'm trying to import images from a remote server and set it to custom fields.
Importing and generating a attachment id is working, but something is messing with the custom fields. I don't know if is a thread issue or something.
Here's my code:
$sql = 'SELECT * FROM produto';
$retval = mysql_query( $sql, $connection) ;
while($row = mysql_fetch_array($retval, MYSQL_ASSOC))
{
$file = $row['imagem'];
$filename = basename($file);
$upload_file = wp_upload_bits($filename, null, file_get_contents($file));
if (!$upload_file['error']) {
$wp_filetype = wp_check_filetype($filename, null );
$attachment = array(
'post_mime_type' => $wp_filetype['type'],
'post_parent' => $parent_post_id,
'post_title' => preg_replace('/\.[^.]+$/', '', $filename),
'post_content' => '',
'post_status' => 'inherit'
);
$attachment_id = wp_insert_attachment( $attachment, $upload_file['file'], $parent_post_id );
$imgs[$row['modelo']][]['imagem'] = $attachment_id;
if (!is_wp_error($attachment_id)) {
require_once(ABSPATH . "wp-admin" . '/includes/image.php');
$attachment_data = wp_generate_attachment_metadata( $attachment_id, $upload_file['file'] );
wp_update_attachment_metadata( $attachment_id, $attachment_data );
}
}
}
foreach ($imgs as $key => $value) {
$args = array(
'post_type' => 'produto',
'meta_key' => 'identifier1',
'meta_value' => $key);
// The Query
$the_query = new WP_Query( $args );
if ( $the_query->have_posts() ) {
while ( $the_query->have_posts() ) {
$the_query->the_post();
update_field('imagens', $value, get_the_ID());
}
}
/* Restore original Post Data */
wp_reset_postdata();
}
If I try to set 20 itens on this custom field, it works perfectly. But when I need to run this loop with 250 results, it breaks.
What am I doing wrong?
Thanks!
I had to migrate one non wp database to a wp ready database, and the database had over 10k images. I also set up the import to do it all at once (I just uploaded not only posts, but images and all the meta data, pages, etc.).
The thing is that importing something at once will most likely time out the server, and you'll get only first n imported images, the rest won't work because of the timeout or some similar error.
The workaround is to put your script in a function that will be called with AJAX, and will import one image at the time. You just need to tweak it a bit to fetch one image at the time.
I'd first check if the $imgs array is full - all your images from old database are present with all the data attached.
If it is, you just need to add something like this
<?php
add_action( 'wp_ajax_import_image', 'import_image_callback' );
add_action( 'wp_ajax_nopriv_import_image', 'import_image_callback' ); // this is only if you're importing from the front end. If in the back end this is not necessary
/**
* AJAX callback iumport image function
*
*/
function import_image(){
$img = $_POST['img'];
$args = array(
'post_type' => 'produto',
'meta_key' => 'identifier1',
'meta_value' => $img
);
// The Query
$image_query = new WP_Query( $args );
if ( $image_query->have_posts() ) {
while ( $image_query->have_posts() ) {
$image_query->the_post();
update_field('imagens', $img['image_value'], get_the_ID());//You'll need to see what you need from the array value here, I just placed dummy image_value as I don't know what the $img array contains
}
}
/* Restore original Post Data */
wp_reset_postdata();
}
Then you'll call this function using AJAX. Now you'll probably have to save your images as JSON object and localize it. That way you'll be able to use that array in your JavaScript to fetch one image at the time with something like this:
jQuery(document).ready(function($) {
"use strict";
var images = localized_object.images_array; //JSON array
for (var i = 0; i < images.length; i++) {
var img = images[i]; //something like this - you'll see if this works 100%, but the gist is the same
upload_image(img); // Call the AJAX callback function
}
function upload_image(image){
$.ajax({
type: "POST",
url: localized_object.ajaxurl, //localize this if you're doing this from the from the front end. If you're using it in back end just use ajaxurl.
data: {
'action': 'import_image',
'img': image,
},
success: function(response) {
console.log('Image imported');
},
error : function (jqXHR, textStatus, errorThrown) {
console.log(jqXHR + ' :: ' + textStatus + ' :: ' + errorThrown);
}
});
}
});
Now since I don't know how your DB dump looks like this is not 100% guaranteed to work, but it gives you a good idea how to do it.
Hope this helps :)

Wordpress mysql and functions

My aim is to import posts from a node.js server to the wordpress database, thus is due to the large amount of records it will be trawling daily..
Do you know of any cheat sheets that show the mysql that a wordpress function uses?
For example
wp_insert_post()
//The above function uses this sql
INSERT INTO wp_posts VALUES(field1) etc etc
Thanks in advance
Auto generating content... Hmmm lets try ..
<?php
//First you need the required files...
include('../wp-load.php' );
require_once('../wp-admin/includes/media.php');
require_once('../wp-admin/includes/file.php');
require_once('../wp-admin/includes/taxonomy.php');
require_once('../wp-includes/taxonomy.php');
// Flush the wordpress db...
$wpdb->flush();
// Get your data from any feed. and lets say you will process it in a foreach loop..
foreach($somedata as $key => $value){
// i don't know your data structure so i give a symbolic example..
$post = array(
'post_author' => 2,
'post_content' => $post_content,
'post_excerpt' => $post_excerpt,
'post_status' => 'publish',
'post_title' => $post_title, // aware & ' so something to escape...
'post_type' => 'post',
'post_date' => $pub_date, // Y-m-d H:i:s
'tags_input' => $tags // comma separated tags..
);
$post_id = wp_insert_post( $post, $wp_error );
wp_set_object_terms( $post_id, $cat_ids, 'category' ); // $cat_ids is an array!
}
?>
The code above will add posts for you.. running it via cron and setting parameters to avoid duplicates your job..
Well if you need to import a remote image and add it to the post as featured you need a function like this..
function somatic_attach_external_image( $url = null, $post_id = null, $thumb = null, $filename = null, $post_data = array(),$resimdesc=null ) {
if ( !$url || !$post_id ) return new WP_Error('missing', "Need a valid URL and post ID...");
require_once( ABSPATH . 'wp-admin/includes/file.php' );
// Download file to temp location, returns full server path to temp file, ex; /home/user/public_html/mysite/wp-content/26192277_640.tmp
$tmp = download_url( $url );
// If error storing temporarily, unlink
if ( is_wp_error( $tmp ) ) {
#unlink($file_array['tmp_name']); // clean up
$file_array['tmp_name'] = '';
return $tmp; // output wp_error
}
preg_match('/[^\?]+\.(jpg|JPG|jpe|JPE|jpeg|JPEG|gif|GIF|png|PNG)/', $url, $matches); // fix file filename for query strings
$url_filename = basename($matches[0]); // extract filename from url for title
$url_type = wp_check_filetype($url_filename); // determine file type (ext and mime/type)
// override filename if given, reconstruct server path
if ( !empty( $filename ) ) {
$filename = sanitize_file_name($filename);
$tmppath = pathinfo( $tmp ); // extract path parts
$new = $tmppath['dirname'] . "/". $filename . "." . $tmppath['extension']; // build new path
rename($tmp, $new); // renames temp file on server
$tmp = $new; // push new filename (in path) to be used in file array later
}
// assemble file data (should be built like $_FILES since wp_handle_sideload() will be using)
$file_array['tmp_name'] = $tmp; // full server path to temp file
if ( !empty( $filename ) ) {
$file_array['name'] = $filename . "." . $url_type['ext']; // user given filename for title, add original URL extension
} else {
$file_array['name'] = $url_filename; // just use original URL filename
}
// set additional wp_posts columns
if ($resimdesc) {
$post_data['post_title'] = $resimdesc; // just use the original filename (no extension)
}
// make sure gets tied to parent
if ( empty( $post_data['post_parent'] ) ) {
$post_data['post_parent'] = $post_id;
}
// required libraries for media_handle_sideload
require_once(ABSPATH . 'wp-admin/includes/file.php');
require_once(ABSPATH . 'wp-admin/includes/media.php');
require_once(ABSPATH . 'wp-admin/includes/image.php');
// do the validation and storage stuff
$att_id = media_handle_sideload( $file_array, $post_id, null, $post_data ); // $post_data can override the items saved to wp_posts table, like post_mime_type, guid, post_parent, post_title, post_content, post_status
// If error storing permanently, unlink
if ( is_wp_error($att_id) ) {
#unlink($file_array['tmp_name']); // clean up
return $att_id; // output wp_error
}
// set as post thumbnail if desired
if ($thumb) {
set_post_thumbnail($post_id, $att_id);
}
return $att_id;
}
You will use this function inside the foreach.. $imgsrc is the url of the image...
$attach_id = somatic_attach_external_image( $imgsrc, $post_id, $thumb = null, $filename = null, $post, $title );
add_post_meta($post_id, '_thumbnail_id', $attach_id, true);
Good luck...

All files unattached in Media Library, need a way to re-attach them

For a few months I used a plugin that automatically downloads remote images and save them. However, I found there's about 15 000 unattached images, that are actually in posts. The plugin never attached the images to the post itself.
I have no idea what to do or how to solve this. I can't do it manually it will take ages.
Is there a way to scan the images and re-attach them to the respective post?
Update: After I run the below plugin that Sergiu mentioned. The report shows:
So it does seem to pick up the images in the post. I just wish it can attach it somehow to that post ID. Is there a way to modify the code?
In the plugin below. In line 525 i removed the code:
if ( stripos( $img, $path ) !== false ) {
$response .= 'Img already in media library<br>';
continue;
}
Now it attaches the images!
Only one last issue is that it makes new copies. I can't find a way for it to not re-download them. I prefer it to just attach them.
Here is, what i think the full piece of code responsible. Please suggest modifications:
http://pastebin.com/ePERuGjt#
/**
* Extracts all images in content adds to media library if external and updates content with new url
* #param object $post The post object
* #return array|bool Post id and images converted on success false if no images found in source
*/
function extract_multi( $post ) {
$html = $post->post_content;
$path = wp_upload_dir();
$path = $path['baseurl'];
$error = 0;
$response = '';
if ( stripos( $html, '<img' ) !== false ) {
$regex = '#<\s*img [^\>]*src\s*=\s*(["\'])(.*?)\1#im';
preg_match_all( $regex, $html, $matches );
if ( is_array( $matches ) && ! empty( $matches ) ) {
$new = array();
$old = array();
foreach( $matches[2] as $img ) {
/** Compare image source against upload directory to prevent adding same attachment multiple times */
$tmp = download_url( $img );
preg_match('/[^\?]+\.(jpg|JPG|jpe|JPE|jpeg|JPEG|gif|GIF|png|PNG)/', $img, $matches);
$file_array['name'] = basename($matches[0]);
$file_array['tmp_name'] = $tmp;
// If error storing temporarily, unlink
if ( is_wp_error( $tmp ) ) {
#unlink($file_array['tmp_name']);
$file_array['tmp_name'] = '';
continue;
}
$id = media_handle_sideload( $file_array, $post->ID );
if ( ! is_wp_error( $id ) ) {
$url = wp_get_attachment_url( $id );
$thumb = wp_get_attachment_thumb_url( $id );
array_push( $new, $url );
array_push( $old, $img );
$response .= '<p><img src="'.esc_url( $thumb ).'" style="max-width:100px;" /><br>';
$response .= '<a href="'. wp_nonce_url( get_edit_post_link( $id, true ) ).'" >'.get_the_title( $id ). '</a> Imported and attached</p>';
} else {
$response .= '<span style="color:red">Upload Error: Could not upload image. Check for malformed img src url</span><br>';
$error ++;
}
}
if( !empty( $new ) ) {
$content = str_ireplace( $old, $new, $html );
$post_args = array( 'ID' => $post->ID, 'post_content' => $content, );
if ( !empty( $content ) )
$post_id = wp_update_post( $post_args );
if ( isset( $post_id ) )
$response .= 'Post Content updated for Post: '.esc_html( $post->post_title).'<br>';
return array( 'error' => $error, 'response' => $response );
} else
$response .= 'No external images found for ' . esc_html( $post->post_title ) . '<br>';
return array ( 'error' => $error, 'response' => $response );
} else {
$response .= 'Error processing images for '. esc_html( $post->post_title ) .'<br>';
return array ( 'error' => $error, 'response' => $response );
}
} else {
$response .= 'No images found for ' . esc_html( $post->post_title) . '<br>';
return array ( 'error' => $error, 'response' => $response );
}
}
I am the original poster for this problem. After a few years, I came across this old post. I dug up the solution, in the hopes that it might help anyone in the future.
This is the modified media-tools.php file:
https://pastebin.com/8iUT78aP
Just install Media Tools plugin: https://github.com/c3mdigital/media-tools-for-WordPress
and overwrite the pastebin with the media-tools.php
Then, the plugin will actually re-attached all unattached media to the proper posts, also without re-downloading the images.
I hope this helps someone, as this problem is pure hell to solve.
This plugin seems to implement a feature dealing with exactly your problem.
i would suggest doing this way:
wget your site
wget -m http://yoursite.com
this should mirror all your site.
wget WILL NOT download unatached images.
check if everything was downloaded
delete wp-content directory (i mean dir where your images are stored)
upload files downloaded by wget.
next time use: DX Delete Attached Media Plugin.
it deletes all images attached to posts, when post is deleted.

How to attach an image to a post using wordpress and xml-rpc? [duplicate]

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.

Categories