RESTful Web Services in a WordPress Plugin?

UPDATE (2011-04-15):

Since I wrote this post I’ve learned a tremendous amount about WordPress plugin development; so much so that I can’t overstate how much more I know today than when I wrote this post years ago.  So, while the following post might be a novelty to read, I highly recommend that you don’t use this approach.

The only way that a RESTful approach to web services in WordPress would make sense to me is if a team of rockstar WordPress plugin developers were to create a fully fleshed-out extension to WordPress that offered a complete RESTful web service implementation including one that addressed all edge cases and security concerns; only then I would consider not defaulting to the non-RESTful approach WordPress uses for AJAX.  

In summary I recommend not trying to swim upstream today and instead use the approach provided by WordPress. Who knows, maybe in the future there will be a viable method of doing RESTful web services in WordPress.


So I’ve got a project where I need to have a Flash component built in Flex to call to a WordPress blog and get information about it’s latest post. Should be no problem right?  The easy way to do this would be to just write a "rest.php" file and brute-force all the setup that WordPress does but I thought it would be so much more valuable to implement this as a plug-in.

I figured that I’d just quickly learn how to build a WordPress plug-in and create one for exposing RESTful web services; after all with a year of programming Drupal modules WordPress’ plug-in API can’t be that hard, right?  Well turns out it wasn’t that easy and I think I have run into a design limitation with WordPress and I’m beginning to wish I’d just taken the brute-force approach and said to hell with writing a plug-in.

Although I am not 100% certain, and I hope someone can point out that I’m just doing something wrong, it seems sadly like I’m pushing the edges of the WordPress API and exposing where it’s design falls short. By the way, I’m working with WordPress v2.5 because why upgrade mid-project when god knows if WordPress will release another in the remaining days before this project is done and I’ll just have to do again before deployment?

Here’s the details.  I started writing a plug-in called "RESTful Services" with a goal of implementing URLs that behave in the following fashion; {format} could potentially be html, xhtml, json, xml, rss, atom, etc.:

http://example.com/services.{format}
Provide a list of RESTful services in specified {format}, defaults to html
http://example.com/services/{service}.{format}/{data}?{params
Provide a RESTful service in specified {format}, defaults to html, with optional provided data and parameters.

But before I got all those options working I just wanted to service a page from my RESTful Services plugin where Content-Type: text/plain. I found this page that professes to explain how to hook into the URL routing and after a few fits and starts I can came up with the following code for my plugin that would indeed response to my http://example.com/services URL:

wp-content/plugins/restful-services/restful-web-services.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
add_action('init', 'restful_services_flush_rewrite_rules');
function restful_services_flush_rewrite_rules() {    
  global $wp_rewrite;
  $wp_rewrite->flush_rules();
}       
 
add_filter('generate_rewrite_rules', 'restful_services_add_rewrite_rules');
function restful_services_add_rewrite_rules( $wp_rewrite ) {      
  $new_rules = array(        
    'services' => 'wp-content/plugins/restful-services/rest.php',     
  );
  $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}

The problem with the above was that it wouldn’t call "wp-content/plugins/restful-services/rest.php"; it would simply continued to call "index.php" and display the home page!  After literally hours and hours of debugging with my trusty PhpEd IDE & debugger I was able to find that the code on lines 737 & 738 of "wp-includes/query.php" told WordPress that my service was the home page! It is almost seems like the "generate_rewrite_rules" was implemented as "a good idea" yet no testing has ever been done on it because for the best I can tell it doesn’t work. (Note I’ve reformatted the code to multiple lines so that it is easier to read and does not extend past the right margin of my blog):

wp-includes/query.php:

if ( !(  $this->is_singular                           
      || $this->is_archive                           
      || $this->is_search                           
      || $this->is_feed                           
      || $this->is_trackback                           
      || $this->is_404                           
      || $this->is_admin                           
      || $this->is_comments_popup ) )   
       $this->is_home = true;

I could possibly hack it to get past this by setting one of those to "true", but none of them are really appropriate; there is nothing there quite like an "is_service" instance variable. Setting something like "this->is_singular" or "this->is_feed" might work but it could manifest incompatibility problems with other plugins or future versions of WordPress. Frankly it was rather disappointing to discover this because it tells me that WordPress has hard-coded all the potential scenarios and doesn’t really have a way around it. Seems to me there should really be a hook here and the type of pages should be allowed to be expanded by plugins rather than be hardcoded as only one of ’singular’, ‘archive’, ’search’, ‘feed’, … and ‘home.’

