coding (67)


jNag 0.3 released

Another day another release of jNag, the Jquery mobile based mobile interface for Nagios.

V0.3 adds:

  • ajax problem polling
  • browse by Hosts and services, as well as host groups
  • a tider interface

As usual the demo is available here

And the download is here

Here’s a couple of screenshots showing off the new features:

That yellow box at the top Pulses on and off if there’s any problems, and it updates via ajax…so no page refresh is required!

Showing off the new, tidier interface when viewing all services monitored by Nagios




jNag 0.2 released

Yesterday I released jNag, my new mobile web interface for the rather spiffy Nagios network monitoring tool.

Today I’ve updated it with some new features:

  • You can now see host problems as well as service problems
  • Acknowledged problems now don’t show in the main list on the front screen
  • (this is the major one) you can now acknowledge problems right from jNag!

download it here

demo is here

Here’s a screenshot of the new ‘acknowledge function in use:




jNag – mobile interface for Nagios

I recently became aware of Jquery Mobile, a new framework from the group behind the rather excellent jquery, that focuses on mobile development. Using the framework you can quickly and easily produce a web app that’s tailored for mobile devices and offers a consistent user experience across a range of mobile platforms.

I decided I needed a little project to get to grips with how JQuery mobile works and, since I’d spent an afternoon at work this week setting up the rather excellent Nagios (a network monitoring server) I thought I’d have a go at producing a mobile interface for it.

Nagios helpfully provides a way of getting JSON formatted data about it’s current status via a cgi script (statusjson.cgi) so it was simply a case of parsing that data and outputting it via the jquery mobile stuff.

Here’s what I came up with: jNag (apologies for the name, it’s late).  It’s a bit rough and ready at the moment, but it can display alerts (warning / critical), allow you to browse through your tree of devices and drill down to view service output.  If you combine it with the rather excellent NagDroid to give you notifications you have a full mobile admin suite.  Not bad for a few hours work.

download here (extracted files need uploading to your nagios webroot)

There’s a demo here (best viewed on an Android device or  iPhone)

here’s how it looks on my Desire HD




WP Akismet plugin behind proxy

The new HTTP Api in wordpress 2.9 is fantastic, it standardises http requests and allows you to define a proxy through which all http requests are routed.  Useful if, like me, you run a wordpress server on a corporate network.

You can configure your proxy server by adding the following lines to your ‘wp-config.php’

define('WP_PROXY_HOST', '192.168.250.1');
define('WP_PROXY_PORT', '800');
define('WP_USEPROXY','TRUE');

However there’s currently one problem with the default installation of wordpress when it comes to proxy support. Akismet (the spam filtering plugin) Doesn’t work behind the proxy. No matter what you put in for your proxy settings you’ll be told ‘unable to connect’.

The reason is that Akismet uses a ‘raw’ socket connection to do its http requests, rather than the spiffy new API. So, here’s how to fix it:

you need to edit the file ‘/wp-content/plugins/akismet/akismet.php’.

search for the function ‘akismet_http_post’ and replace the entire function (you can just rename the old function) with my newly crafted one:

function akismet_http_post($request, $host, $path, $port = 80, $ip=null){
        global $wp_version;
	$akismet_version = constant('AKISMET_VERSION');
        $args = array(
             'method'=>'POST',
             'user-agent'=>"User-Agent: WordPress/$wp_version | Akismet/$akismet_version",
             'body'=>$request
        );
         $url = "http://".$host.$path;

         if( !class_exists( 'WP_Http' ) )
         include_once( ABSPATH . WPINC. '/class-http.php' );
	 $http_request = new WP_Http;
         $http_response = $http_request->request($url,$args);
         if( is_wp_error( $http_response ) )
              return;
         $response[0] = $http_response['headers'];
         $response[1] = $http_response['body'];
	return $response;
}

And voila, working spam detection behind a proxy.




Joomla Hack: autofill subject line on contact email

So, imagine you have a Joomla site containing lots of pages about…well, anything really. It’d be nice if you could have a ‘request info’ link on each page so users could send you an email with a pre-generated subject line about that page, so you can parse emails (either manually or using some kind of automatic system) coming from the site.

eg: your site has seperate pages about ‘product A’ and ‘product B’. you want a link on ‘product A’s’ page that sends you an email with the subject line ‘yoursite.com: product A enquiry’ and the same for product B.

I can’t find any way of doing this ‘properly’ in Joomla, so here’s a hack I came up with. What it does basically is take a variable from your URL and put it into the subject box. So you can setup a link to ‘http://yoursite.com/contact-us?subject=test subject’ and the contact email form will automagically have the subject line filled in with ‘test subject’.

First up, setup a contact to send the emails from (you’ve probably done this anyway if you have a ‘contact us’ page).

Now publish that contact using the ‘contact’ view under something like yoursite.com/contact-us

now here’s the hack:

open up “components/com_contact/views/contact/tmpl/default_form.php”

the line you’re looking for is this one (it’s line 50 in my copy of the file)

change that to this:


The additional line uses the JRequest class to get the value of our ‘subject’ variable. Then we simply write that into the subject field. We also set a default value ‘website enquiry’ so if the subject variable is unset we have a nice default subject line instead of a blank box. using JRequest::getWord to retrieve the variable means our input is automatically filtered to strip out any html / javascript injection attempts.

you can now set up links anywhere in your site to ‘contact-us?subject=your subject’ to automatically fill out the subject line.




PHP: clean up your includes!

If you’ve ever worked on any kind of PHP framework, you’ll likely know the pain that a misplaced ‘space’ in one of your includes can cause.  For the rest of you imagine this scenario:

To load a page in my framework, I have an index.php that loads in a ‘core.php’ (which isn’t web accessible for security purposes) which itself then loads in a long (20 or so) list of included files which make up my framework.  So, my core.php might look like this:

require_once("database.php");
require_once("mail.php");
require_once("something_else.php");
etc
etc
require_once("page_loader.php");
send_headers();
load_page($page_id);
send_footers();

which is lovely, keeps all the functionality of the framework nice and segregated.  However, if one of those included files actually outputs anything to the screen, it can cause all sorts of issues, especially if our ‘send_headers’ function is sending our actual HTML headers…. in this case, anything output to the screen before our HTML headers are sent can really cock things up. Even worse, trying to track down where the output is coming from can be a nightmare when you have a large number of included files.

After a bit of thinking I stumbled across the (probably) blindingly obvious way round this without spending hours debugging and trying to track down which included file is the culprit:

good old fashioned output buffering!

by doing this:

// start buffering
ob_start();

require_once("database.php");
require_once("mail.php");
require_once("something_else.php");
etc
etc
require_once("page_loader.php");

//stop buffering, discard buffer contents
ob_end_clean();

send_headers();
load_page($page_id);
send_footers();

we ensure that anything output by the included files is buffered, then discarded before we actually start outputting stuff.

lovely.




PHP Javascript / CSS loader

I’m currently working on an in-house management system here at Tyrrell Systems, and employ a lot (seriously, a lot) of javascript for everything from ajax loading of information to various layout tweaks and so on.

When I inherited the project (a good 2 years ago now) the previous coders had made use of various scripts they’d found on the web, as well as a lot of hand crafted stuff.  I started swapping things over to the rather spiffy Jquery library and added a load more functionality.

However, the system has inevitably started to slow down over the last year or so, loading in all those javascript libraries has taken its toll on page load times.  As well as the javascript overhead there’s also around 30 different css files to load in (I like to keep things well segregated!).  This leads to the following problem:

As you can see, it was taking nearly 30 seconds just to load in the css & javascript for a typical page.

So, I started looking for a solution.  Now, the general (and rather obvious) approach to this is to ‘munge’ all your css (or javascript) files into one big file, run that through some kind of optimiser and then serve the resulting file instead.  This reduces page requests & bandwidth used in one go.  This is a good example of this general approach.  Unfortunately, 90% of these scripts rely on using mod_rewrite to redirect calls to css / js files through a PHP script.  My site runs on IIS, so they’re no good for me.  To cut a long story short, I decided to write a class that would:

  • Allow me to merge multiple css / js files into 1.
  • optionally optimise that file before serving it.
  • Cache the output so I only have to do this process when I actually change any of the css / javascript.

This is what I came up with.  It makes use of jsmin-php for javascript minification, and css-tidy for well, css-tidying so you’ll need to have those if you want to use optimisation.  Or you can optionally turn it off.  To use the class:

First initialise it:

$main-css = new cacher("main.css","css");

The first parameter is a filename (can be anything you like) and the second is the type (must be css or js).

Next we add our files:

$main-css->add_file("path/to/my/file.css");

or we can add every css/js file in a folder like so:

$main-css->add_folder("path/to/my/folder/");

then when we’ve added everything, we output the files:

$main-css->output();

The output file will be cached (in /theme/cache by default) until you delete the cached copy from that folder. So if you change any of the files included using this class, remember to delete your cached file!

and that’s it.

Just to complete the before / after theme here’s what this did to my loading times:

Here’s the code (as usual, it’s free to use but please give some credit if you do).

nb: the variable $cfg used here is a global class used in the project this came from. Basically, $cfg->root needs to point at the webroot for your project.

<?php
class cacher{	    
	function __construct($filename,$type)
	    $this->type = $type;
            $this->filename = $cfg->root."\theme\cache\".$filename;
            $this->url = "/theme/cache/$filename";
            $this->optimise = $optimise;
            if (file_exists($this->filename)) {
                $this->output();
                $this->cached = true;
            } else {
                $this->cached = false;
                $this->fh = fopen($this->filename,"a") or die("unable to open file $this->filename");
            }
        }

        function add_folder($newfolder){
            global $cfg;
            if ($this->cached == true){
                return;
            } else {
                $dh = opendir($cfg->root.$newfolder);
                while (false !== ($file = readdir($dh))){
                     $ext = substr($file,strrpos($file,'.')+1);
                     if ($this->type == $ext){
                         fwrite($this->fh,file_get_contents($cfg->root.$newfolder."\".$file));
                         fwrite($this->fh,"n");
                     }
                }
                closedir($dh);
            }
        }

        function add_file($newfile){
            if ($this->cached == true){
                return;
            } else {
                fwrite($this->fh,file_get_contents($newfile));
                fwrite($this->fh,"n");
            }
        }

        function output(){
             if ($this->cached == true){
                 return;
             } else {
             if ($this->type == "css"){
                    fclose($this->fh);
                    if ($this->optimise == true){
                        require("lib/csstidy/class.csstidy.php");
                        $css_out = new csstidy();
                        $css_out->parse(file_get_contents($this->filename));
                        $this->fh = fopen($this->filename,"w");
                        fwrite($this->fh,$css_out->print->plain());
                        fclose($this->fh);
                    }
                    echo "";
             } elseif ($this->type == "js") {
                    fclose($this->fh);
                    if ($this->optimise == true){
                        require("lib/jsmin/jsmin.php");
                        $js_out = JSMin::minify(file_get_contents($this->filename));
                         $this->fh = fopen($this->filename,"w");
                        fwrite($this->fh,$js_out);
                        fclose($this->fh);
                    }
                    echo "";
             }

            }
        }

    }

?>