';
}
}
// Create the LJXP Options Page
function ljxp_add_pages() {
add_options_page("LiveJournal", "LiveJournal", 6, __FILE__, 'ljxp_display_options');
}
// Display the options page
function ljxp_display_options() {
global $wpdb;
// List all options to load
$option_list = array( 'ljxp_host' => 'www.livejournal.com',
'ljxp_username' => '',
'ljxp_password' => '',
'ljxp_custom_name_on' => false,
'ljxp_custom_name' => '',
'ljxp_privacy' => 'public',
'ljxp_comments' => 0,
'ljxp_tag' => '1',
'ljxp_more' => 'link',
'ljxp_community' => '',
'ljxp_skip_cats' => array(),
'ljxp_header_loc' => 0, // 0 means top, 1 means bottom
'ljxp_custom_header' => '', ); // I love trailing commas
// Options to be filtered with 'stripslashes'
$option_stripslash = array('ljxp_host', 'ljxp_username', 'ljxp_custom_name', 'ljxp_community', 'ljxp_custom_header', );
foreach($option_list as $_opt => $_default){
add_option($_opt); // Just in case it does not exist
$options[$_opt] =(in_array($_opt, $option_stripslash) ? stripslashes(get_option($_opt)) : get_option($_opt)); // Listed in $option_stripslash? Filter : Give away
// If the option remains empty, set it to the default
if($options[$_opt] == '' && $_default !== ''){
update_option($_opt, $_default);
$options[$_opt] = $_default;
}
}
// If we're handling a submission, save the data
if(isset($_REQUEST['update_lj_options']) || isset($_REQUEST['crosspost_all'])) {
// Grab a list of all entries that have been crossposted
$repost_ids = $wpdb->get_col("SELECT post_id FROM $wpdb->postmeta WHERE meta_key='ljID'");
// Set the update flag
$need_update = 0;
/*
* Warning. This is rather UNSAFE code. The only reason for it to remain unchanged so far is that it is inside a protected area. -- FreeAtNet
* TODO: fix security where appropriate
*/
$request_names = array('ljxp_host' => 'host',
'ljxp_username' => 'username',
'ljxp_custom_name_on' => 'custom_name_on',
'ljxp_custom_name' => 'custom_name',
'ljxp_privacy' => 'privacy',
'ljxp_comments' => 'comments',
'ljxp_tag' => 'tag',
'ljxp_more' => 'more',
'ljxp_community' => 'community',
'ljxp_header_loc' => 'header_loc',
'ljxp_custom_header' => 'custom_header',
);
foreach($request_names as $_orig => $_reqname){
if(isset($_REQUEST[$_reqname]) && $_REQUEST[$_reqname] != $options[$_orig]){
// Do the general stuff
update_option($_orig, $_REQUEST[$_reqname]);
$options[$_orig] = $_REQUEST[$_reqname]; // TODO: xss_clean($_REQUEST[$_reqname])
// And then the custom actions
switch($_orig){ // this is kinda harsh, I guess
case 'ljxp_post' :
case 'ljxp_username' :
case 'ljxp_comments' :
case 'ljxp_community' :
ljxp_delete_all($repost_ids);
case 'ljxp_custom_name_on' :
case 'ljxp_privacy' :
case 'ljxp_tag' :
case 'ljxp_more' :
case 'ljxp_custom_header' :
$need_update = 1;
break;
case 'ljxp_custom_name' :
if($options['ljxp_custom_name']) {
$need_update = 1;
}
break;
default:
continue;
break;
}
}
}
sort($options['ljxp_skip_cats']);
$new_skip_cats = array_diff(get_all_category_ids(), (array)$_REQUEST['post_category']);
sort($new_skip_cats);
if($options['ljxp_skip_cats'] != $new_skip_cats) {
update_option('ljxp_skip_cats', $new_skip_cats);
$options['ljxp_skip_cats'] = $new_skip_cats;
}
unset($new_skip_cats);
if($_REQUEST['password'] != "") {
update_option('ljxp_password', md5($_REQUEST['password']));
}
if($need_update && isset($_REQUEST['update_lj_options'])) {
@set_time_limit(0);
ljxp_post_all($repost_ids);
}
if(isset($_REQUEST['crosspost_all'])) {
@set_time_limit(0);
ljxp_post_all($wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE post_status='publish' AND post_type='post'"));
}
// Copied from another options page
echo '
';
_e('Options saved.', LJXP_DOMAIN);
echo '
';
}
// And, finally, output the form
// May add some Javascript to disable the custom_name field later - don't
// feel like it now, though
?>
$cat) {
$cats[$key]['checked'] = !in_array($cat['cat_ID'], $selected_cats);
$cats[$key]['children'] = ljxp_cat_select($cat['children'], $selected_cats);
}
return $cats;
}
function ljxp_post($post_id) {
global $wpdb, $tags, $cats; // tags/cats are going to be filtered thru an external function
// If the post was manually set to not be crossposted, give up now
if(get_post_meta($post_id, 'no_lj', true)) {
return $post_id;
}
// Get the relevent info out of the database
$options = array(
'host' => stripslashes(get_option('ljxp_host')),
'user' => stripslashes(get_option('ljxp_username')),
'pass' => get_option('ljxp_password'),
'custom_name_on' => get_option('ljxp_custom_name_on'),
'custom_name' => stripslashes(get_option('ljxp_custom_name')),
'privacy' => ( (get_post_meta($post_id, 'ljxp_privacy', true) != 0) ?
get_post_meta($post_id, 'ljxp_privacy', true) :
get_option('ljxp_privacy') ),
'comments' => ( (get_post_meta($post_id, 'ljxp_comments', true != 0) ) ? ( 2 - get_post_meta($post_id, 'ljxp_comments', true) ) : get_option('ljxp_comments') ),
'tag' => get_option('ljxp_tag'),
'more' => get_option('ljxp_more'),
'community' => stripslashes(get_option('ljxp_community')),
'skip_cats' => get_option('ljxp_skip_cats'),
'copy_cats' => array_diff(get_all_category_ids(), get_option('ljxp_skip_cats')),
'header_loc' => get_option('ljxp_header_loc'),
'custom_header' => stripslashes(get_option('ljxp_custom_header')),
);
// If the post shows up in the forbidden category list and it has been
// crossposted before (so the forbidden category list must have changed),
// delete the post. Otherwise, just give up now
$do_crosspost = 0;
foreach(wp_get_post_cats(1, $post_id) as $cat) {
if(in_array($cat, $options['copy_cats'])) {
$do_crosspost = 1;
break; // decision made and cannot be altered, fly on
}
}
if(!$do_crosspost) {
return ljxp_delete($post_id);
}
// And create our connection
$client = new IXR_Client($options['host'], '/interface/xmlrpc');
// Get the challenge string
// Using challenge for the most security. Allows pwd hash to be stored
// instead of pwd
if (!$client->query('LJ.XMLRPC.getchallenge')) {
wp_die('Something went wrong - '.$client->getErrorCode().' : '.$client->getErrorMessage());
}
// And retrieve the challenge string
$response = $client->getResponse();
$challenge = $response['challenge'];
$post = & get_post($post_id);
// Insert the name of the page we're linking back to based on the options set
if(!$options['custom_name_on']) {
$blogName = get_option("blogname");
}
else {
$blogName = $options['custom_name'];
}
// Tagging and categorizing — for LJ tags
// Not to be moved down: the else case of custom header is using $cats and $tags
$cats = array();
$tags = array();
$cats = wp_get_post_categories($post_id, array('fields' => 'all')); // wp_get_post_cats is deprecated as of WP2.5
// the new function can get names itself, too
$tags = wp_get_post_tags($post_id, array('fields' => 'all'));
// Need advice on merging all ( /\ and \/ ) this code
// convert retrieved objects to arrays of (term_id => name) pairs
$modify = create_function('$f, $n, $obj', 'global $$f; $p = &$$f; unset($p[$n]); $p[$obj->term_id] = $obj->name;');
if(count($tags) > 0) array_map($modify, array_fill(0, count($tags), 'tags'), array_keys($tags), array_values($tags));
if(count($cats) > 0) array_map($modify, array_fill(0, count($cats), 'cats'), array_keys($cats), array_values($cats));
switch($options['tag']){
case 0 :
// pass
break;
case 1 :
$cat_string = implode(", ", $cats);
break;
case 2 :
$cat_string = implode(", ", $tags);
break;
case 3 :
$cat_string = implode(", ", array_unique(array_merge($cats, $tags)));
break;
}
if($options['custom_header'] == '') {
$postHeader = '
';
// If the post is not password protected, follow standard procedure
if(!$post->post_password) {
$postHeader .= __('Originally published at', LJXP_DOMAIN);
$postHeader .= ' ';
$postHeader .= $blogName;
$postHeader .= '.';
}
// If the post is password protected, put up a special message
else {
$postHeader .= __('This post is password protected. You can read it at', LJXP_DOMAIN);
$postHeader .= ' ';
$postHeader .= $blogName;
$postHeader .= ', ';
$postHeader .= __('where it was originally posted', LJXP_DOMAIN);
$postHeader .= '.';
}
// Depending on whether comments or allowed or not, alter the header
// appropriately
if($options['comments']) {
$postHeader .= sprintf(__(' You can comment here or there.', LJXP_DOMAIN), get_permalink($post_id));
}
else {
$postHeader .= sprintf(__(' Please leave any comments there.', LJXP_DOMAIN), get_permalink($post_id));
}
$postHeader .= '
';
}
else {
$postHeader = $options['custom_header'];
// pre-post formatting for tags and categories
$htags = '';
$hcats = '';
foreach($tags as $_term_id => $_name) $htags[] = ''.$_name.'';
foreach($cats as $_term_id => $_name) $hcats[] = ''.$_name.'';
$htags = implode(', ', (array)$htags);
$hcats = implode(', ', (array)$hcats);
$find = array('[blog_name]', '[blog_link]', '[permalink]', '[comments_link]', '[comments_count]', '[tags]', '[categories]');
$replace = array($blogName, get_settings('home'), get_permalink($post_id), get_permalink($post_id).'#comments', lj_comments($post_id), $htags, $hcats);
$postHeader = str_replace($find, $replace, $postHeader);
}
// $the_event will eventually be passed to the LJ XML-RPC server.
$the_event = "";
// and if the post isn't password protected, we need to put together the
// actual post
if(!$post->post_password) {
// and if there's no tag, we can spit it out and go on our
// merry way
if(strpos($post->post_content, "", $post->post_content, 2);
$content = preg_split("//", $post->post_content, 2, PREG_SPLIT_DELIM_CAPTURE);
$delim = $content[1];
if (!trim($delim)) $delim = __('Read the rest of this entry »', LJXP_DOMAIN);
$the_event .= apply_filters('the_content', $content[0]);
switch($options['more']) {
case "copy":
$the_event .= apply_filters('the_content', $content[2]);
break;
case "link":
$the_event .= sprintf('
';
break;
case "lj-cut":
$the_event .= '' . apply_filters('the_content', $content[2]) . '';
break;
}
}
}
// Either prepend or append the header to $the_event, depending on the
// config setting
// Remember that 0 is at the top, 1 at the bottom
if($options['header_loc']) {
$the_event .= $postHeader;
}
else {
$the_event = $postHeader.$the_event;
}
// Get the most recent post (to see if this is it - it it's not, backdate)
$recent_id = $wpdb->get_var("SELECT ID FROM $wpdb->posts WHERE post_status='publish' AND post_type='post' ORDER BY post_date DESC LIMIT 1");
// Get a timestamp for retrieving dates later
$date = strtotime($post->post_date);
$args = array('username' => $options['user'],
'auth_method' => 'challenge',
'auth_challenge' => $challenge,
'auth_response' => md5($challenge . $options['pass']), // By spec, auth_response is md5(challenge + md5(pass))
'ver' => '1', // Receive UTF-8 instead of ISO-8859-1
'event' => $the_event,
'subject' => apply_filters('the_title', $post->post_title),
'year' => date('Y', $date),
'mon' => date('n', $date),
'day' => date('j', $date),
'hour' => date('G', $date),
'min' => date('i', $date),
'props' => array('opt_nocomments' => !$options['comments'], // allow comments?
'opt_preformatted' => true, // event text is preformatted
'opt_backdated' => !($post_id == $recent_id), // prevent updated
// post from being show on top
'taglist' => ($options['tag'] != 0 ? $cat_string : ''),
),
'usejournal' => (!empty($options['community']) ? $options['community'] : $options['user']),
);
// Set the privacy level according to the settings
switch($options['privacy']) {
case "public":
$args['security'] = 'public';
break;
case "private":
$args['security'] = 'private';
break;
case "friends":
$args['security'] = 'usemask';
$args['allowmask'] = 1;
break;
default :
$args['security'] = "public";
break;
}
// Assume this is a new post
$method = 'LJ.XMLRPC.postevent';
// But check to see if there's an LJ post associated with our WP post
if(get_post_meta($post_id, 'ljID', true)) {
// If there is, add the itemid attribute and change from posting to editing
$args['itemid'] = get_post_meta($post_id, 'ljID', true);
$method = 'LJ.XMLRPC.editevent';
}
// And awaaaayyy we go!
if (!$client->query($method, $args)) {
wp_die('Something went wrong - '.$client->getErrorCode().' : '.$client->getErrorMessage());
}
// If we were making a new post on LJ, we need the itemid for future reference
if('LJ.XMLRPC.postevent' == $method) {
$response = $client->getResponse();
// Store it to the metadata
add_post_meta($post_id, 'ljID', $response['itemid']);
}
// If you don't return this, other plugins and hooks won't work
return $post_id;
}
function ljxp_delete($post_id) {
// Pull the post_id
$ljxp_post_id = get_post_meta($post_id, 'ljID', true);
// Ensures that there's actually a value. If the post was never
// cross-posted, the value wouldn't be set, and there's no point in
// deleting entries that don't exist
if($ljxp_post_id == 0) {
return $post_id;
}
// Get the necessary login info
$host = get_option('ljxp_host');
$user = get_option('ljxp_username');
$pass = get_option('ljxp_password');
// And open the XMLRPC interface
$client = new IXR_Client($host, '/interface/xmlrpc');
// Request the challenge for authentication
if (!$client->query('LJ.XMLRPC.getchallenge')) {
wp_die('Something went wrong - '.$client->getErrorCode().' : '.$client->getErrorMessage());
}
// And retrieve the challenge that LJ returns
$response = $client->getResponse();
$challenge = $response['challenge'];
// Most of this is the same as before. The important difference is the
// value of $args[event]. By setting it to a null value, LJ deletes the
// entry. Really rather klunky way of doing things, but not my code!
$args = array(
'username' => $user,
'auth_method' => 'challenge',
'auth_challenge' => $challenge,
'auth_response' => md5($challenge . $pass),
'itemid' => $ljxp_post_id,
'event' => "",
'subject' => "Delete this entry",
// I probably don't need to set these, but, hell, I've got it working
'year' => date('Y'),
'mon' => date('n'),
'day' => date('j'),
'hour' => date('G'),
'min' => date('i'),
);
// And awaaaayyy we go!
if (!$client->query('LJ.XMLRPC.editevent', $args)) {
wp_die('Something went wrong - '.$client->getErrorCode().' : '.$client->getErrorMessage());
}
delete_post_meta($post_id, 'ljID');
return $post_id;
}
function ljxp_edit($post_id) {
// This function will delete a post from LJ if it's changed from the
// published status or if crossposting was just disabled on this post
// Pull the post_id
$ljxp_post_id = get_post_meta($post_id, 'ljID', true);
// Ensures that there's actually a value. If the post was never
// cross-posted, the value wouldn't be set, so we're done
if(0 == $ljxp_post_id) {
return $post_id;
}
$post = & get_post($post_id);
// See if the post is currently published. If it's been crossposted and its
// state isn't published, then it should be deleted
// Also, if it has been crossposted but it's set to not crosspost, then
// delete it
if('publish' != $post->post_status || 1 == get_post_meta($post_id, 'no_lj', true)) {
ljxp_delete($post_id);
}
return $post_id;
}
function ljxp_sidebar() {
global $post, $wp_version;
?>
<=((bool)version_compare($wp_version, '2.5', '>=')? 'div class="postbox closed"' : 'fieldset class="dbx-box"' )?> id="ljxpdiv">