Anyway, where this manifests itself is "wp-includes/template-loader.php" file which I have included in it’s entirety below.  It is on lines 24 and 25 where the template loader loaded the home page because it’s not possible to specify otherwise:

wp-includes/template-loader.php:

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
 * Loads the correct template based on the visitor's url
 * @package WordPress
 */
if ( defined('WP_USE_THEMES') && constant('WP_USE_THEMES') ) {
  do_action('template_redirect');
  $is_home = is_home() ;
  if ( is_robots() ) {
    do_action('do_robots');
    return;
  } else if ( is_feed() ) {
    do_feed();
    return;
  } else if ( is_trackback() ) {
    include(ABSPATH . 'wp-trackback.php');
    return;
  } else if ( is_404() && $template = get_404_template() ) {
    include($template);
    return;
  } else if ( is_search() && $template = get_search_template() ) {
    include($template);
    return;
  } else if ( is_home() && $template = get_home_template() ) {
    include($template);
    return;
  } else if ( is_attachment() && $template = get_attachment_template() ) {
    remove_filter('the_content', 'prepend_attachment');
    include($template);
    return;
  } else if ( is_single() && $template = get_single_template() ) {
    include($template);
    return;
  } else if ( is_page() && $template = get_page_template() ) {
    include($template);
    return;
  } else if ( is_category() && $template = get_category_template()) {
    include($template);
    return;
  } else if ( is_tag() && $template = get_tag_template()) {
    include($template);
    return;
  } else if ( is_tax() && $template = get_taxonomy_template()) {
    include($template);
    return;
  } else if ( is_author() && $template = get_author_template() ) {
    include($template);
    return;
  } else if ( is_date() && $template = get_date_template() ) {
    include($template);
    return;
  } else if ( is_archive() && $template = get_archive_template() ) {
    include($template);
    return;
  } else if ( is_comments_popup() && $template = get_comments_popup_template() ) {
    include($template);
    return;
  } else if ( is_paged() && $template = get_paged_template() ) {
    include($template);
    return;
  } else if ( file_exists(TEMPLATEPATH . "/index.php") ) {
    include(TEMPLATEPATH . "/index.php");
    return;
  }
} else {
  // Process feeds and trackbacks even if not using themes.
  if ( is_robots() ) {
    do_action('do_robots');
    return;
  } else if ( is_feed() ) {
    do_feed();
    return;
  } else if ( is_trackback() ) {
    include(ABSPATH . 'wp-trackback.php');
    return;
  }
}

Still another problem in this puzzle is the $wp->send_headers() method shown being called here on line 293 of "wp-includes/classes.php":

wp-includes/classes.php:

290
291
292
293
294
295
296
297
298
function main($query_args = '') {   
  $this->init();   
  $this->parse_request($query_args);   
  $this->send_headers(); 
  $this->query_posts();  
  $this->handle_404();   
  $this->register_globals();   
  do_action_ref_array('wp', array(&$this));   
}

The problem with the $wp->send_headers(), also from "wp-includes/classes.php", is that it seems to have the option of either serving an HTML content type on line 183 and 185, or a content type based on a feed (the content types for the feeds are set in their respective "wp-includes/feed-*.php" files) but no custom content types as far as I can determine as there seems to be no way to override calling this function or the logic path contained within:

