Posted in Linux

If you’ve installed the MediaServer or PhotoStation packages on your Synology NAS you’ve probably noticed @eaDir directories popping up everywhere. These are “hidden” folders equivalent to thumbs.db on Windows where the package stores thumbnail files associated with iTunes support. If you’re not using iTunes you don’t need these directories. You can remove them in two steps:

Disable the Service Creating Them

SSH in as root and run the following:

1
2
cd /usr/syno/etc.defaults/rc.d/
chmod 000 S66fileindexd.sh S66synoindexd.sh S77synomkthumbd.sh S88synomkflvd.sh S99iTunes.sh

Remove the existing directories

Again in SSH use the following to locate them (cd to your volume root first):

1
find . -type d -name "@eaDir"

and if you’re feeling adventurous you can automatically delete them like so:

1
find . -type d -name "@eaDir" -print0 | xargs -0 rm -rf

Read More »

Posted in PHP

If you need to do multiple CURL requests with PHP, curl_multi is a great way to do it. The problem with curl_multi is that it waits for all requests to complete before it begins processing any of the responses – for example if you’re making 100 requests and just one is slow, the other 99 will be held off for processing until the one has come back. This is wasteful so I’ve whipped up a script originally written by Josh Fraser with my own modifications and improvements that will begin processing a response immediately before dispatching the next request.
 

Usage:

The $callback (second) argument of rolling_url() can take any callback supported by PHP.

