'; } } // 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 ?>








[blog_name]
[blog_link]
[permalink]
[comments_link]
[tags]
[categories]
[comments_count]





3 -> 2 -> 0 is a wierd order, but * if categories = 1 and tags = 2, * nothing would equal 0 * and * tags+categories = 3 */ ?>







at least one of the above categories selected will be crossposted.'); ?>

$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('

', get_permalink($post_id), $post_id) . $delim . '

'; 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; ?> <=')? 'div class="postbox closed"' : 'fieldset class="dbx-box"' )?> id="ljxpdiv"> +> :
=')?'class="inside"':'class="dbx-content"')?>> =')?'

':'')?>

=')?'

':'')?>
=')? 'div' : 'fieldset' )?>>