wp-includes/classes.php:

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
function send_headers() {
  @header('X-Pingback: '. get_bloginfo('pingback_url'));
  if ( is_user_logged_in() )
    nocache_headers();
  if ( !empty($this->query_vars['error']) && '404' == $this->query_vars['error'] ) {
    status_header( 404 );
    if ( !is_user_logged_in() )
      nocache_headers();
    @header('Content-Type: ' . get_option('html_type') . '; charset=' . get_option('blog_charset'));
  } else if ( empty($this->query_vars['feed']) ) {
    @header('Content-Type: ' . get_option('html_type') . '; charset=' . get_option('blog_charset'));
  } else {
    // We're showing a feed, so WP is indeed the only thing that last changed
    if ( !empty($this->query_vars['withcomments'])
      || ( empty($this->query_vars['withoutcomments'])
        && ( !empty($this->query_vars['p'])
          || !empty($this->query_vars['name'])
          || !empty($this->query_vars['page_id'])
          || !empty($this->query_vars['pagename'])
          || !empty($this->query_vars['attachment'])
          || !empty($this->query_vars['attachment_id'])
        )
      )
    )
      $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT';
    else
      $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT';
    $wp_etag = '"' . md5($wp_last_modified) . '"';
    @header("Last-Modified: $wp_last_modified");
    @header("ETag: $wp_etag");
 
    // Support for Conditional GET
    if (isset($_SERVER['HTTP_IF_NONE_MATCH']))
      $client_etag = stripslashes(stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));
    else $client_etag = false;
 
    $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']);
    // If string is empty, return 0. If not, attempt to parse into a timestamp
    $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;
 
    // Make a timestamp for our most recent modification...
    $wp_modified_timestamp = strtotime($wp_last_modified);
 
    if ( ($client_last_modified && $client_etag) ?
         (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) :
         (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {
      status_header( 304 );
      exit;
    }
  }
 
  do_action_ref_array('send_headers', array(&$this));
}

Still, I was able to come up with a solution although it is so very hackish.  My solution was to hook the "template_redirect" action on line 7 of "wp-includes/template-loader.php" (see code from that file above.) Though it seems to works thus far, my solution just feels wrong for the following reasons:

  1. It ignores the fact that WordPress continues to think that my web service URL is the home page,
  2. It first lets "$wp->send_headers()" set the content type before it overrides it,
  3. It uses an "exit" rather than a return to keep WordPress from serving up the home page template, and
  4. It doesn’t use the routing mechanism apparent built into WordPress (see "null" on line 30 of "wp-content/plugins/restful-services/restful-web-services.php" below, I assume it should have been the URL of the .php file I plan to execute but WordPress doesn’t see to use what I put there.)

The function "restful_web_services_exec_service()" is what is called to execute the appropriate web service:

wp-content/plugins/restful-services/restful-web-services.php:

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*
Plugin Name: RESTful Web Services
Plugin URI: http://mikeschinkel.com/wordpress/restful-web-services/
Description: This plugin enables REST-based API web services for WordPress.
Author: Mike Schinkel
Version: 0.1
Author URI: http://mikeschinkel.com/
*/
 
define('RESTFUL_WEB_SERVICES_DIR', dirname(__FILE__));
define('RESTFUL_WEB_SERVICES_URL_PATTERN','services(/?.*)?');
$abspath = trim(str_replace('\\','/',ABSPATH),'/');
$rest_services_dir = str_replace('\\','/',RESTFUL_WEB_SERVICES_DIR);
$rest_services_path = trim(str_replace($abspath,'',$rest_services_dir),'/');
define('RESTFUL_WEB_SERVICES_PATH', $rest_services_path);
 
// NOTE, See: http://codex.wordpress.org/Custom_Queries#Permalinks_for_Custom_Archives
add_action('init', 'restful_web_services_flush_rewrite_rules');
add_filter('generate_rewrite_rules', 'restful_web_services_add_rewrite_rules');
add_action('template_redirect', 'restful_web_services_exec_service');
 
add_action('init', 'restful_web_services_flush_rewrite_rules');
function restful_web_services_flush_rewrite_rules() {
 global $wp_rewrite;
 $wp_rewrite->flush_rules();
}
 
add_filter('generate_rewrite_rules', 'restful_web_services_add_rewrite_rules');
function restful_web_services_add_rewrite_rules( $wp_rewrite ) {
  $new_rules = array(
    RESTFUL_WEB_SERVICES_URL_PATTERN => null,
  );
  $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}
 
function restful_web_services_exec_service() {
  global $wp;
  if ($wp->matched_rule==RESTFUL_WEB_SERVICES_URL_PATTERN) {
    if ($wp->request == 'services') {
      header('Content-Type: text/plain');
      print 'TODO: Generate a list of RESTful Web Services here for this WordPress Blog.';
    } else {
      list($dummy,$service_name) = explode('/',$wp->request);
      if (file_exists($service_php = (RESTFUL_WEB_SERVICES_DIR . '/services/' . $service_name . '.php'))) {
        include_once $service_php;
      } else {
        header('Content-Type: text/plain');
        status_header(404);
        print '404 - Service not found.';
      }
    }
    exit;
  }
}

You’ll note that my function "restful_web_services_exec_service()" is very bare-bones at the moment serving only a plain text message "TODO:" for the path http://example.com/services, and assuming that any path http://example.com/services/{service} will execute a same-named .php file in the services subdirectory, i.e. for http://example.com/services/latest-post it will look for "wp-content/plugins/restful-web-services/services/lastest-post.php" and then delegate all the work to that .php file.

wp-content/plugins/restful-services/services/latest-vidclip.php:

2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
Filename: latest-post.php
Service Name: Latest Post
*/
global $wp_query;
$post = $wp_query->post;
$file = get_attached_file($post->ID);
if (empty($file)) {
  list($file)= get_enclosed($post->ID);
}
$charset = get_option('blog_charset');
header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
$link = get_permalink($post->ID);
$html = &lt; &lt;<post></post><post id="{$post-&gt;ID}">      <video>$file</video>   <link />$link  </post>  POST; print $html;

Here is an example output returned by calling http://example.com/services/latest-post:

1
2
&lt; ?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; 
<post id="2">      <video>http://videos example.org/video1.flv</video>   <link />http://example.org/sample-post-1/  </post>

While bare-bones, that will meet my needs for the moment since I really only have the need for one service that responds to the HTTP GET verb. However, I can see where I might end up fleshing this out and create lots of helper functions that would streamline the creation of web services for RESTful access to the entirety of the WordPress database. If I do so I’ll be happy to donate this plugin to the community.  In the mean time if you’d like to use this for your own use feel free but caveat emptor. However, if you’d like to use this code to create a plugin to contribute to the community, please contact me for collaboration.  Of course if you’d like to retain me to fully flesh it out for you, I’m always open to that too! :-)

