\n

Archive for the Features & Bugs Category

The first post 2.3 IRC meetup just took place. It was a very constructive meeting in which we discussed a few of the things we want to accomplish for version 2.4.

A few of the proposed things that I like:

  • Implement the usage of $wpdb->prepare. (improves security)
  • Use jQuery for our AJAX (Asynchronous Javascript And XML) stuff
  • Admin interface redesign
  • Basic undo functionality
  • ...

A complete transcript of the meeting can be found here.

It's too early to really comment on any of the stuff, but I like the general direction.

Another article on a new feature in the soon to be released 2.3 version. This time we'll be talking about something new called Unfiltered upload.

Let's take the following scenario: We wan't to write an article about something (we'll use geocaching in this example) and want to suplement that post with a file (gpx in this case) that your users can download and use.
Our first step would be to go to the Write Post screen, start typing and upload that file using the build-in uploader:

Upload file

In the 2.2.x version (and earlier) when we press Upload we get the following result:

Upload file failed

Due to security reasons we're only allowed to upload a pre-defined list of file types. And that's a Good thing ™. You wouldn't want a Contributer for instance to be able to upload a PHP file and take over your blog, or worse, your machine. Looking at the 2.3 sources shows us that the following types of files are allowed:

$mimes = is_array($mimes) ? $mimes : apply_filters('upload_mimes', array (
	'jpg|jpeg|jpe' => 'image/jpeg',
	'gif' => 'image/gif',
	'png' => 'image/png',
	'bmp' => 'image/bmp',
	'tif|tiff' => 'image/tiff',
	'ico' => 'image/x-icon',
	'asf|asx|wax|wmv|wmx' => 'video/asf',
	'avi' => 'video/avi',
	'mov|qt' => 'video/quicktime',
	'mpeg|mpg|mpe' => 'video/mpeg',
	'txt|c|cc|h' => 'text/plain',
	'rtx' => 'text/richtext',
	'css' => 'text/css',
	'htm|html' => 'text/html',
	'mp3|mp4' => 'audio/mpeg',
	'ra|ram' => 'audio/x-realaudio',
	'wav' => 'audio/wav',
	'ogg' => 'audio/ogg',
	'mid|midi' => 'audio/midi',
	'wma' => 'audio/wma',
	'rtf' => 'application/rtf',
	'js' => 'application/javascript',
	'pdf' => 'application/pdf',
	'doc' => 'application/msword',
	'pot|pps|ppt' => 'application/vnd.ms-powerpoint',
	'wri' => 'application/vnd.ms-write',
	'xla|xls|xlt|xlw' => 'application/vnd.ms-excel',
	'mdb' => 'application/vnd.ms-access',
	'mpp' => 'application/vnd.ms-project',
	'swf' => 'application/x-shockwave-flash',
	'class' => 'application/java',
	'tar' => 'application/x-tar',
	'zip' => 'application/zip',
	'gz|gzip' => 'application/x-gzip',
	'exe' => 'application/x-msdownload',
	// openoffice formats
	'odt' => 'application/vnd.oasis.opendocument.text',
	'odp' => 'application/vnd.oasis.opendocument.presentation',
	'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
	'odg' => 'application/vnd.oasis.opendocument.graphics',
	'odc' => 'application/vnd.oasis.opendocument.chart',
	'odb' => 'application/vnd.oasis.opendocument.database',
	'odf' => 'application/vnd.oasis.opendocument.formula',
));

But I do want to attach that gpx file to that post. I know it's safe. So how can I do this? That's where unfiltered uploads come into play. Version 2.3 introduces a new capability which let's you upload any type of file you want. But for security reasons that capability is by default only given to users in the Administrators role. So if I try the previous as an Administrator in 2.3 I get the following screen:

Upload file succeeded

That's in short what the new unfiltered_upload capability does.

Please note that if you use a certain filetype often, but don't want to give the unfiltered_upload capability to somebody, try the WordPress mime-config plugin. It allows you to extend the default list of allowed file types.

Eventhough WordPress 2.3 hasn't yet been released to the general public at the moment I'm writing this, I'd like to do one or more posts on some of the new features that went into 2.3. Although tagging is propably the biggest new addition, everybody is already blogging about it, or will be. So I'll be concentrating one some smaller, but still very usefull new features.

In this post, that will be Importer Plugins. What are Importer Plugins? As most of you know, WordPress comes with a set of importers which can be used to import data from other formats/blogs into your blog. (Think DotClear, Blogger and many more) But there are many more systems out there which don't have importers that are build into WordPress. That's where plugins come in. A plugin can be used to extend the default WordPress behaviour and starting with 2.3 can also be used to add your own importer to WordPress.

So how do you create such a Importer Plugin? Let's build one step by step. We start with an empty plugin template. (For basic information on writing plugins I recommend reading the Codex Writing a Plugin article or this tutorial by Leonid Mamchenkov)

  1. <?php
  2. /*
  3. Plugin Name: Importer Plugin
  4. Version: 0.1
  5. Plugin URI: http://wordpress.nazgul.nu/
  6. Description: Example of an importer plugin.
  7. Author: Nazgul
  8. Author URI: http://nazgul.nu/
  9. */
  10.  
  11. /* Copyright 2007 Nazgul
  12.  
  13.   This program is free software; you can redistribute it and/or modify
  14.   it under the terms of the GNU General Public License as published by
  15.   the Free Software Foundation; either version 2 of the License, or
  16.   (at your option) any later version.
  17.  
  18.   This program is distributed in the hope that it will be useful,
  19.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21.   GNU General Public License for more details.
  22.  
  23.   You should have received a copy of the GNU General Public License
  24.   along with this program; if not, write to the Free Software
  25.   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  26. */
  27. ?>

So how do we turn this into an importer? We create a class which will do the importing for us. I based this class on the bundled importers, as they almost all use this form, but stripped out the fluff for now.

  1. // The importer
  2. class Test_Import {
  3. var $importer_code = 'importerplugin';
  4. var $importer_name = 'Importer Plugin';
  5. var $importer_desc = 'Import stuff';
  6.  
  7. // Function that will handle the wizard-like behaviour
  8. function dispatch() {
  9. if (empty ($_GET['step']))
  10. $step = 0;
  11. else
  12. $step = (int) $_GET['step'];
  13.  
  14. switch ($step) {
  15. case 0 :
  16. wp_import_upload_form('admin.php?import='.$this->importer_code.'&step=1');
  17. break;
  18. case 1 :
  19. check_admin_referer('import-upload');
  20. $this->import();
  21. break;
  22. }
  23. }
  24.  
  25. // Function that does the actual importing
  26. function import() {
  27. $file = wp_import_handle_upload();
  28. if ( isset($file['error']) ) {
  29. echo '< p>Sorry, there has been an error.< /p>';
  30. echo '< p><strong>' . $file['error'] . '</strong>< /p>';
  31. return;
  32. }
  33. $this->file = $file['file'];
  34. $this->id = (int) $file['id'];
  35.  
  36. // TODO: Write import code
  37. }
  38.  
  39. // Default constructor
  40. function Test_Import() {
  41. // Nothing.
  42. }
  43. }

What we see is a class with a constructor which does nothing, 2 functions and a couple of variables. Let's start with those variables. importer_code is the name which WordPress will be using to identify the importer internally. importer_name is the human readable name which will be shown on the Importer screen end importer_desc is the description which is displayed on that same page.

The importers are wizards. That means they can have multiple screens during various stages of the import process. That's where our dispatch function comes in. It shows the different screens based on the step in the wizard where you currently are. Currently the first step (0) shows an upload form and the second step does some security checking and runs the actual import by means of the import function.

So now that we have an importer, how do we let WordPress know it exists? We register it using the register_importer function. That function is part of wp-admin/includes/import.php which isn't loaded by default, so we'll have to do that as well.

  1. // Instantiate and register the importer
  2. include_once(ABSPATH . 'wp-admin/includes/import.php');
  3. if(function_exists('register_importer')) {
  4. $test_import = new Test_Import();
  5. register_importer($test_import->importer_code, $test_import->importer_name,
  6. $test_import->importer_desc, array ($test_import, 'dispatch'));
  7. }

We use include_once and function_exists because we don't want to kill the blog if the inclusion fails for some reason. (It will on pre 2.3 version because the import.php was located elsewhere) Next we instantiate our importer and register it, passing in our plugin code, name, description and dispatch function. That's all there is to building an Importer Plugin. I admit that this plugin doesn't actually import anything, but I'll leave that up as an exercise for the reader. :)

One last thing I'd like to include is some sort of protection. As stated, this behaviour is new to 2.3 and this plugin therefore won't work on older versions. Let's make sure people who try this plugin on older versions can't accidentally activate this plugin.

  1. add_action('activate_'.plugin_basename(__FILE__), 'importerplugin_activate');
  2.  
  3. function importerplugin_activate() {
  4. global $wp_db_version;
  5.  
  6. // Deactivate on pre 2.3 blogs
  7. if($wp_db_version<6075) {
  8. $current = get_settings('active_plugins');
  9. array_splice($current, array_search( plugin_basename(__FILE__), $current), 1 );
  10. update_option('active_plugins', $current);
  11. do_action('deactivate_'.plugin_basename(__FILE__));
  12. }
  13. }

You can download the complete sourcecode for the plugin here.

That's it for now. Stay tuned for more posts about new 2.3 features.