Ransford Okpoti's Dev Notes

August 14, 2009

A hack to automatically upgrade all WordPress plugins with a single click

Filed under: Javascript,PHP,WordPress — Tags: , , — admin @ 1:45 pm

WordPress rocks!! I love wordpress as much as I love to watch Friends,one of my favourite television series courtesy ViaSat 1. You care to know who my favourite character is? Hmm, am not telling you. Lets hit the road running now. As a blogging tool, wordpress has created a niche for itself among it peers. It has great features like assigning an entry, or a post, to multiple categories, has a nice tagging system and recently, you can actually upgrade/update your WordPress Plugins automatically without the need to download the plugin and extract it into the plugins folder of your wordpress installation. The plugins upgrade indicator which displays the number of plugins that need upgrading is a pretty cool enhancement, so you don’t go looking for which plugins have latest releases you are not aware of.

Despite the fact that it is a great tool, upgrading a plugin can be a real pain in the a** of users since you need to click on the upgrade automatically link , depending on your chosen translation, for each of the plugins that need upgrading. 20 clicks for 20 different plugins that need upgrading to give you a clearer picture. So I posed to question to myself “Couldn’t this process have been made a lot easier on the user?“. And obviously, the answer was a “YES!!, it can.”

A WordPress admin console showing the need to click on upgrade automatically link for each installed plugin

A WordPress admin console showing the need to click on upgrade automatically link for each installed plugin

WHAT WE WANT TO ACHIEVE

We want to select the plugins to be upgraded, using the checkboxes provided beside each plugin, from the plugins page in the admin console, and choose Upgrade from the list of actions under the Bulk Actions menu, and click on the Apply button to upgrade all the selected plugins that need upgrading.

HACKING WORDPRESS

Since we don’t want to mess with the codes in plugins.php too much, we’ll make use of javascript and a little bit of PHP to achieve our task by doing the following:

  • Adding the Upgrade option to the list of Bulk Actions
  • Attaching a click event listener to the Apply action/button.
  • Creating a JSON (Javascript Object Notation) object to contain the plugins that requires upgrading.

Here is the code that does the trick.

<script type="text/javascript">
/*<![CDATA[*/
var json = {
	<?php
	if ( ! empty($upgrade_plugins) ){
		$url = 'update.php?action=upgrade-plugin&amp;plugin=';
		$noUpgradePlugins = count($upgrade_plugins);
		$i = 0;
		foreach($upgrade_plugins as $plugin=>$v){
			$i++;
			$actionurl = $url.$plugin;
			$action = 'upgrade-plugin_'.$plugin;
			$pluginurl=  str_replace( '&amp;', '&amp;', urldecode(wp_nonce_url($actionurl, $action)));
			$pluginId = str_replace('-','', substr($plugin,0,strpos($plugin,'/')));
	?>
			"<?php echo $pluginId ;?>": "<?php echo $pluginurl ;?>" <?php if($noUpgradePlugins>$i) echo ",\n";?>
	<?php	}
	}
	?>
}

/*
the 1 and 2 at the end of the some of the variable names represents
the top and down instances of the component respectively
*/
var btnApply1 = document.getElementsByName('doaction_active')[0];
var btnApply2 = document.getElementsByName('doaction_active')[1];

var cbo1 = document.getElementsByName('action')[0];
var cbo2 = document.getElementsByName('action2')[0];

// This adds the Upgrade option to the list of bulk actions
cbo1.options.add(new Option("Upgrade","upgrade-selected"));
cbo2.options.add(new Option("Upgrade","upgrade-selected"));

btnApply1.onclick = function(){
	if(cbo1.value!='upgrade-selected'){
		return true;
	}

	upgradePlugins();
	return false;
}

btnApply2.onclick = function(){
	if(cbo2.value!='upgrade-selected'){
		return true;
	}

	upgradePlugins();
	return false;
}

// Upgrades only the checked plugins that have upgrades/updates available
function upgradePlugins(){
	var tags = document.getElementsByName('checked[]');

	for(var i=0; i<tags.length; i++){
		if(tags[i].checked){
			p = tags[i].value;
			pluginId = p.substring(0,p.indexOf('/'));
			pluginId = pluginId.replace(/-/g,''); // replaces all instances of -

			url='';
			try{
				url = eval('json.'+pluginId);
			}
			catch(e){}

			if(typeof url=='string' &amp;&amp; url.length>0){
				var frame = document.createElement('frame');
				frame.src = url;
				document.getElementsByTagName('head')[0].appendChild(frame);
			}
		}
	}

}
/*]]>*/