Finally, if anyone who knows the WordPress API better than I do can tell me where I erred in my analysis, and I really hope I did, please let me know so I can architect this thing better. On the other hand, if I was spot-on in my analysis maybe this will help the WordPress team understand what needs to be done so they can empower WordPress to generate any arbitrary content type without having to resort to hacks or bypassing the WordPress index.php startup code. 

37 comments ↓

#1 mattyH on 10.03.08 at 5:51am

Hey Mike, have you seen the add_feed() function in the wp-includes/rewrite.php file? It seems like a way to add a feed like the default rss or rss2.

#2 Erik Rapprich on 02.26.09 at 12:06pm

Mike,

Thanks for this post. I’ve written a whole slew of WP plugins but never anything that plays with http. Anyway, I’m working on a dashboard widget plugin that displays updates via CURL on the dashboard and I had everything working individually but when I tried to load the request through the plugin I kept getting a “headers already sent” error. I think, I got now, thanks to your post.

Good work!

#3 Jamie Gaines on 03.27.09 at 11:21am

Hi, Mike.

I found your post while trying to figure out how to write my own plug-in that would allow me to expose new RESTful services in WP. I did a bit more searching and came across this gem written a few months after your post by Joseph Scott.

http://josephscott.org/archives/2008/11/adding-xml-rpc-methods-to-wordpress/

I haven’t tried this myself yet but it seems like it was the hook you were looking for.

#4 MikeSchinkel on 03.31.09 at 8:18pm

Hi Jamie:

Thanks for the comment. The Joseph Scott article is a good one. OTOH, I want to point out that “RPC” is anathema to those of us who appreciate RESTful architectures. Just google “REST vs. RPC” to read more about it. Better yet, pick up a copy of “RESTful Web Services” as it covers the differences in depth.

-Mike

#5 Joseph Scott on 04.08.09 at 5:28pm

I’ll skip the stab at XML-RPC for now and simply ask why you didn’t use the existing AtomPub API? Most REST fans usually hold that up as the model.

#6 MikeSchinkel on 04.08.09 at 6:02pm

@Joseph Scott:

Thanks for the comment. Why don’t I use AtomPub API? Because I view using it for most webservices as using a sledgehammer to swat a gnat; it’s hard to do, you get tired very quickly, and the gnat almost always gets away.

Much like we all entered the era of SOAP with high hopes, most of us have come to realize that it was way overarchitected and got it the way far more often then it helped. Similarly I’ve felt that the AtomPub initiative has been founded on “a great idea at the time” but in practice its overkill most of the time. I’m also seeing some of the “powers that be” in the REST community backing away from it as a panacea, at least the ones whose reputations are not too attached to the success of AtomPub.