1
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
function curl_multi_download(array $urls, callable $callback, array $custom_options = array())
{
	// make sure the rolling window isn't greater than the # of urls
	$rolling_window = 5;
	$rolling_window = (sizeof($urls) < $rolling_window) ? sizeof($urls) : $rolling_window;
 
	$master = curl_multi_init();
	$curl_arr = array();
	$options = array(
		CURLOPT_RETURNTRANSFER => true,
		CURLOPT_FOLLOWLOCATION => true,
		CURLOPT_MAXREDIRS => 5,
	) + $custom_options;
 
	// start the first batch of requests
	for ( $i = 0; $i < $rolling_window; $i++ )
	{
		$ch = curl_init();
		$options[CURLOPT_URL] = $urls[$i];
		curl_setopt_array($ch, $options);
		curl_multi_add_handle($master, $ch);
	}
 
	do
	{
		while(($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM);
		if($execrun != CURLM_OK)
			break;
		// a request was just completed -- find out which one
		while( $done = curl_multi_info_read($master) )
		{
			$info = curl_getinfo($done['handle']);
 
			// request successful.  process output using the callback function.
			$output = curl_multi_getcontent($done['handle']);
			call_user_func_array($callback, array($info, $output));
 
			if ( isset($urls[$i+1]) )
			{
				// start a new request (it's important to do this before removing the old one)
				$ch = curl_init();
				$options[CURLOPT_URL] = $urls[$i++];  // increment i
				curl_setopt_array($ch, $options);
				curl_multi_add_handle($master, $ch);
			}
 
			// remove the curl handle that just completed
			curl_multi_remove_handle($master, $done['handle']);
		}
	} while ($running);
 
	curl_multi_close($master);
	return true;
}

 

Bonus Round

Here’s a curl_download() function, used to grab the response from a single URL (For extra points switch out curl_exec() with curl_exec_utf8() using the same method as above):

1
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
function curl_multi_getcontent_utf8( $ch )
{
	$data = curl_multi_getcontent( $ch );
	if ( !is_string($data) )
		return $data;
 
	unset($charset);
	$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
 
	/* 1: HTTP Content-Type: header */
	preg_match( '@([\w/+]+)(;\s*charset=(\S+))?@i', $content_type, $matches );
	if ( isset( $matches[3] ) )
		$charset = $matches[3];
 
	/* 2: <meta> element in the page */
	if ( !isset($charset) )
	{
		preg_match( '@<meta\s+http-equiv="Content-Type"\s+content="([\w/]+)(;\s*charset=([^\s"]+))?@i', $data, $matches );
		if ( isset( $matches[3] ) )
			$charset = $matches[3];
	}
 
	/* 3: <xml> element in the page */
	if ( !isset($charset) )
	{
		preg_match( '@<\?xml.+encoding="([^\s"]+)@si', $data, $matches );
		if ( isset( $matches[1] ) )
			$charset = $matches[1];
	}
 
	/* 4: PHP's heuristic detection */
	if ( !isset($charset) )
	{
		$encoding = mb_detect_encoding($data);
		if ($encoding)
			$charset = $encoding;
	}
 
	/* 5: Default for HTML */
	if ( !isset($charset) )
	{
		if (strstr($content_type, "text/html") === 0)
			$charset = "ISO 8859-1";
	}
 
	/* Convert it if it is anything but UTF-8 */
	/* You can change "UTF-8"  to "UTF-8//IGNORE" to
	   ignore conversion errors and still output something reasonable */
	if ( isset($charset) && strtoupper($charset) != "UTF-8" )
		$data = iconv($charset, 'UTF-8', $data);
 
	return $data;
}

See inside for the full code.

Read More »

Posted (Updated ) in Uncategorized

I just installed Ubuntu 13.04 in Virtualbox 4.2.16 and found much to my annoyance that the VM thought my mouse was a little higher than it actually was:

Ubuntu thinks my mouse is a little higher than it actually is

It turns out this is caused by having 3D acceleration turned on in VM Settings – Display window. After doing a bit of sleuthing I came across a forum post on virtualbox.org with a command that did the trick nicely.

Simply open a terminal and run

VBoxManage setextradata global GUI/Customizations noStatusBar

Restart your VM and voila. Perfect mouse working with 3D acceleration!

Mouse Y-Axis working as it should

Read More »

Posted (Updated ) in Javascript

Despite a global shift towards touch-oriented devices on the internet and the existence of a relatively old bug report, Google has decided not to add support for mobile/touch to their ChartRangeFilter for Google Charts (as of the time of writing). See for yourself:

Demo | Source – Doesn’t work on mobile. Frustrating!

This is really annoying, especially if you are dealing with a significant chunk of data that could take advantage of this functionality by allowing users to ‘zoom in’ on specific areas.

 

A Workaround (For Now)

Although we can’t get the ChartRangeFilter working on mobile, we can display something else instead for mobile devices such as the handy jQRangeSlider. To do so we’ll need to hook into the methods provided by google.visualization.ControlWrapper (which sits around the ChartRangeFilter).

In the source we have:

var controlWrapper = new google.visualization.ControlWrapper({
	controlType: 'ChartRangeFilter',
	...
});

We can update the selection in the ChartRangeFilter by calling the following on its ControlWrapper:

controlWrapper.setState({
	range: {
		start: new Date(2004, 01, 01),
		end: new Date(2006, 01, 01)
	}
});
controlWrapper.draw();

Combine this with a mobile UA check (terrible I know, but you can’t really use browser  feature detection for this) and a DateRangeSlider (I’m using dates on my X axis) and you get the following:

var is_mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
// ChartRangeFilter doesn't work on mobile. Use a dateRangeSlider to manipulate it
if ( is_mobile )
{
	$('#filter').hide();
	$('#filter_mobile').show();
 
	$( "#filter_mobile" ).dateRangeSlider({
		bounds: {
			min: new Date(2001, 1, 1),
			max: new Date(2013, 3, 1)
		},
		defaultValues: {
			min: new Date(2001, 1, 1),
			max: new Date(2013, 3, 1)
		},
		step: {
			months: 1
		},
		arrows: true,
		wheelMode: null
	}).bind('valuesChanged', function(e, data) {
		controlWrapper.setState({range: { start: data.values.min, end: data.values.max }});
		controlWrapper.draw();
	});
}

Demo

ChartRangeFilter workaround for mobile devices

ChartRangeFilter workaround for mobile devices

Try the demo below. View it on a mobile device (or use a mobile UA string). You get the standard Google Charts ChartRangeFilter for desktop and jQRangeSlider for mobile. Everything works nicely!

Demo | Source – Snazzy mobile-specific date range widget

Read More »

Posted (Updated ) in Linux

This morning I noticed one of my drives wasn’t mounted so I attempted to mount manually and got the following error message:

$ sudo mount /dev/sdi1 /mnt/my_drive/

mount: wrong fs type, bad option, bad superblock on /dev/sdi1,
missing codepage or helper program, or other error
In some cases useful info is found in syslog – try
dmesg | tail or so

Strange. Following the messages advice I checked out dmesg:

$ dmesg | tail

[ 213.962722] EXT4-fs (sdi1): no journal found

 

The Solution

If this happens to you, you can ignore the error and mount in readonly mode using the following command:

sudo mount -o loop,ro,noexec,noload /dev/sdi1 /mnt/your_broken_partition/

Thanks to Computer Forensics for their useful post on this issue.

ALWAYS BACKUP YOUR DATA

Read More »

Posted (Updated ) in PHP

As I’m sure is the case with just about every WordPress user, my site is constantly hit with failed login attempts from bots – usually originating from other countries. I had the idea last night to implement country-based login restrictions using CloudFlare’s IP Geolocation server variable.

My goal was to redirect redirect all users visiting the login page from countries other than Australia to the home page. This occurs before a login attempt can even be made.

Here’s how it’s done:

  • Go into CloudFlare settings and make sure the IP Geolocation option is turned onMake sure CloudFlare IP Geolocation is turned on
  • Add the following to your active themes functions.php file
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    // Limit login countries - requires cloudflare
    add_filter( 'authenticate', 'login_failed', 1, 3);
    function login_failed( $user, $username, $password )
    {
        if ( !isset( $_SERVER['HTTP_CF_IPCOUNTRY'] ) )
            return $user;
     
        if ( !in_array( $_SERVER['HTTP_CF_IPCOUNTRY'], array('AU') ) )    {
            wp_redirect( home_url() );
            exit;
        }
     
        return $user;
    }

To test simply use a country other than your own. Once it’s confirmed working switch to your country and you’re good to go!

Read More »

Posted in Database

Here is a super simple SQL query to determine the size of all tables in a given database:

SELECT TABLE_NAME AS "Table",
	round(((data_length + index_length) / 1024 / 1024), 2) AS Size_in_MB
FROM information_schema.TABLES 
WHERE table_schema = 'my_db_name'
ORDER BY Size_in_MB DESC

This can be very useful when debugging for instance in figuring out which rogue plugin/module on your site is dumping hundreds of megs of data into your database.

Read More »

Posted (Updated ) in Uncategorized

Disclaimer: The details of this post are shamelessly ripped from phatness.com. All credit to those guys, I just wanted a copy for myself for future reference.


On every OS ever, Home, End, Pg Up and Pg Down keys work like so:

- Home -> move the cursor to the beginning of the line
- End -> move the cursor to the end of the line
- Pg-Up -> move the cursor up the length of the viewport
- Pg-Dn -> move the cursor down the length of the viewport

But OSX in all its infinate wisdom decided that doesn’t make sense. Instead they should work like so:

- Home -> move (nothing, not even the cursor, just your view) to the beginning of the DOCUMENT
- End -> move (nothing, not even the cursor, just your view) to the end of the DOCUMENT
- Pg-Up -> move (nothing, not even the cursor, just your view) up the length of the viewport
- Pg-Dn -> move (nothing, not even the cursor, just your view) down the length of the viewport

Now I’m not one to criticise… but that’s stupid and anyone who likes it is stupid 😉

 

How to Fix

There’s a really useful open source utility out there called DoubleCommand which is highly recommended and I’m sure broadly used. If you want a non-application solution there are also some config files to edit to get the job done.

Most Applications

Create a file
/home/<username>/Library/KeyBindings/DefaultKeyBinding.dict and add the following:

{
    "\UF729"  = "moveToBeginningOfLine:";
    "$\UF729" = "moveToBeginningOfLineAndModifySelection:";
    "\UF72B"  = "moveToEndOfLine:";
    "$\UF72B" = "moveToEndOfLineAndModifySelection:";
    "\UF72C"  = "pageUp:";
    "\UF72D"  = "pageDown:";
}

Terminal

Terminal preferences

Terminal preferences

Open Terminal – Preferences – Settings – Keyboard. Edit each of the following setting their action to ‘send string to shell’.

Key		Escape Sequence
 
Home		\033[1~
End		\033[4~
Page Up		\033[5~
Page Down	\033[6~

Now open /home/<username>/.inputrc and add:

"\e[1~": beginning-of-line
"\e[4~": end-of-line
"\e[5~": history-search-backward
"\e[6~": history-search-forward
"\e[3~": delete-char
"\e[2~": quoted-insert
"\e[5C": forward-word
"\e[5D": backward-word
"\e\e[C": forward-word
"\e\e[D": backward-word
set completion-ignore-case On

Other notes

Restart your machine for changes to take affect. Restarting individual applications also works if you can’t restart for whatever reason. I hear Firefox needs its own changes – if you happen to know what those are comment below and I’ll add them in.

Read More »

Posted (Updated ) in Database, PHP

I recently came across a tutorial on sitting Redis infront of WordPress allowing for insanely fast page generation. I gave it a try and it really works, in fact I’m now using it on this very site! The best part however is the fact that the script requires absolutely no modification to your existing WordPress site save for 1 line of htaccess. Truly amazing.

Below I’ll detail my slightly modified version of Jim’s script along with some metrics.

 

Firstly, What is Redis and what will it do for me?

The Redis website describes Redis as

… an open source, BSD licensed, advanced key-value store. It is often referred to as a data structure server since keys can contain stringshasheslistssets and sorted sets.

What does this mean? Essentially it’s Memcached but more useful. Redis stores key-value pairs in memory and spits them out when requested. Unlike Memcached it has built in persistence but what’s most important to us is that it’s fast – very fast.

We’ll be using Redis to speed up our site by loading cached pages from it directly without even booting up WordPress. This will save a large amount of page generation time and get out site infront of our users’ eyeballs faster.

 

Exactly how much faster are we talking?

In my very unscientific tests, loading www.flynsarmy.com a bunch of times resulted in the following:

Before (Secs) After (Secs)
1.556
0.468
0.494
0.498
0.492
0.514
0.499
0.511
0.499
0.02001
0.00896
0.00883
0.00959
0.01472
0.00916
0.00915
0.00756
0.01989

As you can see from the table above this equates to a 20x to 50x speed increase and that was WITH W3 Total Cache installed! Results of course may vary but I think you get the picture.

Read More »

Posted (Updated ) in PHP

I have a project coming up involving editing WordPress posts from the front end of the site. There are a bunch of plugins that let you do this the coolest of which seems to be Front-end Editor but I wanted to come up with my own solution. Luckily it turned out to be surprisingly quick and painless!

For this tutorial I’ll be editing the cafe custom post type from my last post.

Read More »