Forums

[Plugin: SEO Slugs] Setting post slug when posting via XML-RPC (6 posts)

  1. Waddle
    Member
    Posted 2 years ago #

    I'm new to both plugin development and XML-RPC.

    I'm currently trying to re-work the seo-slugs plugin so that it removes the stopwords from the post slug when I'm posting via XML-RPC. Currently seo-slugs works properly when posting using the dashboard, but when I try to post using XML-RPC the stopwords aren't trimmed from the slug. I'm using the metaWeblog.newPost call to post via XML-RPC using a very simple script.

    I've tracked the offending code to about line 2050 in the core wordpress file xmlrpc.php, inside the mw_newPost() method.

    $post_name = "";
    if(isset($content_struct["wp_slug"])) {
    	$post_name = $content_struct["wp_slug"];
    }

    We eventually need to get the trimmed slug value into $post_name, which is used later in the wp_insert_post() function around line 2235. It would appear to me that the easiest way to do that is to set the value of $content_struct['wp_slug'] via a filter or action hook. $content_struct is generated from the $args fed into mw_newPost().

    There is an action hook near the top mw_newPost():

    do_action('xmlrpc_call', 'metaWeblog.newPost');

    which I have been trying to hook in to so that I can modify $content_struct['wp_slug']. Here's the content of my modified version of seo-slugs with my attempt at a hook:

    // Add hook
    add_action("xmlrpc_call", add_slug, 0, 1);
    
    // This sets the slug
    function add_slug($args) {
    	global $content_struct, $wpdb, $args;
    
    	$slug = strip_slug($content_struct['title']);
    	$content_struct["wp_slug"] = $slug;
    
    	return $content_struct;
    
    }
    
    // Slightly modified version of original seo_slugs() function
    function strip_slug($slug) {
    
    	global $wpdb, $content_struct, $stopwords;
    
    	$seo_slug = strtolower($slug);
    
    	$seo_slug = preg_replace('/&.+?;/', '', $seo_slug); // kill HTML entities
        // kill anything that is not a letter, digit, space or apostrophe
        $seo_slug = preg_replace ("/[^a-zA-Z0-9 \']/", "", $seo_slug);
    
        // Turn it to an array and strip common words by comparing against c.w. array
    	$array = explode(" ", $seo_slug);
    	$array2 = array_diff($array, $stopwords);
    
        $seo_slug = join("-", $array2);
    	$seo_slug = ereg_replace(' ', '-', $seo_slug);
    
    	return "$seo_slug";
    }
    
    $stopwords = array(
    	'this',
    	'the',
    	'a',
    	// plus a whole list of stopwords
    
    );

    However, the problem I'm having is that content_struct isn't global outside the mw_newPost method and it can't be read or written to by add_slug(). If I edit xmlrpc.php manually to declare $content_struct to be global at the top of mw_newPost, then the code works and the slug is trimmed and saved correctly when posting via XML-RPC. However, I don't want to edit a core file, but would prefer to make a proper plugin so there are no problems with later updates to wordpress.

    What am I doing wrong? How do I make the value of $content_struct readable and writeable without editing xmlrpc.php? I have also read about adding filters to xmlrpc methods (like http://josephscott.org/archives/2008/11/adding-xml-rpc-methods-to-wordpress/ ) - should I be trying to do this instead?

  2. Joseph Scott
    Member
    Posted 2 years ago #

    Why aren't you using the wp_insert_post_data filter? That is called by wp_insert_post and should work no matter how the post is added.

  3. Waddle
    Member
    Posted 2 years ago #

    Thanks for you reply.

    I've delved into wp_insert_post and I think the best place to strip the stop words from the slug is about line 1650 in wp-includes/post.php.

    if ( !isset($post_name) || empty($post_name) ) {
    	if ( !in_array( $post_status, array( 'draft', 'pending' ) ) ) {
    		/* HERE IS THE SPOT WE NEED TO SET THE POST NAME */
    		$post_name = sanitize_title($post_title);
    }	else {
    		$post_name = '';
    	}
    } else {
    	$post_name = sanitize_title($post_name);
    }

    Within the sanitize_title() function in wp-includes/formatting.php, there is a hook I want to use:

    function sanitize_title($title, $fallback_title = '') {
    	$raw_title = $title;
    	$title = strip_tags($title);
    	$title = apply_filters('sanitize_title', $title, $raw_title);
    
    	if ( '' === $title || false === $title )
    		$title = $fallback_title;
    
    	return $title;
    }

    By setting the post slug here in sanitize_title, rather than later in wp_insert_post_data, it can be passed wp_unique_post_slug() later on so we can be assured of a unique post slug.

    This is my modified plugin code:

    add_filter("sanitize_title", strip_slug_san, 0);
    function strip_slug_san($title) {
    	global $title, $raw_title, $data, $post_title;
    	$title = strip_slug($title);
    	return $title;
    }

    However, the $title from sanitize_title() is not being passed to my strip_slug_san() function. I would have thought that it would be, considering that it's being called from within the sanitize_title() function. I feel I'm close, what am I doing wrong?

  4. Waddle
    Member
    Posted 2 years ago #

    I'm slightly further along and royally stuck.

    Here's the complete plugin code I'm using:

    add_filter("sanitize_title", strip_slug_san, 0);
    function strip_slug_san($title) {
    
    	$title = strip_slug($title);
    
    	return $title;
    }
    
    function strip_slug($slug) {
    
    	//global $wpdb, $content_struct, $stopwords;
    	global $stopwords;
    
    	$seo_slug = strtolower($slug);
    
    	$seo_slug = preg_replace('/&.+?;/', '', $seo_slug); // kill HTML entities
        // kill anything that is not a letter, digit, space or apostrophe
        $seo_slug = preg_replace ("/[^a-zA-Z0-9 \']/", "", $seo_slug);
    
        // Turn it to an array and strip common words by comparing against c.w. array
    	$array = explode(" ", $seo_slug);
    	$array2 = array_diff($array, $stopwords);
    
        $seo_slug = join("-", $array2);
    	$seo_slug = ereg_replace(' ', '-', $seo_slug);
    
    	return "$seo_slug";
    }
    
    $stopwords = array(
        'a',
        'the',
        'and',
        'etc'
    );

    I'm hooking into sanitize_title and the slug is being stripped of its stopwords successfully. However, when I click on any of the links to the posts on the index page, I get a 404. If I comment out
    $title = strip_slug($title);
    then posts are added normally (without the stop words being stripped) and can be retrieved normally.

    I've checked the wp_posts table and the post_name values are set correctly when I'm trimming the stopwords.

    Any ideas?

  5. Joseph Scott
    Member
    Posted 2 years ago #

    Sounds like a rewrite problem.

  6. Waddle
    Member
    Posted 2 years ago #

    I got it working. I abandoned trying to hook into sanitize_title, because it appears to be called multiple times per page and the scope was difficult. I tried using wp_insert_post_data and it works fine. Even if you make two different posts with the same title, the slugs are still appended with numbers correctly despite where wp_insert_post_data appears in the script.

    Here's my working code in case it helps anyone else trying to set post slugs using XML-RPC:

    add_filter("wp_insert_post_data", strip_slug_san, 0);
    function strip_slug_san($data) {
    //	global $data;
    
    	$slug = $data['post_name'] ;
    
    	$slug = strip_slug($slug);
    
    	$data['post_name'] = $slug;
    
    	return $data;
    
    }
    
    function strip_slug($slug) {
    
    	//global $wpdb, $content_struct, $stopwords;
    	global $stopwords;
    
    	$count = count($stopwords);
    
    	$seo_slug = strtolower($slug);
    
    	$seo_slug = preg_replace('/&.+?;/', '', $seo_slug); // kill HTML entities
    
        // kill anything that is not a letter, digit, space or apostrophe
        $seo_slug = preg_replace ("/[^a-zA-Z0-9- \']/", "", $seo_slug);
    
        // Turn it to an array and strip common words by comparing against c.w. array
    	$this_array = explode('-', $seo_slug);
    
    	$array2 = array_diff($this_array, $stopwords);
    
        $slug2 = implode("-", $array2);
    
    	return "$slug2";
    }
    
    $stopwords = array(
        'a',
        'the',
        'and',
        'etc'
    );

    Thanks for your tips Joseph.

Topic Closed

This topic has been closed to new replies.

About this Topic