For example Tim Bray writes [1] “but many REST converts see complicated magic where I see a few simple easy-to-understand virtues” and [2] “I’m increasingly inclined to believe that there is a large class of simple administrative-CRUD apps where you can send those name-value pairs upstream, and the advantages may make it worthwhile living with both fighting the i18n fires and a certain loss of RESTafarian purity.” Then there is Joe Gregorio [3] who writes “I’m not 100% in agreement with Tim, but I’m a lot closer than I was a few years ago….On a simpler alternative to an Atom body.”

Frankly, after much pondering and a reasonable amount of implementation I’ve come to the conclusion that one of the best content types for GET is “application/json” and one of the best content types for POST and PUT is “application/x-www-form-urlencoded.”

I think we should follow one overridding principle whenever possible: “Simpler is almost always better.”

-Mike
P.S. I will say though that compared to SOAP, REST+AtomPub is light years better.

[1] http://www.tbray.org/ongoing/When/200x/2007/04/30/REST-is-easy
[2] http://www.tbray.org/ongoing/When/200x/2009/01/29/Name-Value-Pairs
[3] http://bitworking.org/news/405/webhooks

#7 Joseph Scott on 04.08.09 at 7:18pm

Simplicity is one of the reasons that I like XML-RPC (aside from the fact that it’s already there and works). Using XML for the markup (instead of say JSON, which would be much nicer) makes it more verbose than needed, but I like that it only needs a few simple concepts to make it work. There’s an XML-RPC library for just about everything under the sun, making it easy to let the library deal with the encoding and focus just on the app and logic.

I’ve looked at the JSON-RPC options, which looks appealing, but isn’t likely to get much traction this late in the game I’m afraid.

#8 MikeSchinkel on 04.08.09 at 7:33pm

There are so many reasons I prefer REST over (any)-RPC but it’d have to write a book to do the subject justice. OTOH, since someone already has written a book on it (O’Reilly’s RESTful Web Services) I’ll defer to it. Hopefully you’ll consider adding it to you backlog and come back to discuss after you’ve read their arguments for REST and against RPC?

#9 juust on 05.03.09 at 6:55pm

Pardon my ignorance, Mike, do you have that search engine friendly .htaccess rule that reroutes all requests to index.php ? If that were so, that would explain a lot ;)

#10 MikeSchinkel on 05.03.09 at 7:03pm

@juust - Actually, yes. That’s by and large how WordPress works; it routes most PHP requests through index.php.

#11 juust on 05.04.09 at 3:54am

If you want to write it as plugin, without modifying .htaccess or hacking into the codebase, the only option I can think of is adding the service endpoint file to your template folder as page template :
http://www.wordpressmax.com/customize-wordpress/custom-page

And then adding the services as pages, based on the service broker template. That way wordpress handles them through ‘is_single’, and has the template handle the request. As long as you dont call get_header(); in the template you can dish out any filetype or data :
as poc : http://www.juust.org/index.php/services/?data=test

#12 PB on 05.27.09 at 1:38am

Hey Mike, have you seen the add_feed() function in the wp-includes/rewrite.php file? It seems like a way to add a feed like the default rss or rss2.

#13 Futile » links for 2009-10-03 on 10.03.09 at 12:30pm

[…] RESTful Web Services in a WordPress Plugin? | MikeSchinkel.com (tags: development web wordpress WebService rest) […]

#14 R'phael Spindel on 04.21.10 at 12:12am

This was a great start in the right direction. I just got this all figured out. This is the appropriate way to implement a web service api in Wordpress:

function add_my_rewrite_rules() {
global $wp_rewrite;
$my_non_wp_rule = array
(
’services/(.*)\.(.*)/(.*)$’ => sprintf( ‘%s?action=my_service_handler&service=$1&format=$2&params=$3′, ‘wp-admin/admin-ajax.php’ )
);
$wp_rewrite->non_wp_rules = array_merge( $my_non_wp_rule, $wp_rewrite->non_wp_rules );
}
add_action( ‘generate_rewrite_rules’, ‘add_my_rewrite_rules’ );