</script>

All you need to do now is to copy the above codes and paste it at the bottom of the plugins.php file located in the wp-admin folder in your preferred editor. Save the changes, and refresh your plugins page, and give it a try.

HOW IT WORKS

WordPress automatic upgrade hack in action

WordPress automatic upgrade hack in action


On the plugins page, select Upgrade from the Bulk actions list, check the plugins you want to upgrade and click on the Apply button.

CONCLUSION

This demonstrates a practical application of DOM manipulation using Javascript. The downside of our little practical javascript solution to this problem is that each time you update WordPress itself, the plugins.php file will be replaced with the one from the latest version of WordPress.
I hope it was helpful.

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Twitter

May 26, 2009

how to use PHP’s __autoload function

Filed under: PHP — admin @ 6:36 am

Classes used in this posting may refer to classes or interfaces where appropriate.

Ever wondered if there could be a more appropriate way to include dependent files into other PHP files other than using multiple include or require language constructs? Well, wonder no more, the __autoload magic function in PHP5 comes to the rescue. In simple words, this method/function gets invoked anytime the execution reaches a line containing a class or an interface for which the interpreter does not know of it’s existence either because it has not been defined in the same file or it’s been defined in a separate file ,but has not been included in the current executing script/file.

The __autoload function allows for the lazy loading of classes, the classes only gets loaded when they are needed.

Lets look at some examples. In the code below, the __autoload function will never be called because there is only one class which has been defined within the file itself, so the interpreter knows of its definition.

<?php
function __autoload($class){
    echo 'Loading Interface/Class with name '.$class;
}

class Address{
}
?>

With this second example, the PHP interpreter does not known the definition of the interface IAddress, so it calls the __autoload function passing it the name IAddress, but since the function does not do anything yet except output the string Loading Interface/Class with name IAddress , and raises the error:

Interface ‘IAddress’ not found in path/to/script

<?php
function __autoload($class){
    echo 'Loading Interface/Class with name '.$class;
}

class Address implements IAddress{

}
?>

If the definition of the interface IAddress was in a different file say IAddress.php and in the same directory as our current script, we would have had the following code below.

<?php
include_once 'IAddress.php';
class Address implements IAddress{

}
?>

Lets look at another script which requires several classes defined in the classes directory.

<?php
include_once 'classes/Address.php';
include_once 'classes/Person.php';
include_once 'classes/ShoppingCart.php';

$addr = new Address();
?>

From the above code, it should hit you that if your script needs several classes/interfaces, that have rightly been, defined in their own separate files, then you’ll have to manually include all of them, so you could end up with 15 or 20 or more lines of include_once/require_once in your code. This definitely doesn’t sound exciting, you could argue that you could add the classes directory to your include path using the set_include_path function, but it still doesn’t take away the fact that you’ll have to include the class files using any of the file inclusion statements.

Now, lets solve the simple problem above before moving to a more complex scenario.

To quickly solve this, all we do is to remove all the include/require lines from the above code and add the __autoload function.

<?php
function autoLoader($class_name){
    include_once('classes/'.$class.'.php');
}

spl_autoload_register('autoLoader');

$addr = new Address();
?>

For this to work, the class name should lead us to easily predict the name of the file to be included i.e., the class name must be part of the file name. So, a class named Address stored in
address.php,
address.class.php,
address.inc.php or
class.address.php
is more helpful than storing it in some_unrelated_name.php .

This works perfectly now, but it won’t work in all scenarios because

1. All classes may not be in a single directory called classes, unless you decide to do it so.

2. All classes may not be named in this format: class_name.php eg. Address.php, Person.php, etc. What happens if there are mixed class naming formats like Address.class.php, class.Address.php, Address.inc.php or any other format that the developer(s) of the class used.

While we are looking at situations that might break our seemingly wonderful solution to the class file inclusion nightmare, lets look at a class name like Zend_Log, Log_display or Archive_Tar, you can quickly take a look at the class names in the PEAR package/directory in relation to the directory they are defined in. The naming convention adopted was to solve the problem of class name clashes. The naming convention is: [directory_]+class
where the + implies that there must be at least one directory before the class name.
Here, the naming gives information about the directory that contains the actual class eg. here are some class names and their implied locations

Zend_Log -> Zend/Log.php

Calendar_Month_Weekdays -> Calendar/Month/Weekdays.php

