I am trying to write APIC picture to mp3 file with getid3. here is the code;
$cover = "/home/user/public_html/artwork/cover.jpg";
$TagData['attached_picture'][]=array(
'picturetypeid'=>2, // Cover. More: module.tag.id3v2.php -> function APICPictureTypeLookup
'description'=>'cover', // text field
'mime'=>'image/jpeg', // Mime type image
'data'=>$cover // Image data
);
but it doesnt work. image size is around 1.5 MB. should i resize it or sth ?
where am i wrong ?
Thanks
Looking at the demo they have on their website: http://www.getid3.org/source/demo.write.phps
snippet of code:
$fd = fopen($_FILES['userfile']['tmp_name'], 'rb')
$APICdata = fread($fd, filesize($_FILES['userfile']['tmp_name']));
fclose ($fd);
$imagetypes = array(1=>'gif', 2=>'jpeg', 3=>'png');
if (isset($imagetypes[$APIC_imageTypeID])) {
$TagData['attached_picture'][0]['data'] = $APICdata;
$TagData['attached_picture'][0]['picturetypeid'] = $_POST['APICpictureType'];
$TagData['attached_picture'][0]['description'] = $_FILES['userfile']['name'];
$TagData['attached_picture'][0]['mime'] = 'image/'.$imagetypes[$APIC_imageTypeID];
}
Seems like the data key needs to be the image content, not just the path to the image file.
So in your case, it should be something like:
$cover = "/home/user/public_html/artwork/cover.jpg";
$fd = fopen($cover, 'rb')
$APICdata = fread($fd, filesize($coverFile));
fclose ($fd);
$TagData['attached_picture'][]=array(
'picturetypeid'=>2, // Cover. More: module.tag.id3v2.php -> function APICPictureTypeLookup
'description'=>'cover', // text field
'mime'=>'image/jpeg', // Mime type image
'data'=>$APICdata // Image data
);
Note: This is just after a quick glance at the demo code, I have not used this library or tested this code.
GetID3 needs the content of the file to be send for the data, not the file path. Then only it will be able to embed them into the file. Try
$cover = "/home/user/public_html/artwork/cover.jpg";
$TagData['attached_picture'][]=array(
'picturetypeid'=>2, // Cover. More: module.tag.id3v2.php -> function APICPictureTypeLookup
'description'=>'cover', // text field
'mime'=>'image/jpeg', // Mime type image
'data'=> file_get_contents($cover) // Image data; not the file name
);
tested and working :)
I found this in the source code:
case 'APIC':
// 4.14 APIC Attached picture
// Text encoding $xx
// MIME type <text string> $00
// Picture type $xx
// Description <text string according to encoding> $00 (00)
// Picture data <binary data>
So picture data must be binary.
The solution is here: getid3 demo
This one is working for me for long time:
$TagData = array(); //your other tags
$fd = fopen($albumPath, 'rb');
$APICdata = fread($fd, filesize($albumPath));
fclose($fd);
if($APICdata) {
$TagData += array(
'attached_picture' => array(0 => array(
'data' => $APICdata,
'picturetypeid' => '0x03',
'description' => 'cover',
'mime' => image_type_to_mime_type($albumExifType)
))
);
}
//and write the tags to file
<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info#getid3.org> //
// available at http://getid3.sourceforge.net //
// or http://www.getid3.org //
// also https://github.com/JamesHeinrich/getID3 //
/////////////////////////////////////////////////////////////////
// //
// /demo/demo.simple.write.php - part of getID3() //
// Sample script showing basic syntax for writing tags //
// See readme.txt for more details //
// ///
/////////////////////////////////////////////////////////////////
//die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in '.$_SERVER['PHP_SELF']);
$TextEncoding = 'UTF-8';
$albumPath = "img/img.jpg"; // path to your image
require_once('../getid3/getid3.php');
// Initialize getID3 engine
$getID3 = new getID3;
$getID3->setOption(array('encoding'=>$TextEncoding));
require_once('../getid3/write.php');
// Initialize getID3 tag-writing module
$tagwriter = new getid3_writetags;
//$tagwriter->filename = '/path/to/file.mp3';
$tagwriter->filename = 'uploads/problem.mp3';
//$tagwriter->tagformats = array('id3v1', 'id3v2.3');
$tagwriter->tagformats = array('id3v2.3');
// set various options (optional)
$tagwriter->overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data (experimental)
$tagwriter->remove_other_tags = false; // if true removes other tag formats (e.g. ID3v1, ID3v2, APE, Lyrics3, etc) that may be present in the file and only write the specified tag format(s). If false leaves any unspecified tag formats as-is.
$tagwriter->tag_encoding = $TextEncoding;
$tagwriter->remove_other_tags = true;
// populate data array
$TagData = array(
'title' => array('My Song'),
'artist' => array('My Song'),
'album' => array('My Song'),
'year' => array('20015'),
'genre' => array('My Song'),
'comment' => array('My Song'),
'track' => array('01'),
);
$fd = fopen($albumPath, 'rb');
$APICdata = fread($fd, filesize($albumPath));
fclose($fd);
if($APICdata) {
$TagData += array(
'attached_picture' => array(0 => array(
'data' => $APICdata,
'picturetypeid' => '0x03',
'description' => 'cover',
'mime' => image_type_to_mime_type(#$albumExifType)
))
);
}
$tagwriter->tag_data = $TagData;
// write tags
if ($tagwriter->WriteTags()) {
echo 'Successfully wrote tags<br>';
if (!empty($tagwriter->warnings)) {
echo 'There were some warnings:<br>'.implode('<br><br>', $tagwriter->warnings);
}
} else {
echo 'Failed to write tags!<br>'.implode('<br><br>', $tagwriter->errors);
}
For anyone who just needd to update their ID3 tags including the Album Art , above code works just fine You need to have getID3 library to work .
This Answer is based on JacopKane's Answer so credit goes to him
Related
Found a handful of questions on here about this with no answer, so hopefully, someone can point me in the right direction...
I'm trying to create and save a csv file to storage, then update the DB in Laravel. I can create the file successfully, and I can update the DB successfully... but I'm stuck on putting them both together. In my controller, I have this for creating the file (taken from here):
public function updatePaymentConfirm(Request $request) {
$users = User::all();
$fileName = 'test.csv';
$headers = array(
"Content-type" => "text/csv",
"Content-Disposition" => "attachment; filename=$fileName",
"Pragma" => "no-cache",
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
"Expires" => "0"
);
$columns = array('First Name', 'Email');
$callback = function() use($users, $columns) {
$file = fopen('php://output', 'w');
fputcsv($file, $columns);
foreach ($users as $user) {
$row['First Name'] = $user->first_name;
$row['Email'] = $user->email;
fputcsv($file, array($row['First Name'], $row['Email']));
}
fclose($file);
};
// return response()->stream($callback, 200, $headers);
}
When the function completes, the last line (that's commented out) prompts the user to download the newly created file (which is not the functionality I'm looking for). I tried adding this to my controller in its place for saving to storage and also updating the database:
$fileModel = new UserDocument;
if($callback) {
$filePath = $callback->storeAs('uploads', $fileName, 'public');
$fileModel->name = $fileName;
$fileModel->file_path = '/storage/' . $filePath;
$fileModel->save();
return back()
->with('success','File has been uploaded.')
->with('file', $fileName);
}
It saves a row to the db, albeit incorrectly, but it doesn't save the file to storage. I've reworked the $filePath line a million times, but I keep getting this error Call to a member function storeAs() on resource or something similar. I'm relatively new to working with Laravel, so I'm not sure what I should be looking for. Thoughts?
Removed everything and started over... got it! And for anyone else running into the same issue: just calling for a file that doesn't exist creates the file (unless the file exists - then it updates it), so you don't have to create a temp file or use $file = fopen('php://output', 'w'); to create the file. It'll automatically "save" the newly generated file in the file path you specified when you fclose() out of the file.
The only thing I'll note is that the file path has to exist (the file doesn't, but the file path does). In my instance, the file path already exists, but if yours doesn't or if you're not sure if it does, check to see if it exists, and then make the directory.
public function updatePaymentConfirm(Request $request) {
$user = Auth::user();
$path = storage_path('app/public/docs/user_docs/'.$user->id);
$fileName = $user->ein.'.csv';
$file = fopen($path.$fileName, 'w');
$columns = array('First Name', 'Email Address');
fputcsv($file, $columns);
$data = [
'First Name' => $user->first_name,
'Email Address' => $user->email,
];
fputcsv($file, $data);
fclose($file);
$symlink = 'public/docs/user_docs/'.$user->id.'/';
$fileModel = new UserDocument;
$fileModel->name = 'csv';
$fileModel->file_path = $symlink.$fileName;
$fileModel->save();
return redirect()->route('completed');
}
** UPDATE **
Everything worked perfectly locally, and when I pushed this to production, I received this error 🙄:
fopen(https://..../12-3456789.csv): failed to open stream: HTTP wrapper does not support writeable connections.
I'm saving to an s3 bucket, and I had to rework the entire process. You can't create and/or write to a file in the directory. I had to create a temp file first. Here's where I landed:
$user = Auth::user();
$s3 = Storage::disk('s3');
$storage = Storage::disk('s3')->url('/');
$path = 'public/docs/user_docs/'.$user->id.'/';
$csvFile = tmpfile();
$csvPath = stream_get_meta_data($csvFile)['uri'];
$fd = fopen($csvPath, 'w');
$columns = array('First Name', 'Email Address');
$data = array(
'First Name' => $user->first_name,
'Email Address' => $user->email,
);
fputcsv($fd, $columns);
fputcsv($fd, $data);
fclose($fd);
$s3->putFileAs('', $csvPath, $path.$user->ein.'.csv');
Today I have fixed it with this snipe:
// output up to 5MB is kept in memory, if it becomes bigger it will
// automatically be written to a temporary file
$csv = fopen('php://temp/maxmemory:'. (5*1024*1024), 'r+');
fputcsv($csv, array('blah','blah'));
rewind($csv);
$output = stream_get_contents($csv);
// Put the content directly in file into the disk
Storage::disk('myDisk')->put("report.csv", $output);
This code is easy and functional, use Laravel Storage Class
https://laravel.com/docs/9.x/filesystem#main-content
use Illuminate\Support\Facades\Storage;
// data array
$results = [
['id' => 0, 'name' => 'David', 'parent' => 1],
['id' => 1, 'name' => 'Ron', 'parent' => 0],
['id' => 2, 'name' => 'Mark', 'parent' => 1]
];
// create a variable to store data
$pages = "id,name,parent\n"; // use " not ' or \n not working
// use foreach to data
foreach ($results as $where) {
$pages .= "{$where['id']},{$where['name']},{$where['parent']}\n";
}
// use Fecades Laravel Storage
Storage::disk('local')->put('file.csv', $pages);
I have been making a plugin which allows admins to upload image, i can see the image(so can other people which is expected), delete and re-upload, but the problem is when another admin try to edit the plugin, they cant see the draft image, they only see the image they upload and i see the image i upload as draft. What i want is if i upload an image everyone that tries to edit, they should see the image i have upload as draft.
I will add my code below, if someone can help i will really appreciate it.
view.php:
$i = 1;
$out = array();
$fs = get_file_storage();
$files = $fs->get_area_files($this->context->id, 'block_cls_user_profile', 'cls_img', $i, false, false);
foreach ($files as $file) {
$filename = $file->get_filename();
$url = moodle_url::make_pluginfile_url(
$file->get_contextid(),
$file->get_component(),
$file->get_filearea(),
$file->get_itemid(),
$file->get_filepath(),
$filename);
// $out[] = html_writer::link($url, $filename);
}
// $br = html_writer::empty_tag('br');
lib.php
// Check the contextlevel is as expected - if your plugin is a block, this becomes CONTEXT_BLOCK, etc.
if ($context->contextlevel != CONTEXT_BLOCK) {
return false;
}
// Make sure the filearea is one of those used by the plugin.
if ($filearea !== 'cls_img') {
return false;
}
// $fs = get_file_storage();
// $file = $fs->get_file($context->id, 'local_myplugin', $filearea, $args[0], '/', $args[1]);
// send_stored_file($file);
// Make sure the user is logged in and has access to the module (plugins that are not course modules should leave out the 'cm' part).
require_login($course, true);
// No check for capability, because everybody needs to see it
// Check the relevant capabilities - these may vary depending on the filearea being accessed.
/*
if (!has_capability('mod/bookcase:addinstance', $context)) {
return false;
}
*/
// Leave this line out if you set the itemid to null in make_pluginfile_url (set $itemid to 0 instead).
$itemid = array_shift($args); // The first item in the $args array.
// Use the itemid to retrieve any relevant data records and perform any security checks to see if the
// user really does have access to the file in question.
// Extract the filename / filepath from the $args array.
$filename = array_pop($args); // The last item in the $args array.
if (!$args) {
$filepath = '/'; // $args is empty => the path is '/'
} else {
$filepath = '/'.implode('/', $args).'/'; // $args contains elements of the filepath
}
// Retrieve the file from the Files API.
$fs = get_file_storage();
$file = $fs->get_file($context->id, 'block_cls_user_profile', $filearea, $itemid, $filepath, $filename);
if (!$file) {
return false; // The file does not exist.
}
// We can now send the file back to the browser - in this case with a cache lifetime of 1 day and no filtering.
// From Moodle 2.3, use send_stored_file instead.
send_stored_file($file, 86400, 0, $forcedownload, $options);
edit_form.php
$mform->addElement('filemanager', 'config_profile_image', get_string('file'), null,
array('maxbytes' => $CFG->maxbytes, 'areamaxbytes' => 10485760, 'maxfiles' => 1,
'accepted_types' => '*', 'return_types'=> FILE_INTERNAL | FILE_EXTERNAL));
$i = 1;
if ($draftitemid = file_get_submitted_draft_itemid('config_profile_image')) {
file_save_draft_area_files($draftitemid, $this->block->context->id, 'block_cls_user_profile', 'cls_img', $i,array('subdirs' => false, 'maxfiles' => 1));
}
I have edited the edit_form.php and now it looks like this:
global $CFG, $mform;
$i = 0;
$draftitemid = file_get_submitted_draft_itemid('config_profile_image');
file_prepare_draft_area($draftitemid, $this->block->context->id, 'block_cls_user_profile', 'cls_img', $i,
array('subdirs' => 0, 'maxbytes' => $CFG->maxbytes, 'maxfiles' => 1));
$entry->config_profile_image = $draftitemid;
$mform->set_data($entry);
file_save_draft_area_files($draftitemid, $this->block->context->id, 'block_cls_user_profile', 'cls_img', $i,array('subdirs' => false, 'maxfiles' => 1));
parent::set_data($defaults);}}
But the error i get now is 'Call to a member function set_data() on null'.
Thank you.
I tried to write a php script to get cover image and use it as cover image for mp3 file using getid3 php but the cover image is not showing up in media players.
The cover image is showing in windows folders but not in media players icon badge... Also not showing up on google play music on android.
Belows the code.
<?php
$scr1=$_POST['search1'];
$op90=$_POST['search2'];
$kat=$_POST['search3'];
$album=$_POST['search4'];
$genre=$_POST['search5'];
$TextEncoding = 'UTF-8';
$albumPath = "../../image/".$scr1.".jpg"; // path to your image
require_once("../getid3/getid3.php");
// Initialize getID3 engine
$getID3 = new getID3;
$getID3->setOption(array('encoding'=>$TextEncoding));
require_once("../getid3/write.php");
// Initialize getID3 tag-writing module
$tagwriter = new getid3_writetags;
$tagwriter->filename = '../../../public_html/song/'.$op90.'.mp3';
$filename='../../../public_html/song/'.$op90.'.mp3';
//$tagwriter->tagformats = array('id3v1', 'id3v2.3');
$tagwriter->tagformats = array('id3v2.3');
$ThisFileInfo = $getID3->analyze($filename);
// set various options (optional)
$tagwriter->overwrite_tags = true; // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data (experimental)
$tagwriter->remove_other_tags = false; // if true removes other tag formats (e.g. ID3v1, ID3v2, APE, Lyrics3, etc) that may be present in the file and only write the specified tag format(s). If false leaves any unspecified tag formats as-is.
$tagwriter->tag_encoding = $TextEncoding;
$tagwriter->remove_other_tags = true;
// populate data array
$TagData = array(
'title' => array($op90),
'artist' => array($kat),
'album' => array($album),
'year' => array($ThisFileInfo['comments_html']['year'][0]),
'genre' => array($genre),
'comment' => array($ThisFileInfo['comments_html']['comment'][0]),
'track' => array($ThisFileInfo['comments_html']['track'][0]),
);
$fd = fopen($albumPath, 'rb');
$APICdata = fread($fd, filesize($albumPath));
fclose($fd);
if($APICdata) {
$TagData += array(
'attached_picture' => array(0 => array(
'data' => $APICdata,
'picturetypeid' => '0x03',
'description' => 'cover',
'mime' => image_type_to_mime_type(#$albumExifType)
))
);
}
$tagwriter->tag_data = $TagData;
// write tags
if ($tagwriter->WriteTags()) {if (!empty($tagwriter->warnings)) {}} else {}
How do I generate a (text only) file in Drupal 7, and attach it to a particular node automatically.
Is there a way to do this with a module, or should I use code to do it in some way (this seems tricky).
First create a file using the drupal file_save_data
$data = 'Your text data'; // Text data to be saved to file.
$filename = 'filename.txt'; // Filename
$file = file_save_data($data,'public://' .$filename);
Now $file will contain the file object that we can attach to the node. Let say you have already created and attached a file field to node type. Lets call it 'field_custom_file'
Then load the node you want to attach then cast the file object to array and attach it to the field.
$node = node_load($nid); // $nid is the id of the node where you want to attach the file.
$node->field_custom_file[LANGUAGE_NONE][] = (array)$file;
node_save($node);
$file = (object) array(
'uid' => 1,
'uri' => $filepath,
'filemime' => file_get_mimetype($filepath),
'status' => 1,
'display' => 1,
);
$file = file_copy($file, 'public://');
$node->field_file['und'][0] = $file;
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.