function my_service_handler() {
$service = $_REQUEST[’service’];
$format = $_REQUEST[’format’];
$params = parse_str( $_REQUEST[params’] );

// any service logic here

// set the output content type by sending the proper html header for content type requested
header( ‘Content-Type: text/xml’ ); // for xml

// send my service api response based on format choosen
echo $my_service_response_xml->asXML(); // example for xml

exit(); // seems to leave a trailing status code if not exited but not the home page!
}

This should solve all of your concerns mentioned in the method you proposed.

#15 R'phael Spindel on 04.21.10 at 12:16am

If you have any other questions about my solution, I would be happy to discuss in more detail.

#16 MikeSchinkel on 04.23.10 at 6:33pm

@R’pael: Thanks for the comment.

Yeah, I think my way shown is not the way WordPress normally implements it. I was coming from a RESTful perspective new to WordPress but since writing this I’ve been using WordPress a lot and would do it differently. BTW, FWIW, I don’t like exactly how WordPress does it but I’d rather be compatible with the defacto-standard approach even if it has warts.

So thanks again for your comment.

#17 Luca on 05.19.10 at 5:55am

Hi did this evolve in a downloadable plugin for WP? Unfortunately I have not a clue on how to implement this but I really need to make a call to a Web Service to and display the returning xml file.

#18 panorama on 05.21.10 at 12:00am

There’s an XML-RPC library for just about everything under the sun, making it easy to let the library deal with the encoding and focus just on the app and logic.

thanks MikeSchinkel

#19 Rennell on 06.24.10 at 10:31pm

Mike,

I was reading your post about the macbook. Are you insane? You’re the first person to speak negatively about apple. I didn’t get a chance to finish the rest of the post because my eyes started hurting. “Dissing” the mac is like speaking disrespectfully towards Tupac or Elvis.

#20 MikeSchinkel on 06.24.10 at 11:17pm

@Rennell - Good gosh, I love your sarcasm!

But why not comment on that post instead of this?

#21 José P. Airosa on 07.14.10 at 4:11pm

Hello Mike,

Nice work you have there.

I’ve also been involved in developing a plugin for wordpress to enable REST API functionality.

Would love to have some feedback on your behalf.

http://wordpress.org/extend/plugins/wp-restful/

Thanks and keep up the great work.

#22 MikeSchinkel on 07.16.10 at 4:11am

Jose: Looks like a really awesome plugin, thanks for sharing. Hope I have time to play with it in the near future.

#23 Link su WordPress 4 December 2011 » » WordPress ItalyWordPress Italy Blog on 12.03.11 at 11:03pm

[…] RESTful Web Services in a WordPress Plugin? | MikeSchinkel.com […]

#24 Rich on 02.04.13 at 11:26am

Im having a similar problem now! How would you suggest I handle it. Im using the template_redirect to recognize the pattern and send to my plugin, but its still reading as a wordpress 404.

#25 MikeSchinkel on 02.05.13 at 8:41pm

Hi @Rich,

I wrote this article back in 2008; almost 5 years ago! So much I’ve learned since then so reading this post is painful! As of end of 2012 I am now blogging much better techniques here: http://hardcorewp.com.

Without looking at your code I’m wondering if you remembered to include an exit; immediately after you’ve output your representation?

#26 NoobMe on 10.22.13 at 8:12pm

I have this codes for wordpress

add_action(’generate_rewrite_rules’, ‘cs_rewrite_rules’);
add_filter(’init’, ‘eduFlush’);
function cs_rewrite_rules() {
global $wp_rewrite;
$new_non_wp_rules = array(
‘^generator/?$’ => ‘educontent/wp-content/plugins/wordwork/admin/templates/tcpdf/samp/flashcards.php’,
);
$wp_rewrite->non_wp_rules += $new_non_wp_rules;
}

function eduFlush(){
global $wp_rewrite;
$wp_rewrite->flush_rules();
}

i need to rewrite the url in wordpress but its not working.,. Here are my questions
1. Where I should put this codes? I tried to put it on the top of my php codes but its not working.
2. Is my codes correct?

Thx sir .. :)

#27 mikeschinkel on 10.22.13 at 10:19pm

Hi @NoobMe,

Thanks for your comment and your code. However, you don’t really explain what you are trying to accomplish. I can guess by looking at your code but since your code is not working then I will probably guess wrong. Please explain in English what you want to achieve.

-Mike

#28 NoobMe on 10.22.13 at 10:37pm

I need to change this url