Log_display -> Log/display.php

To solve this, we define our own autoloader function to try and load the required classes/interfaces. The code is shown below. Lets name the script autoloader.php

<?php
/**
 *
 * @param string $className Class or Interface name automatically
 *              passed to this function by the PHP Interpreter
 */
function autoLoader($className){
    //Directories added here must be relative to the script going to use this file
    $directories = array(
      '',
      'classes/'
    );

    //Add your file naming formats here
    $fileNameFormats = array(
      '%s.php',
      '%s.class.php',
      'class.%s.php',
      '%s.inc.php'
    );

    // this is to take care of the PEAR style of naming classes
    $path = str_ireplace('_', '/', $className);
    if(@include_once $path.'.php'){
        return;
    }

    foreach($directories as $directory){
        foreach($fileNameFormats as $fileNameFormat){
            $path = $directory.sprintf($fileNameFormat, $className);
            if(file_exists($path)){
                include_once $path;
                return;
            }
        }
    }
}

spl_autoload_register('autoLoader');
?>

Now, all we have to do is to include autoloader.php into any of the scripts what will be making use of other classes/interfaces.

<?php
include_once 'autoloader.php';

$addr = new Address();
$shoppingCart = new ShoppingCart();
$person = new Person();

$person->setAddress($addr);
$person->setShoppingCart($shoppingCart);

?>


Download autoloader.php

Resources:
http://www.php.net/autoload

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Twitter

October 23, 2008

Smarty Templating Overview

Filed under: PHP,Smarty — Tags: , , — admin @ 12:39 pm

Smarty is one of the most popular PHP templating engines. There are many other templating engines, based on similar principles, such as

  • PHPTemplate
  • PHPTAL (TAL Template Attribute Language), phptal.motion-twin.com/
  • Savant, http://phpsavant.com/
  • FastTemplate
  • PHPlib

Irrespective of the templating engine that one uses, one of the design goals is the separation of business logic from the presentation logic. A point of caution, you can still use Smarty, the wrong way, without the clear separation of the business and presentation logic since Smarty does not enforce the separation of the 2 logics.

Business logic basically has to deal with how you solve the problem at hand, such as the computation of an employee’s monthly salary using the various parameters required in a typical payroll application.

Presentation logic, as the name suggests, deals with the logic pertaining to the presentation of data/content only. Examples of presentation logic are: iterating through an array of data to be displayed, picking the first X number of characters to display such as displaying a news or an article summary, alternating row colours of tables, etc.

Simply put, a templating engine replaces placeholders with values you specify.
In a real world web development project, there are going to be 2 main teams working on the project namely:
The developers/programmers who uses the full power of the programming language to achieve the requirements of the system such as accessing data from a database or a web service, validating and process the data, and persisting or presenting the processed data if required.
Designer or user interface (UI) team which builds the interface the users of the application would interact with using xHTML and it’s various controls or any appropriate UI technology suitable for the project at hand without affecting the developers codes.

SMARTY INSTALLATION

  1. Grab a copy of the smarty templating engine from http://smarty.net/download.php, as at the time of writing this article the latest stable version is 2.6.19. Extracting the content of the downloaded compressed file gives you this structure:
    Smarty-2.6.19/
      demo/
      lib/
      misc/
      unit_test/
      …
  2. Create a lib folder in your site where all libraries will be kept.
  3. Within the lib folder create a folder with the name smarty and copy the libs folder of the extracted file here.
  4. Now, create these 4 folders
    • templates, all template files, normally with the extension .tpl, goes into this folder
    • templates_c, compiled versions of all the templates will automatically be place here for you by smarty
    • configs, configuration files goes here
    • cache, directory/folder for cache files

After the installation, the directory structure for your project/site must be something like this

root_of_site/
  cache/
  configs/
  lib/
  smarty/
  libs/
  templates/
  templates_c/

After the installation, it is recommended that you subclass the Smarty class itself as this gives you the chance to modify some of the default settings of smarty and apply project specific settings here. Now, lets create a file with the name CustomSmarty.class.php in the site’s root folder ie. /root_of_site/CustomSmarty.class.php

