<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Pale Purple | Pale Purple</title>
	<atom:link href="http://www.palepurple.co.uk/feed" rel="self" type="application/rss+xml" />
	<link>http://www.palepurple.co.uk</link>
	<description>Web and Mobile Application Development</description>
	<lastBuildDate>Fri, 19 Apr 2013 16:29:53 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Geary kickstarter</title>
		<link>http://www.palepurple.co.uk/geary-kickstarte</link>
		<comments>http://www.palepurple.co.uk/geary-kickstarte#comments</comments>
		<pubDate>Fri, 19 Apr 2013 10:42:00 +0000</pubDate>
		<dc:creator>palepurple</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[geary]]></category>

		<guid isPermaLink="false">http://www.palepurple.co.uk/?p=625</guid>
		<description><![CDATA[We and others have sponsored the &#8220;kickstarter&#8221; campaign for Geary (a new desktop email client for Linux). &#8220;Geary is a new email reader for GNOME designed to let you read your email quickly and effortlessly. Its interface is based on conversations, so you can easily read an entire discussion without having to click from message to [...]]]></description>
				<content:encoded><![CDATA[<p>We and <a title="Bytemark sponsoring Geary" href="http://blog.bytemark.co.uk/2013/04/17/geary">others</a> have sponsored the &#8220;<a title="Geary on IndieGoGo" href="http://www.indiegogo.com/projects/geary-a-beautiful-modern-open-source-email-client">kickstarter</a>&#8221; campaign for <a title="Geary - Yorba" href="http://www.yorba.org/projects/geary/">Geary</a> (a new desktop email client for Linux).</p>
<p><em>&#8220;Geary is a new email reader for GNOME designed to let you read your email quickly and effortlessly. Its interface is based on conversations, so you can easily read an entire discussion without having to click from message to message. Geary is still in early development and has limited features today, but we’re planning to add lightning-fast searching, multiple account support, and much more. Eventually we’d like Geary to have an extensible plugin architecture so that developers will be able to add all kinds of nifty features in a modular way.&#8221;</em></p>
<p>As of today (6 days left), they seem as far as ever from reaching their goal (only 30% complete).</p>
<p>While dominating nearly every other part of the software eco-system, Linux still seems to be failing on the desktop.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.palepurple.co.uk/geary-kickstarte/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Configuring multiple websites with ease using Apache</title>
		<link>http://www.palepurple.co.uk/configuring-multiple-websites-with-ease-using-apache</link>
		<comments>http://www.palepurple.co.uk/configuring-multiple-websites-with-ease-using-apache#comments</comments>
		<pubDate>Fri, 15 Mar 2013 16:04:01 +0000</pubDate>
		<dc:creator>palepurple</dc:creator>
				<category><![CDATA[systems administration]]></category>

		<guid isPermaLink="false">http://www.palepurple.co.uk/?p=550</guid>
		<description><![CDATA[When using Apache to host websites, developers tend to either have one configuration file with multiple sites defined within in it (often /etc/httpd/httpd.conf or similar), or end up dealing with a swarm of separate files in /etc/apache2/sites-available &#8211; one per domain. If all the domains are identical, in terms of file structure &#8211; apart from [...]]]></description>
				<content:encoded><![CDATA[<p>When using Apache to host websites, developers tend to either have one configuration file with multiple sites defined within in it (often /etc/httpd/httpd.conf or similar), or end up dealing with a swarm of separate files in /etc/apache2/sites-available &#8211; one per domain.</p>
<p>If all the domains are identical, in terms of file structure &#8211; apart from the hostname &#8211; then we can use Apache&#8217;s <a title="Apache documentation - mod_vhost_alias" href="http://httpd.apache.org/docs/current/mod/mod_vhost_alias.html">mod_vhost_alias</a> module to simplify the configuration. When combined with mod_rewrite and use of some Apache environment variables (e.g. %{HTTP_HOST}), your configuration can become much more manageable.</p>
<p>So, you can go from multiple virtual hosts configured similar to the below &#8211; where all that&#8217;s changing between site(s) is effectively the hostname &#8211; :</p>
<pre>&lt;Virtualhost *:80&gt;
    ServerName mydomain.com
    ServerAlias www.mydomain.com
    DocumentRoot /var/www/vhosts/mydomain.com/public/
    &lt;Location '/'&gt;
         Allow from ALL
         AllowOverRide ALL
    &lt;/Location&gt;

    CustomLog /var/log/apache2/mydomain.com-access.log combined
    ErrorLog /var/log/apache2/error.log
&lt;/VirtualHost&gt;</pre>
<p>To :</p>
<pre>&lt;VirtualHost *:80&gt;
    UseCanonicalName Off
    VirtualDocumentRoot /var/www/vhosts/%0/public/
    &lt;Location '/'&gt;
        Allow from ALL
        AllowOverRide ALL
    &lt;/Location&gt;
    CustomLog /var/log/apache2/%{HTTP_HOST}-access.log combined
    ErrorLog /var/log/apache2/error.log
&lt;/VirtualHost&gt;</pre>
<p>Advantages:</p>
<ol>
<li>Once the directory structure has been created (and DNS configured) sites can be served immediately</li>
<li>No need to restart Apache,</li>
<li>No need to create a new configuration file (one per site etc)</li>
</ol>
<p>Disadvantages:</p>
<ol>
<li>Perhaps difficult to handle e.g. www.mydomain.com and mydomain.com being hosted from the same directory (leading to symlinks in e.g. /var/www/vhosts).</li>
<li>Have to rely on .htaccess files for per-site configuration (which may not be desirable ).</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.palepurple.co.uk/configuring-multiple-websites-with-ease-using-apache/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHPUnit testing &#8211; mock and double objects</title>
		<link>http://www.palepurple.co.uk/phpunit-testing-mock-and-double-objects</link>
		<comments>http://www.palepurple.co.uk/phpunit-testing-mock-and-double-objects#comments</comments>
		<pubDate>Tue, 05 Mar 2013 17:10:26 +0000</pubDate>
		<dc:creator>palepurple</dc:creator>
				<category><![CDATA[php]]></category>
		<category><![CDATA[phpunit]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[zend framework]]></category>

		<guid isPermaLink="false">http://www.palepurple.co.uk/?p=604</guid>
		<description><![CDATA[When writing unit tests, it&#8217;s often desirable to &#8216;mimick&#8217; a backend call to web service or database (without using the real thing backend). Ideally you need this to be quick and easy to do. One way of doing this is to use mock or double objects. This article discusses their use with PHPUnit (a unit [...]]]></description>
				<content:encoded><![CDATA[<p>When writing unit tests, it&#8217;s often desirable to &#8216;mimick&#8217; a backend call to web service or database (without using the real thing backend). Ideally you need this to be quick and easy to do. One way of doing this is to use mock or double objects. This article discusses their use with PHPUnit (a unit testing framework for PHP).</p>
<p>In some circumstances, a library or component may provide a mock or test backend (for example, with the Zend_Http_Client, a test adapter can be plugged in which allows you to pre-set a given response, like in example 1 below). In some circumstances, however, you may not have access to a test adapter, or it may not be possible to inject dependencies into the necessary objects.</p>
<p>The below article shows how you can use hard-coded mock objects (Zend_Http_Client_Adapter_Test), or allow PHPUnit to dynamically create one for you using its <a href="http://www.phpunit.de/manual/3.5/en/test-doubles.html">test double</a> functionality. </p>
<p><span id="more-604"></span></p>
<p>The example code below is based around sending push notifications to the Android cloud messaging platform &#8211; for android devices. A service (not included in full) has already been written (Mobile_AndroidPushMessage) which uses Zend_Http_Client internally to send the push message. Its API is similar to the below :</p>
<pre class="brush: php; title: ; notranslate">
class Mobile_AndroidPushMessage {
     public function addTo($recipientKey);
     public function setPayload($data);
     public function setHttpClient(Zend_Http_Client $client);
     public function send(); // returns boolean
     public function getErrors(); // return array or null if no errors
}
</pre>
<h2>Using a using Zend_Http_Client_Adapter_Test</h2>
<p>The Zend_Http_Client library allows you to inject in a different backend adapter &#8211; so rather than making an actual request to Google, we can use a pre-determined one. This ensures our test is repeatable and predictable &#8211; and fast.</p>
<p>So, firstly, let&#8217;s setup a contrived PHPUnit test with a &#8216;fake&#8217; success message &#8230;. </p>
<pre class="brush: php; title: ; notranslate">
// .... class spec etc.
public function setUp() { 
    $m = new Mobile_AndroidPushMessage();
    $httpClient = new Zend_Http_Client();
    $successAdapter = new Zend_Http_Client_Adapter_Test();
    $successAdapter-&gt;setResponse(&quot;HTTP/1.1 200 OK \r\nContent-Type: application/json\r\n\r\n&quot; .
       '{&quot;multicast_id&quot;:579055555555471,&quot;success&quot;:1,&quot;failure&quot;:0,&quot;canonical_ids&quot;:0,&quot;results&quot;:[{&quot;message_id&quot;:&quot;0:13612757325555555555d6f9fd7ecd&quot;}]}');
    $httpClient-&gt;setAdapter($successAdapter);
    $m-&gt;setHttpClient($httpClient);
    $this-&gt;androidGcmService = $m; // store it for later use by tests
}
  ...
</pre>
<p>And test is as follows :</p>
<pre class="brush: php; title: ; notranslate">
public function testBasicBehaviour() {
    // ... setup payload/recipient key data, can be anything in this example. 
    $m = $this-&gt;androidGcmService;
    $m-&gt;setPayload('message');
    $m-&gt;addTo('someone');
    $ok = $m-&gt;send();
    $this-&gt;assertTrue($ok, &quot;always returns true, as setUp contains a hard coded success message!&quot;);
}
</pre>
<p>And, as we control the response, we can easily change the above to mimic a failure at the GCM end, using :</p>
<pre class="brush: php; title: ; notranslate">
public function testErrorHandling() { 
    $m = $this-&gt;androidGcmService;
    $adapter = $m-&gt;getHttpClient()-&gt;getAdapter();
    $adapter-&gt;setResponse('HTTP/1.1 200 OKr\nContent-Type: application/json\r\n\r\n I am not json, so something blows up here');
    $m-&gt;setPayload('whatever');
    $m-&gt;addto('someone');
    $ok = $m-&gt;send();
    $this-&gt;assertFalse($ok);
    $errors = $m-&gt;getErrors();
    // Check contents of the 'errors' array.
}
</pre>
<p>So &#8211; using the Test adapter we can fake error conditions from Google and test our code in isolation. </p>
<h2>Using a mock object / test double around Zend_Http_Client</h2>
<p>Sometimes we may not always have a test adapter to pass in, or we may instead want to test that (for example) a single HTTP Post request is made. Often a library will not be structured in a manner which lends itself for unit tests, and you may often find yourself needing to sub-class the library and modify parts of its behaviour &#8211; however this is probably error prone and time consuming. Thankfully, PHPUnit can help &#8211; with its test double support (see <a href="http://www.phpunit.de/manual/3.5/en/test-doubles.html">here</a> for PHPUnit&#8217;s docs on this)</p>
<p>A basic example follows -</p>
<pre class="brush: php; title: ; notranslate">
public function testPostOnce() {
    $m = $this-&gt;androidGcmService;
    // create the doppleganger Zend_Http_Client object, only mocking the 'request' method
    // other methods will function as expected.
    $mock = $this-&gt;getMock('Zend_Http_Client'), array('request'));
    $http_body_text = 'some.json.goes.here';
    $fakeResponse = new Zend_Http_Response('200', array(), $http_body_text);
    // tell PHPUnit that the 'request' method should only be called once, and must be a HTTP POST.
    $mock-&gt;expects($this-&gt;once())-&gt;method('request')-&gt;with($this-&gt;equalsTo('POST'))-&gt;will($this-&gt;returnValue($fakeResponse));
    $m-&gt;setHttpClient($mock);
    // set recipients &amp; payload etc.
    $ok = $m-&gt;send();
    $this-&gt;assertTrue($ok);
}
</pre>
<p>When we call the &#8216;getMock&#8217; function we provide the name of the class we which to &#8216;extend&#8217;. By default, PHPUnit will mock all methods of the specified class &#8211; and calling them will return null. In our case we only want to mock one method &#8211; &#8216;request&#8217;, and leave all other methods unchanged.</p>
<p>So, the test checks that our class will call the request method once (and once only) and, when doing so, there will be one argument (&#8216;POST&#8217;). When the method is called, it will return a pre-populated &#8216;fake&#8217; response ($fakeResponse).</p>
<p>PHPUnit is doing some magic behind the scenes when creating the mock/double instance, in that it is extending the parent (Zend_Http_Client). This is good &#8211; it allows our type hints to function correctly (setHttpClient expects a class of type Zend_Http_Client) and redefines the request() method. </p>
<p>The above is much easier than us creating a new class extending Zend_Http_Client just for the purposes of this test.</p>
<p>As an example, if the Mobile_AndroidPushMessage class actually made a GET request by &#8216;mistake&#8217; you&#8217;d see output like :</p>
<p>Expectation failed for method name is equal to <string:request> when invoked 1 time(s)<br />
Parameter 0 for invocation Zend_Http_Client::request('POST') does not match expected value.<br />
Failed asserting that two strings are equal.<br />
--- Expected<br />
+++ Actual<br />
@@ @@<br />
-'GET'<br />
+'POST'</p>
<p>Using the above, we can ensure that the Http Client is called once, check the parameters used and specify the return value. Again, this is without depending on the actual Google service.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.palepurple.co.uk/phpunit-testing-mock-and-double-objects/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Improving site performance using asynchronous database writes and memcache in PHP</title>
		<link>http://www.palepurple.co.uk/improving-site-performance-using-asynchronous-database-writes-and-memcache-in-php</link>
		<comments>http://www.palepurple.co.uk/improving-site-performance-using-asynchronous-database-writes-and-memcache-in-php#comments</comments>
		<pubDate>Tue, 05 Mar 2013 13:27:22 +0000</pubDate>
		<dc:creator>palepurple</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[asynchronous]]></category>
		<category><![CDATA[memcache]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://www.palepurple.co.uk/?p=596</guid>
		<description><![CDATA[Web applications often have a requirement to log information about a request to a database. In a contrived example this could just be a global counter which attempts to amaze visitors to your site about how many requests you&#8217;ve handled in the last hour/day/week/month. One approach to doing this is to update a database each [...]]]></description>
				<content:encoded><![CDATA[<p>Web applications often have a requirement to log information about a request to a database. In a contrived example this could just be a global counter which attempts to amaze visitors to your site about how many requests you&#8217;ve handled in the last hour/day/week/month. </p>
<p>One approach to doing this is to update a database each time a page of interest is accessed, however this isn&#8217;t very good for performance reasons. The article below illustrates the problem and how it could be done using an asynchronous background task.</p>
<p><span id="more-596"></span></p>
<h2>Initial implementation</h2>
<p>Starting with code like :</p>
<pre class="brush: php; title: ; notranslate">
$todaysDate = date('Ymd');
$db-&gt;query(&quot;INSERT INTO site_hits (date, count)
    VALUES (?, ?) ON DUPLICATE KEY UPDATE count = count + 1&quot;, array($todaysDate, 1));
</pre>
<p>We could use the MySQL &#8216;ON DUPLICATE KEY&#8217; check, and having a unique key constraint on the &#8216;date&#8217; field we can ensure we have an incrementing number for each day. This number is being updated in real time (hopefully good). The above can be enhanced slightly by changing it to be &#8216;INSERT DELAYED&#8217;, which should stop MySQL waiting for the query to execute before returning control back to your PHP code.</p>
<h2>The problem</h2>
<p>The drawback to the above is that every page hit now results in a write to the database &#8211; so although your users will see that figure incremented each time they refresh a page, the data will not be cached by MySQL and will result in the server having to read/write from disk all the time. Under normal operations this may not be a problem &#8211; but if the site experiences a sudden spike in traffic, you&#8217;ll probably wish your I/O bandwidth wasn&#8217;t being wasted by the stat counter. </p>
<h2>A solution &#8211; asynchronous writing to the database</h2>
<p>A (hopefully) better approach is to separate out the writing of the counter to the database from that of the page load by using a &#8220;global variable&#8221; which resides in memory. Periodically this variable is written to disk by a scheduled task/cron job. Pseudo code to provide an example of this is shown below :</p>
<p>On each page load, we instead do the equivalent of :</p>
<pre class="brush: php; title: ; notranslate">
$counter = memcache_connect('MemcacheServerHost');
$cacheKey = 'my-global-counter';
$count = $counter-&gt;get($cacheKey);
if(empty($count)) {
    $count = 0;
}
$count++;
$counter-&gt;set($cacheKey, $count);
</pre>
<p>The above could also be done using APC or XCache variables instead of Memcache, depending on circumstance.</p>
<p>Next, create a cron job which runs every few minutes and does the equivalent of :</p>
<pre class="brush: php; title: ; notranslate">
$counter = memcache_connect('MemcacheServerHost');
$count = (int) $counter-&gt;get($cacheKey);
if($count &gt; 0) {
    $db-&gt;query(&quot;INSERT INTO site_hits (date, count) VALUES (?, ?) ON DUPLICATE KEY UPDATE count = count + &quot; . $count, array('20130305', $count));
    $counter-&gt;set($cacheKey, 0);
}
</pre>
<p>The above can also be modified slightly again, so we can delay writing to the database if the server load is above a given threshold or we otherwise think the server is too busy. At this point we&#8217;d probably want to wrap up our procedural style proof of concept code into a reasonable object &#8211; like the following :</p>
<pre class="brush: php; title: ; notranslate">
// ....
// In each page request :
SiteStatCounter::lazyRecordHit();
</pre>
<p>Cron job :</p>
<pre class="brush: php; title: ; notranslate">
// ....
// In the cron job :
if(Server::isNotBusy()) {
    SiteStatCounter::saveHitsToDb();
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.palepurple.co.uk/improving-site-performance-using-asynchronous-database-writes-and-memcache-in-php/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Kashdroid &#8211; now shipping</title>
		<link>http://www.palepurple.co.uk/kashdroid-now-shipping</link>
		<comments>http://www.palepurple.co.uk/kashdroid-now-shipping#comments</comments>
		<pubDate>Tue, 29 Jan 2013 18:40:15 +0000</pubDate>
		<dc:creator>palepurple</dc:creator>
				<category><![CDATA[android]]></category>
		<category><![CDATA[mobile apps]]></category>
		<category><![CDATA[palepurple]]></category>
		<category><![CDATA[accounts]]></category>
		<category><![CDATA[kashdroid]]></category>
		<category><![CDATA[kashflow]]></category>
		<category><![CDATA[mobile app]]></category>

		<guid isPermaLink="false">http://www.palepurple.co.uk/?p=553</guid>
		<description><![CDATA[We&#8217;ve finally published our Kashflow Android client &#8211; Kashdroid - which is now available on the Google Play store It&#8217;s available for free, and provides you with nearly all the functionality of the official web application &#8211; but integrates with your Android phone / tablet (e.g. address book sync, photo upload to receipts, maps/directions/navigation and quick [...]]]></description>
				<content:encoded><![CDATA[<p>We&#8217;ve finally<a title="Kashdroid – Kashflow accounting on the move" href="http://www.palepurple.co.uk/mobile-application-development/kashdroid-a-kashflow-app-for-android"> published our Kashflow Android client &#8211; Kashdroid</a> - which is now available on the <a title="Kashdroid - Google Play" href="https://play.google.com/store/apps/details?id=com.kashdroid">Google Play store</a></p>
<p>It&#8217;s available for free, and provides you with nearly all the functionality of the official web application &#8211; but integrates with your Android phone / tablet (e.g. address book sync, photo upload to receipts, maps/directions/navigation and quick dial etc).</p>
<p><span id="more-553"></span></p>
<p>It can work in an offline manner &#8211; assuming it&#8217;s already loaded the data once before, so should work well if you&#8217;re using it while mobile. You can even create invoices and so on while offline, and when network connectivity is next established it will submit them.</p>
<p>A few features are available after you purchase an in-app purchase (e.g. Widgets, Address book sync, Invoice creation etc).</p>
<p>Some screenshots follow &#8211; we&#8217;ve also added a video (which you should be able to see from the <a title="Kashdroid on Google Play" href="https://play.google.com/store/apps/details?id=com.kashdroid">Google Play listing</a>; as will be obvious, we&#8217;re not video production experts, so it&#8217;s probably a bit amateurish - when Kashdroid tops the app charts we&#8217;ll hire someone to do a proper version!).</p>
<h2>The initial welcome screen</h2>
<p>Showing some &#8220;pretty&#8221; graphs, if you tilt the device over or scroll down along with overview figures</p>
<p><img class="aligncenter size-medium wp-image-556" alt="device-2013-01-29-155723" src="http://www.palepurple.co.uk/wp-content/uploads/2013/01/device-2013-01-29-155723-180x300.png" width="180" height="300" /></p>
<p><img class="aligncenter size-medium wp-image-559" alt="device-2013-01-29-160040" src="http://www.palepurple.co.uk/wp-content/uploads/2013/01/device-2013-01-29-160040-300x180.png" width="300" height="180" /></p>
<h2>Customer Listing</h2>
<p>View your customer&#8217;s details &#8211; linking into the phone/maps/navigation etc</p>
<p><img class="aligncenter size-medium wp-image-558" alt="device-2013-01-29-160022" src="http://www.palepurple.co.uk/wp-content/uploads/2013/01/device-2013-01-29-160022-180x300.png" width="180" height="300" /></p>
<p>We&#8217;ve created different views / layouts for different orientations &#8211; so it&#8217;s nice to use on larger devices like tablets.</p>
<p><img class="aligncenter size-medium wp-image-561" alt="device-2013-01-29-160114" src="http://www.palepurple.co.uk/wp-content/uploads/2013/01/device-2013-01-29-160114-300x180.png" width="300" height="180" /></p>
<h2>Invoices</h2>
<p>Colour coded to alert you to problematic customers</p>
<p><img class="aligncenter size-medium wp-image-557" alt="device-2013-01-29-155823" src="http://www.palepurple.co.uk/wp-content/uploads/2013/01/device-2013-01-29-155823-180x300.png" width="180" height="300" /></p>
<p>The app allows you to create/edit invoices,</p>
<p><img class="aligncenter size-medium wp-image-560" alt="device-2013-01-29-160055" src="http://www.palepurple.co.uk/wp-content/uploads/2013/01/device-2013-01-29-160055-300x180.png" width="300" height="180" /></p>
<p>As well as sending them to customers via email -</p>
<p><img class="aligncenter size-medium wp-image-564" alt="device-2013-01-29-160228" src="http://www.palepurple.co.uk/wp-content/uploads/2013/01/device-2013-01-29-160228-300x180.png" width="300" height="180" /></p>
<p><img class="aligncenter size-medium wp-image-562" alt="device-2013-01-29-160140" src="http://www.palepurple.co.uk/wp-content/uploads/2013/01/device-2013-01-29-160140-300x180.png" width="300" height="180" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.palepurple.co.uk/kashdroid-now-shipping/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Kashdroid &#8211; a kashflow client</title>
		<link>http://www.palepurple.co.uk/kashdroid-a-kashflow-client</link>
		<comments>http://www.palepurple.co.uk/kashdroid-a-kashflow-client#comments</comments>
		<pubDate>Mon, 17 Dec 2012 17:06:56 +0000</pubDate>
		<dc:creator>palepurple</dc:creator>
				<category><![CDATA[mobile apps]]></category>

		<guid isPermaLink="false">http://www.palepurple.co.uk/?p=547</guid>
		<description><![CDATA[We&#8217;ve been busy writing a Kashflow Android app over the last few months; we think it&#8217;s pretty good, and would like to find a few beta testers &#8211; further information is available here]]></description>
				<content:encoded><![CDATA[<p>We&#8217;ve been busy writing a Kashflow Android app over the last few months; we think it&#8217;s pretty good, and would like to find a few beta testers &#8211; further information is <a title="Kashdroid – a Kashflow app for Android" href="http://www.palepurple.co.uk/kashdroid-a-kashflow-app-for-android">available here</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.palepurple.co.uk/kashdroid-a-kashflow-client/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Spam detection / tagging for a forum</title>
		<link>http://www.palepurple.co.uk/forum-spam-detection-and-tagging</link>
		<comments>http://www.palepurple.co.uk/forum-spam-detection-and-tagging#comments</comments>
		<pubDate>Thu, 22 Nov 2012 19:15:00 +0000</pubDate>
		<dc:creator>palepurple</dc:creator>
				<category><![CDATA[systems administration]]></category>
		<category><![CDATA[wordpress]]></category>
		<category><![CDATA[akismet]]></category>
		<category><![CDATA[bb-press]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[spam]]></category>

		<guid isPermaLink="false">http://www.palepurple.co.uk/?p=536</guid>
		<description><![CDATA[One customer has a forum which has been consistently plagued by spam &#8211; here&#8217;s a short writeup about what we found worked for us in an initial attempt at reducing the tidal wave of spam. The obvious initial caveat is that whatever measures may be detailed within this post are unlikely to be applicable for [...]]]></description>
				<content:encoded><![CDATA[<p>One customer has a forum which has been consistently plagued by spam &#8211; here&#8217;s a short writeup about what we found worked for us in an initial attempt at reducing the tidal wave of spam.</p>
<p><span id="more-536"></span></p>
<p>The obvious initial caveat is that whatever measures may be detailed within this post are unlikely to be applicable for other scenarios &#8211; spammers change tactics.</p>
<p>The site in question runs a WordPress based forum (bbpress) and receives a relatively high level of traffic (due to which, it&#8217;s presumably a tempting target for spam postings).</p>
<p>The forum was protected by Akismet, however even with Akismet, an unacceptable level of spam was getting through (Akismet reports that it&#8217;s stopped 106,000 spam in the last few weeks).</p>
<p>Initially we :</p>
<ol>
<li>Monitored the Apache access log &#8211; from a quick rummage it was easy to see the main pages involved were bb-post.php and bb-login.php (these were the main ones receiving POST requests), often from the same IP address in quick succession.</li>
<li>We edited bb-post.php and bb-login.php and dumped the contents of the PHP GET and POST data to disk &#8211; so we could see what sort of requests they were receiving.</li>
</ol>
<p>Based on the above, it was obvious that :</p>
<ol>
<li>A large volume of spammy POST requests originated in Asia, from a relatively small set of IP addresses.</li>
<li>A proportion of the spammy comments being posted were in Chinese (yet the site targets English speakers)</li>
<li>A proportion of the spammy comments were very large (i.e. &gt;4kb) while most legitimate posts were relatively small (2-4 lines of text).</li>
<li>Spammy comments often contained a high number of URLs</li>
</ol>
<p>While initially it was tempting to go for the quick &#8216;kill&#8217; and block subnet&#8217;s of users using an iptables rule, this wasn&#8217;t ideal as it could block legitimate users. The idea of having to maintain this pool of IP addresses wasn&#8217;t something to look forward to either &#8211; we&#8217;d need some way of identifying disreputable IP addresses/clients and automatically blocking them (perhaps with fail2ban in the future).</p>
<p>From experience with tools like SpamAssassin in the past, it seemed most sensible to adopt a scoring strategy with submitted posts &#8211; so, for example, we&#8217;ve added rules like the following :</p>
<ol>
<li>If the user agent matches a known pattern (E.g. IceWeasel on Debian), spam count += 1</li>
<li>If the user&#8217;s IP is located in Asia (through use of a geoip lookup), spam count += 1</li>
<li>If there are more than 3 URLs within the post content, spam score += 1</li>
<li>If the user&#8217;s IP is listed in real time blacklists, spam score += 1</li>
</ol>
<p>Given enough rules, and a reasonable threshold, the chance of wrongly identifying/tagging a post decreases. Now, if the post score is above a specific threshold (e.g. 5) we discard the request returning a non-descriptive error message.</p>
<p>We&#8217;ve implemented the request processing before the forum code itself runs/loads, and to minimise overhead, we only check HTTP post requests.</p>
<p>Appropriate logging of requests and data has allowed us to tweak the ruleset over the last 24 hours, to the extent that from a 12 hour period, we&#8217;ve identified 3501 spam comments, had one spam posting get through by mistake (which led to a rule update, and was caught by Akismet anyway) and so far, no false positives.</p>
<p>We&#8217;ve no intention of replacing Akismet &#8211; as it does an excellent job &#8211; but certain requests seemed to be easy to target &#8211; and by blocking them we can make a significant difference to the site&#8217;s performance and users.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.palepurple.co.uk/forum-spam-detection-and-tagging/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VCi Android application &#8211; completed</title>
		<link>http://www.palepurple.co.uk/vci-android-application-completed</link>
		<comments>http://www.palepurple.co.uk/vci-android-application-completed#comments</comments>
		<pubDate>Tue, 06 Nov 2012 13:34:35 +0000</pubDate>
		<dc:creator>palepurple</dc:creator>
				<category><![CDATA[mobile apps]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[app]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://www.palepurple.co.uk/?p=533</guid>
		<description><![CDATA[Our latest mobile app has just been finished and accepted by the customer (as version 1.0) &#8211; so congratulations to all concerned! It&#8217;s taken over 6 months of development. The application is used by engineers (so reports a listing of jobs available to them) and allows them to retrieve appropriate details and update job statuses [...]]]></description>
				<content:encoded><![CDATA[<p>Our<a title="VCI app on Google Play store" href="https://play.google.com/store/apps/details?id=uk.co.vantagecomputing.vci&amp;feature=search_result#?t=W251bGwsMSwyLDEsInVrLmNvLnZhbnRhZ2Vjb21wdXRpbmcudmNpIl0" target="_blank"> latest mobile app</a> has just been finished and accepted by the customer (as version 1.0) &#8211; so congratulations to all concerned! It&#8217;s taken over 6 months of development.</p>
<p>The application is used by engineers (so reports a listing of jobs available to them) and allows them to retrieve appropriate details and update job statuses as necessary.</p>
<p>Some interesting features :</p>
<ul>
<li>App functionality is customised based on a user&#8217;s permissions</li>
<li>App can run in an &#8216;offline&#8217; mode &#8211; queuing up packets to be sent when network connectivity is re-established (this allows it to work in areas of no network coverage, without affecting the user experience).</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.palepurple.co.uk/vci-android-application-completed/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Upcoming talk &#8211; Native vs Hybrid mobile application development (WooWeb)</title>
		<link>http://www.palepurple.co.uk/upcoming-talk-native-vs-hybrid-mobile-application-development-wooweb</link>
		<comments>http://www.palepurple.co.uk/upcoming-talk-native-vs-hybrid-mobile-application-development-wooweb#comments</comments>
		<pubDate>Thu, 18 Oct 2012 12:39:11 +0000</pubDate>
		<dc:creator>palepurple</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[mobile apps]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[phonegap]]></category>
		<category><![CDATA[wooweb]]></category>

		<guid isPermaLink="false">http://www.palepurple.co.uk/?p=525</guid>
		<description><![CDATA[Tonight (2012/10/18), David will give a short (15ish minute) talk at the WooWeb meeting (Worcester, UK) covering mobile app development &#8211; specifically the advantages and disadvantages of developing using frameworks like PhoneGap or writing an app natively. Contents: Why PhoneGap? Disadvantages / Advantages of PhoneGap? Disadvantages / Advantages of Native development Example(s)]]></description>
				<content:encoded><![CDATA[<p>Tonight (2012/10/18), David will give a short (15ish minute) talk at the <a title="WooWeb - Worcester Techie Meet" href="http://www.meetup.com/wooweb/events/78507132/">WooWeb</a> meeting (Worcester, UK) covering mobile app development &#8211; specifically the advantages and disadvantages of developing using frameworks like <a title="PhoneGap" href="http://phonegap.com/">PhoneGap</a> or writing an app natively.</p>
<p>Contents:</p>
<ul>
<li>Why PhoneGap?</li>
<li>Disadvantages / Advantages of PhoneGap?</li>
<li>Disadvantages / Advantages of Native development</li>
<li>Example(s)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.palepurple.co.uk/upcoming-talk-native-vs-hybrid-mobile-application-development-wooweb/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>What we&#8217;ve been up to lately (September 2012)</title>
		<link>http://www.palepurple.co.uk/what-we-have-been-up-to-lately-september-2012</link>
		<comments>http://www.palepurple.co.uk/what-we-have-been-up-to-lately-september-2012#comments</comments>
		<pubDate>Fri, 05 Oct 2012 12:04:29 +0000</pubDate>
		<dc:creator>palepurple</dc:creator>
				<category><![CDATA[palepurple]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[geospatial]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[phonegap]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.palepurple.co.uk/?p=508</guid>
		<description><![CDATA[We think we&#8217;ve been fairly busy lately, so here&#8217;s a summary covering some of the things we&#8217;ve been up to - Released Android and iPhone apps (Food Hygiene &#8211; for ScoresOnTheDoors.org.uk). The Android release is now all native (so quite responsive and fast); both have new graphics and an improved method of searching. (See the iTunes [...]]]></description>
				<content:encoded><![CDATA[<p>We think we&#8217;ve been fairly busy lately, so here&#8217;s a summary covering some of the things we&#8217;ve been up to -</p>
<ol>
<li>Released Android and iPhone apps (Food Hygiene &#8211; for ScoresOnTheDoors.org.uk). The Android release is now all native (so quite responsive and fast); both have new graphics and an improved method of searching. (See the <a title="Food Hygiene app on iTunes" href="http://itunes.apple.com/gb/app/food-hygiene/id378087298?mt=8">iTunes</a> or <a title="ScoresOnTheDoors app on Google Play" href="https://play.google.com/store/apps/details?id=com.sotd.foodhygiene&amp;feature=search_result#?t=W251bGwsMSwyLDEsImNvbS5zb3RkLmZvb2RoeWdpZW5lIl0.">Play</a> stores). (PhoneGap, Java, GeoSpatial searching).  We&#8217;re working on improvements to the iOS variant (better iPhone 5 support), and monitoring the feedback for each.</li>
<li>Integrated an existing LAMP application with <a title="Investec" href="http://www.investec.co.uk/">Investec</a> - allowing the user&#8217;s to submit lease / funding proposals (SOAP, XML. PHP5)</li>
<li>Busy refactoring an <a title="DrHauschka" href="http://www.drhauschka.co.uk">online shop</a> (to improve the mobile experience and enhance administration) (PHP5, MySQL)</li>
<li>Updates to the <a title="Scores On The Doors" href="http://www.scoresonthedoors.org.uk">ScoresOnTheDoors</a> website (improve performance, improve business feedback processing, improve data quality, improve the search experience)</li>
<li>Refactoring various applications to support PHP 5.4 which we&#8217;ve now moved to internally.</li>
<li>Migrated two Linux servers (either updating to Debian Squeeze, or to new hardware &#8211; for example for the <a title="Norton Owners Club" href="http://www.nortonownersclub.org">Norton Owners Club</a> )</li>
<li>Deployed some performance improvements/enhancements for WordPress based sites we support which use Varnish (E.g. <a title="Anorak" href="http://www.anorak.co.uk">Anorak.co.uk</a>). The cache-hit ratio is now about 90-95% &#8211; which is greatly helping responsiveness and server load. (WordPress, Varnish, Linux, MySQL)</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.palepurple.co.uk/what-we-have-been-up-to-lately-september-2012/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using disk: enhanced
Object Caching 598/715 objects using disk: basic

 Served from: www.palepurple.co.uk @ 2013-05-18 18:40:19 by W3 Total Cache -->