educontent/wp-content/plugins/wordwork/admin/templates/tcpdf/samp/flashcards.php

to

educontent/generator

I successfully made it work by using .htaccess, but I want to do this without using htaccess, so I used wp_rewrite in wordpress. But I cant make it work.

#29 mikeschinkel on 10.22.13 at 11:27pm

Hi @NoobMe,

If you use WordPress’ URL routing it will do a lot of work that you don’t need before it loads /flashcards.php. Actually no matter what you do WordPress will do more work than you need so modifying .htaccess is the best way from a server perspective, but not the best way if you are trying to distribute a plugin.

For what you want to do I much prefer to use the ‘do_parse_request’ filter hook which allows you to see the URL before WordPress inspects it, and if it’s the URL you want then you can simple load the file you want.

I’ve created a Gist that has the working code I tested. The code needs to be put in the root directory of the plugin, i.e. in the same directory as the /admin/ subdirectory:

https://gist.github.com/mikeschinkel/7112588

Hope this helps.

-Mike

#30 NoobMe on 10.23.13 at 1:34am

Hi sir, I added a educontent-generator-url.php on the root folder of my plugin, but it still redirect to this url

educontent/wp-content/plugins/wordwork/admin/templates/tcpdf/samp/flashcards.php

I have form that redirect to the url above that submit data and generate a PDF.

$site_title = get_bloginfo( ‘wpurl’ );
$pdfmaker_path = $site_title.’/wp-content/plugins/wordwork/admin/templates/tcpdf/samp/flashcards.php’;

‘;

I just shortened the codes.
1st is for the dynamic path of the page
2nd is for the form that will submit the data to another page.
Im sorry for my english sir.

#31 NoobMe on 10.23.13 at 1:41am

Im not sure if I will create a new php page or just add the codes you made to my existing php page. I did tried both but still displaying the same url

educontent/wp-content/plugins/wordwork/admin/templates/tcpdf/samp/flashcards.php

#32 Mike Schinkel on 10.23.13 at 8:14am

Hi @NoobMe,

I don’t understand what you are describing. If you have a form and it is “redirecting” (?) to the URL, you should be using the new URL in your code: educontent/generate

If that is not it, maybe it would be better if you either create a Gist for me with the code for your plugin (if it is in a single file) or zip it up and send to me so I can see it.

-Mike

#33 NoobMe on 10.23.13 at 7:26pm

I’ll just create a sample code and get back to you thx.

#34 NoobMe on 10.23.13 at 7:47pm

Hi this is the gist https://gist.github.com/anonymous/7129407
in this example, it just passed the value of the textbox to another page,

origpage.php to targetpage_is_very_long.php

What we need to do is to rewrite the long url.. to just lets say
targetpage.php
Im sorry for my bad english, I hope you get what I’m saying.

#35 NoobMe on 10.23.13 at 9:39pm

Hi Mike I’ll explain what I did.
1. I added a file called educontent-generator-url.php and place your codes there.
2. I saved the the educontent-generator-url.php to the root folder of the plugin.
3. On the form where in the
“educontent/wp-content/plugins/wordwork/admin/templates/tcpdf/samp/flashcards.php”
is called I changed it to educontent-generator-url.php
so it looks like this

form action=”educontent-generator-url.php” method=”POST” enctype=”multipart/form-data” target =”blank” id=”flashcontent” onsubmit=”return check_submit(this);”>’;

4. After submitting, i only get this error

Fatal error: Call to undefined function add_filter() in C:\xampp\htdocs\educontent\educontent-generator-url.php on line 7

I hope I explain it well XD Thx again for your help

#36 mikeschinkel on 10.23.13 at 11:04pm

@NoobMe - Your Gist did not provide the complete plugin. If you want help you need to provide me the complete plugin otherwise I have to guess at what you are trying to do, and it’s taking a lot of my time to do that.

However, the error you get about undefined function add_filter() tells you that WordPress is not loaded when you run my code. Why is your form action educontent-generator-url.php? If you do that of course it won’t work, and it’s not what you are trying to accomplish anyway. Your form’s action should be /educontent/generator.

If that does not help then email me a ZIP file of your complete plugin so I can see exactly what you are doing wrong.

-Mike

#37 NoobMe on 10.23.13 at 11:12pm

ok i’ll send you a zip file in your email.

Leave a Comment