<?php
    set_include_path( get_include_path().PATH_SEPARATOR.'./lib');
    require_once('Smarty/libs/Smarty.class.php');
    if(!defined('REQUIRED_SMARTY_DIR')) define('REQUIRED_SMARTY_DIR','./');	

    class CustomSmarty extends Smarty{

        function __construct(){
            $this->Smarty();

            /*
            You can remove this comment, if you prefer this JSP tag style
            instead of the default { and }
            $this->left_delimiter =  '<%';
            $this->right_delimiter = '%>';
            */

            $this->template_dir = REQUIRED_SMARTY_DIR.'templates';
            $this->compile_dir 	= REQUIRED_SMARTY_DIR.'templates_c';
            $this->config_dir  	= REQUIRED_SMARTY_DIR.'config';
            $this->cache_dir   	= REQUIRED_SMARTY_DIR.'cache';
        }
    }
?>

Now let’s create our template file with the name index.tpl into the templates folder.
Note that all the elements in the tag { } will be substituted in our code.
index.tpl

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>Smarty Demo</title>
  </head>

  <body>
    {$msg} <br/>
    <!-- Applying a modifier -->
    {$msg|upper} <br/>

    Select a country:
    <select name="select">
      {html_options options=$countries selected=$selectedCountry}
    </select>
    <p/>
    <!-- For dynamically generated options, it comes in handy -->
    {html_checkboxes values=$country_keys output=$countries
    selected=$selectedCountries separator='<br/>'}
    <p/>

    <!--
I wouldn't recommend you use the date component provided by Smarty since
it's not visually appealing and offers no validation whatsoever
    -->
    Day of departure: {html_select_date start_year="-10"} <br/>
    Time of departure: {html_select_time}

    <p/>
    <!--
{$flight->company} replaces <?php echo $flight->company; ?> you would have to use
if you weren't using Smarty
    -->
    <form id="form1" name="form1" method="post" action="">
      Flight: <input  type="text" id="company" value="{$flight->company}" />
      <br/>
      From: <input type="text" id="from" value="{$flight->from}" />
    </form>

    <p/>
    <table width="100%" cellpadding="0" cellspacing="0">
      <tr align="left">
        <th width="18%" scope="col">SSN</th>
        <th width="25%" scope="col">COURSES</th>
        <th width="14%" scope="col">CREDITS</th>
        <th width="21%" scope="col">SCORE</th>
        <th width="22%" scope="col">COMMENT</th>
      </tr>
      {section name=c loop=$courses }
      <tr bgcolor="{cycle values='#FFA, #FFF'}">
        <td>{$smarty.section.c.iteration}</td>
        <td>{$courses[c].name}</td>
        <td>{$courses[c].credit}</td>
        <td>{$courses[c].score}</td>
        <td>
          {if $courses[c].score gte 50}
          PASS
          {else}
          FAIL
          {/if}
        </td>
      </tr>
      {/section}
    </table>
    <p/>

    <!--
Modifiers can be used to change the output of the passed variable by
doing some processing on it first such as displaying a date in a specified
format.
    -->
    Created on: {$posted|date_format:"%A, %B %e, %Y"}
  </body>
</html>

Next, create a PHP file with the name index.php in the site’s root folder ie. /root_of_site/index.php

<?php
    include_once('CustomSmarty.class.php');

    class Flight{
        public $company;
        public $from;
        public $to;

        public function __construct(){
            $this->company = 'South African Airways';
            $this->from = 'Accra';
            $this->to = 'London';
        }
    }

    $lsCourses = array(
        array('name'=>'Programming using BASIC', 'credit'=>2, 'score'=>70),
        array('name'=>'XMLifying data', 'credit'=>1, 'score'=>40),
        array('name'=>'Web 2.0 Concept', 'credit'=>2, 'score'=>49)
    );

    $countries = array(
        'gh' => 'Ghana',
        'ng' => 'Nigeria',
        'lr' => 'Liberia',
        'ch' => 'China'
    );
    $smarty = new CustomSmarty();
    $smarty->debugging_ctrl = URL;

    $smarty->assign('msg', 'This is a demonstration of the Smarty templating engine.');
    $smarty->assign('countries', $countries);
    $smarty->assign('country_keys', array_keys($countries));
    $smarty->assign('selectedCountry', 'lr');
    $smarty->assign('selectedCountries', array('lr'));
    $smarty->assign('flight', new Flight());
    $smarty->assign('courses', $lsCourses);
    $smarty->assign('posted', '2008-08-27');

    $smarty->display('index.xhtml');
?>

I used smarty_demo as the site root.
Pointing your browser’s URL to http://localhost/smarty_demo, you should see the outcome displayed below.

Smarty demo screen capture

Smarty demo screen capture

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Twitter
« Newer PostsOlder Posts »

Powered by WordPress