Technically Feasible - Posts Feed Confessions of an Enterprise Software Developer 2020-09-30T00:00:00-00:00 Michael Oldroyd New site design live 2011-03-08T00:00:00-00:00 <p>I'm happy to say that after buying my domain nearly four years ago, the site is now live. It's the first design that I am generally pleased with. Planned updates include adding support for mobile browsers, and the right sidebar needs some work, and comments lists have been left slightly neglected. There will be some QA tests of available mobile plug-ins at some stage, though I hope to avoid the &quot;Instant iOS&quot; styling that the popular ones provide.</p> <p>The design is under technical review, so if anybody notices any rendering issues feedback would be appreciated. The theme is built from the ground up, loosely based upon the skeleton theme Bianco. There's some CSS3 sprinkled around; differences in older browsers are intentionally not addressed. <a href="">Rendering bugs in IE6 won't be fixed</a>, lets not go back to the mosaic browser either :).</p> <p>Here's the list of versions for mainstream desktop browsers that we should look OK in:</p> <ul> <li>Internet Explorer 8+</li> <li>Firefox 3.5.x+</li> <li>Google Chrome / Chromium 8.0+</li> <li>Safari 4+</li> <li>Opera 10+</li> </ul> <p>The CSS that may be unsupported in these browsers depending on version:</p> <ul> <li>Text Shadow</li> <li>Box Shadow</li> <li>Border Radius</li> <li>Multiple Backgrounds</li> <li>2D Transforms</li> <li>Web Fonts</li> <li>CSS Gradients</li> </ul> <p>I won't be adding any shivs or poly-fills to the design, other than to add support for forward-looking technologies like HTML5 elements in up-to-date browsers only.</p> <p>It's also worth noting that the <a href="">Last.FM</a> plug-in was non-functional in WordPress 3.1 (the admin area was broken). I converted the code to a class-based widget. After doing some cleaning up, I may provide the code for others to use.</p> Record and Wipe CDRW discs via CLI 2011-03-08T00:00:00-00:00 <p>I find that the GUI tools in Ubuntu are buggy with some disks and drives that I use. The two lines that pretty much make &quot;Brasero&quot; redundant:</p> <h3 id="record-iso-to-disc">Record ISO to Disc <a class="direct-link" href="#record-iso-to-disc" aria-hidden="true">#</a></h3> <p>cdrecord -v dev=/dev/cdwriter isofile.iso</p> <h3 id="blank-a-cdrw">Blank a CDRW <a class="direct-link" href="#blank-a-cdrw" aria-hidden="true">#</a></h3> <p>sudo cdrecord blank=all -immed dev=/dev/cdrw</p> National No Smoking Day... 2011-03-09T00:00:00-00:00 <p>I am always disappointed when <a href="">No Smoking Day</a> comes along. Each year I realize it's upon us, feeling guilty having just bought a pack earlier in the morning. This year was no exception. Unlike most years though I have been trying to give up, consistently failing to do so since February.</p> <p>The difference this year is that I heard about the event from Twitter. It's a weird shift that I have noticed this year in particular, which started when I was following the coverage of the Egypt protests. One of the users I was following tweeted that Mubarak had fled the country, so I then tuned into the BBC's live coverage on my phone. I would probably have only heard about it in passing the following day whilst I caught up on the news. Obviously, this was a major international event, and generally you only find out about things which matter to whom you follow. I don't think I would ever rely on Twitter alone for news alerts, but it's good that we don't have to rely on an editorial process before we learn about events unfolding as they happen.</p> <p>On the smoking bit, I hope that others have been more inspired by today than myself. I will prevail eventually; a spot of willpower, and maybe slap a patch on tomorrow.</p> Templated 410 Error Pages in WordPress 2011-03-10T00:00:00-00:00 <blockquote> <p>The 4xx class of status code is intended for cases in which the client seems to have erred. Except when responding to a HEAD request, the server should include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. These status codes are applicable to any request method. <strong>User agents should display any included entity to the user.</strong> These are typically the most common error codes encountered while online.</p> <p><a href="" title="List of HTTP status codes">via Wikipedia</a></p> </blockquote> <p>410 is a less common status code to encounter. Back in the days of static web-sites, a physical file was either found or wasn't. If it wasn't, then it had been deleted (or never existed), which resulted in a 404 Not Found. For 410 to work, there had to be a record of what had been there previously and then been subsequently removed. Content usually moves, so redirects are suitable, but when removed it's usually a 404 page you will find.</p> <p>I just installed '410 for WordPress'. This handles the record of pages that are 'gone'. I <a href="" title="410 Test Page">added a link</a> to the list, but was disappointed when I was presented by a white page exclaiming <em>'Sorry, the page you requested has been permanently removed.'</em>. It's commonplace to template the 404 pages of sites (It's built into WordPress themeing), and a shame that WordPress just doesn't do the same when it encounters any other error code.</p> <p>If you inspect the source code for the plug-in, it fires an action 'wp_410_response'. All we really need to do is hook into this action, so open up the functions.php and add the following code:</p> <p>add_action('wp_410_response','load_410_template',1,0);<br> function load_410_template() {<br> require('410.php');<br> exit(0);<br> }</p> <p>Then create the 410.php file. I simply copied my 404.php, and changed the wording to match the context. That's two error codes covered!</p> <p>Enjoy :).</p> <p><strong>Update:</strong> The 0.5 release of the 410 for WordPress plug-in detects the 410.php file within the active template, so for this version and above there is no need to add the event hook into your template's functions.php. Just add the file and your covered.</p> Override core widget output in WordPress 2011-03-11T00:00:00-00:00 <p>Another snippet for the WordPress theme developers out there. I decided that the category widget didn't display the post count in a very nice-looking way. So I found a nice support post that demonstrates the user of add_filter() to alter output of the category widget. Not the most ideal way to do things in my opinion, but it does the job.</p> <p>add_filter('wp_list_categories', 'cat_count_span_inline');<br> function cat_count_span_inline($output) {<br> $output = str_replace(' (','',$output);<br> $output = str_replace(')','',$output);<br> return $output;<br> }</p> <p>Via <a href="" title="Wordpress Support"></a></p> Speeding up your page load times 2011-03-14T00:00:00-00:00 <p>Just by configuring your site and server in the right way, you can get much faster and responsive load times out of your CMS. With mainstream open source CMS software, you are given the ability to add bolt-on functionality. It's common for each of these add-ons to provide their own set of CSS definitions, images and JavaScript. This makes these add-ons easy to implement for non-technical users. If you aren't careful though, you will end up with tens or even hundreds of CSS and JavaScript files. This has a performance penalty, which can <a href="">affect your rankings in the SERPs</a>.</p> <p>For example, this site includes 44 requests for the home page (at the time of writing), which includes:</p> <ul> <li>The page itself</li> <li>Six external style-sheet links, Two included for the theme using @import</li> <li>Two in-line styles</li> <li>Four JavaScript files (jQuery and one plug-in, Modernizr, and one for the theme)</li> <li>Three web font files</li> <li>Images</li> </ul> <p>Being the first time I had checked, I'm slightly disappointed with this. Two of the style-sheets are unnecessary (<a href="">Last.FM</a> feed widget), as I have completely overridden the styling in my template. I will remove those two redundant CSS files from <a href="">Last.FM</a> feed. When I release the updated plug-in some day I will offer a switch to disable default styling! I would have to configure caching in Apache, or add a caching plug-in for WordPress. Currently external resources will have to be requested with an <a href="" title="If-modified-since on">If-modified-since</a> header for each page load. That's not to mention images, web fonts, flash animations, Java applets or anything else I could include in a web page.</p> <p>Over the next week, I hope to share with you some of the ways that you and I can get more out of our sites. Just by configuring your site and hosting settings, you can improve the browsing experience for your users. I will be using WordPress examples for the site configuration, but much of it will be relevant for other CMS software. I will also be referring to configuring Apache under a Linux-based operating system, though other operating systems and web server software will provide equivalent functionality.</p> Friday Update #1 2011-03-25T00:00:00-00:00 <p>My ten visitors may have noticed it was quiet over here for the last week and a half. For the last fortnight, I have been moving home. We began sorting and packing our belongings 3 weeks prior to that, yet we were still moving things out at 10:30 the night before the deadline. Probably because there was so much to clean and fix at the old place. It's quite amazing how much shit you can stuff into one house. In total it's taken a full week just to move our stuff and fix the place up. This includes 3 1/2 hours to get a sofa through a door, which was ridiculous. I did feel guilty that Bob - our sole surviving goldfish - was one of the last to move. <a href="" title="Hey! It's Firefox 4.0 Final!"><img src="" alt="" title="ff4"></a>I missed a few important milestones with all the madness. Firefox 4 (finally) got it's <a href="">final release three days ago</a>. I've been using the Minefield dailies for months, but the final release seems much more stable. The UI looks well polished under Windows 7, though I am quite disappointed in the &quot;Firefox&quot; menu button under Linux. Although I do have a special attachment with Firefox, Chrome is still better for me. I get desktop notifications for my mail, and various social media, which Firefox can't currently deliver. Google seems to have pushed out it's <a href="">refreshed Chrome/Chromium branding</a> (on the dev channel at least?). It's logo has lost the glossy spherical ball appearance, but it still looks like a multicoloured Poké Ball to me. It just doesn't sit right with me, though I'm no 'expert' in logo design. IE9 final came out just before on 14th (enough said).</p> <p>Those lucky Apple fans in the UK should by now have begun to receive their <a href="">iPad 2 orders today</a>. Nobody in my office has ordered one, though I know two of the guys have the first gen model. I don't know whether this is the exception or the trend, but I don't feel that Apple put enough effort into this one to justify upgrading. Not that I would be one to go out and buy an iPad anyway, I would much prefer a new <a href="">Galaxy Tab</a> (whatever the name ends up being) or <a href="">Dell Streak 7</a> for the same purpose. I much prefer my 5&quot; Dell Streak to an iOS device, it's screen is nicely sized, and not too big to hold to your face either!</p> <p>A more regular schedule of updates will resume now that I am able to think, hopefully with some news from the pending Australian Grand Prix. The McLaren team was on form today! Time for soup and a sammich...</p> Brief History of Fonts on the Web 2011-03-28T00:00:00-00:00 <p>For years, web designers have been yearning for custom fonts in web design. It's a real shame that web fonts didn't really take off with <a href="">IE4 in 1997</a>! The web was a different place back then, web design was a <a href="" title=" from Jan '97">pretty new thing</a>. Bandwidth was highly constrained at the time, so one could not imagine waiting for web fonts to load. Microsoft's container format of choice is also EOT, which is a Microsoft proprietary technology. Although everybody slates it's proprietary and closed nature, it was sort of a good idea. <a href="" title="EOT definition on Wikipedia">EOT provides domain locking, encryption, sub-setting and compression</a> - all good things for convincing those behemoth font foundries to allow web embedding licenses.</p> <p>Now is a better time than ever to move away from the standard web-safe fonts. Mainstream browsers have caught up with IE, over a decade later. Here is a short history lesson on the many techniques that Web Designers have been using to get beautiful type into their pages.</p> <h3 id="fonts-in-images">Fonts in Images <a class="direct-link" href="#fonts-in-images" aria-hidden="true">#</a></h3> <p>With the introduction of high-speed internet, the web has become a much more multimedia oriented place. With lack of web fonts in anything other than IE, developers concentrated on alternative methods to get aesthetically pleasing fonts onto their pages. We began using images to display custom typefaces. This was great, as you could have the page look how your designer wanted it to. Not very accessible though, as people with screen-readers couldn't read any of your image-font headings.</p> <p>Designers began using images as backgrounds using CSS, using various methods to hide the original text (with varying success). These methods make certain assumptions on how <a href="">screen readers handle CSS and Flash content</a>. Unfortunately, one of the more popular accessibility suites for Windows - <a href="">JAWS</a> - wouldn't read &quot;hidden&quot; text as everybody expected it to. This lead to the use of overflow: hidden and a negative text indent.</p> <p>Licensing for this method is the least worrying. You just need to have the font on your machine and produce an image to then upload, which doesn't require a font embedding license. If you are generating images from a server, you may potentially <a href="">require some form of extended license</a>.</p> <h3 id="fonts-in-flash">Fonts in Flash <a class="direct-link" href="#fonts-in-flash" aria-hidden="true">#</a></h3> <p>The other issue with using images was the effort involved. Unless you had a complex set of server code to dynamically generate images (and produced 24-bit transparent PNGs or solid-colour background), you would have to create the header image text each time you wanted to change the text. If you generated a lot of content, this was going to lead to a lot of extra work. This is where <a href="">Flash Replacement techniques</a> came about. These solutions use <a href="">Adobe Flash</a> .swf files, which have the ability to store font data embedded, and can then be sent text data as a parameter. This is then used to replace the text on the page with the desired font.</p> <p>This solution had it's problems. Some web browsers / versions of flash under more <a href="">exotic operating systems</a> lacked support for the <a href="">wmode=transparent feature,</a> which renders the background of a flash element as transparent; letting the html content beneath the flash show through. Lacking support for wmode also breaks overlays in some cases, so Flash has to be hidden when an in-content overlay (e.g. a lightbox) is displayed on the page. There is also the issue of font licensing, as the flash file can be copied and re-used on other web sites in some cases. JavaScript is required to dynamically replace text. It also requires that Flash Player plug-in be installed an enabled on the system, though in most cases detection occurs before replacement is done. The same accessibility issues can arise as with images too, so text-indent would still be used as with image replacement.</p> <h3 id="fonts-in-javascript">Fonts in JavaScript <a class="direct-link" href="#fonts-in-javascript" aria-hidden="true">#</a></h3> <p>More recent additions have been developed, in the wake of <a href="">HTML5 canvas</a> element support. These involve re-rendering the font into JavaScript code. <a href="">Cufón</a> and <a href="">Typeface.js</a> are two such solutions. Support is provided by canvas in fairly recent browsers - VML in IE -  so coverage of browsers is relatively high. VML was added to <a href="">IE from version 5</a>. These solutions typically support sub-setting, and cufón includes domain locking, though not true encryption so reverse engineering is feasible (this lacks benefit in most cases, as the conversion process simplifies the font paths to lower file-sizes).</p> <p>The drawbacks to these methods are that <a href="">styling</a> support is limited, even text selection is an issue that has to be worked around. JavaScript has to be enabled for replacement to work, too. Licensing issues are similar to that of Flash Techniques.</p> <h3 id="css3-font-face-support">CSS3 Font Face Support <a class="direct-link" href="#css3-font-face-support" aria-hidden="true">#</a></h3> <p>The most favoured method for font embedding is through the use of CSS Fonts, which make up one of the modules of the <a href="">CSS3 standard</a>. It's considered a Working Draft over at the w3c, although it's usage in modern web design is on the increase and browsers are now backing it. The specification was included in CSS2 but <a href="">removed from CSS2.1</a>. Browsers support different font formats, as mentioned EOT is IE only. Chrome, Firefox, IE9 and Opera support the <a href="">WOFF format</a>, which is a step forward for open specification web font formats. <a href="">SVG fonts</a> are supported by Safari and Opera. <a href="">TTF fonts</a> are supported by Firefox, Chrome, Safari, Opera and IE9. A good compatibility list is provided at <a href=""></a>, though bare in mind that it's a wiki page.</p> <p>The main drawbacks to font-face font embedding are unanimous format support. TTF is the most widely supported, though until very recently you were required to use SVG for supporting iPads and iPhones. Opera only added support for TTF fonts since version 10. So for each version of each browser you hope to support, you need to serve up the correct set of font formats.</p> <p>For each font-style (bold, italic, oblique, etc.) you will need to provide a separate font file. Some web browsers will try to emulate these if they are missing, though <a href="">support for this isn't unanimous</a>.</p> <p>There is also the potential to have the same font licensing issues for commercial fonts.</p> <h3 id="why-css-font-embedding-is-the-future">Why CSS Font Embedding is the Future <a class="direct-link" href="#why-css-font-embedding-is-the-future" aria-hidden="true">#</a></h3> <p>A lot of the time, you can side-step the licensing issue simply by using fonts which are licensed for web embedding, and where applicable free to use for commercial purposes. <a href="">Font Squirrel</a> is a great resource for such fonts, though I would advise that you check over the licence for yourself before using the fonts. The fantastic feature of this site, is that they (in many cases) provide a <a href="">font-face download kit</a> for their fonts. This includes all the subsets and formats needed for browser support, and a CSS file ready for embedding. What makes this site really special is that you can provide your own font and upload it, and you get a <a href="">font-face kit ready made for you</a>. Always check that the font you wish to use supports the character set that you intend on using. The issue with free fonts is that they can quite often be incomplete, and quite often they lack the polish and effort put into commercial fonts.</p> <p>You no longer need to host your own fonts, either. <a href="">Typekit</a> is just one 3rd party service which provides commercial web fonts, ready to embed, and you sidestep any licensing issues. Google also provides the <a href="">Google Font Directory</a>, which is basically a CDN for freely available web fonts. These services also only provide the supported font format to the requesting browser, whereas with home-brew solutions rely on multiple declarations and <a href="">smiley faces</a>.</p> <p>This site is using the <a href="">League Gothic kit</a> from Font Squirrel (as I couldn't find a CDN for it), and <a href=";subset=latin">Droid Sans</a> via the Google Font CDN. My chosen fallbacks for League Gothic look awful, so I think that some more alterations could be in the pipeline.</p> WordPress Post Formats made easy 2011-03-30T00:00:00-00:00 <p><a href=""></a></p> Filter a category from your WordPress Blog 2011-04-05T00:00:00-00:00 <p>I'm pretty new to working with the internals of WordPress. It seems very easy to settle for installing hundreds of plug-ins to achieve the simplest of tasks. Plug-ins are awkward for me, as they have the tendancy to not quite do what I want them to. I end up messing with them in some way which breaks updates.</p> <p>A few days ago, I decided that I would like to replicate my Twitter content onto my site. I added a plug-in which facilitated the import, but I was finding that my main page was being swamped by Tweets. This was drowning out the actual post content (which coincidently I take a lot more effort to write!).</p> <p>I did a little research, and <a href="">found what on the surface seems like a good idea</a>. The problem with this method is that it occurs in &quot;the loop&quot;. Once you are in &quot;the loop&quot;, the post query has been performed - and paginated. So you can only exclude results, and for each excluded result you have one less post on the page. For example: if there were 10 excluded posts on a page and a post limit per page of 10, the home page would be blank.</p> <p>The solution was to formulate a little <a href="">post query filter</a> which does the task. The post query filters are great; they allow us to alter the post query prior to getting the post list and applying pagination. The function is currently hard-coded to the category I wanted to filter out; just copy-paste into functions.php of your theme, changing the number '30' for the ID of your category:</p> <p>function filter_home_where($where) {<br> global $wpdb;<br> if (is_home() || is_feed()) {<br> $where .= &quot; AND {$wpdb-&gt;posts}.ID NOT IN (<br> SELECT object_id<br> FROM {$wpdb-&gt;term_relationships} as tr<br> LEFT JOIN {$wpdb-&gt;term_taxonomy} as tt<br> ON tr.term_taxonomy_id = tt.term_taxonomy_id<br> WHERE tt.term_id = 30)&quot;;<br> }<br> return $where;<br> }<br> add_filter('posts_where', 'filter_home_where');</p> <p>The function adds an additional WHERE clause to the query, in the form of a sub-query. This SELECTs a list of post id's from the term_relationships table (which contains the object_id, which is basically post_id for our use case), joined to the term_taxonomy table (for term_id, which is essentially category_id). Any post ID returned by the sub query will be excluded in the post list.</p> <p>This isn't just limited to Categories. You can filter on any taxonomy data, such as tag, post formats, etc. It would be simple to tweak the query to apply multiple taxonomy exclusions in one pass too. I have been working on some form of admin area, which I am adding to the theme, so I hope to follow up with that once it's in place.</p> <p>The only thing I noticed is that it doesn't filter the category from <a href="">/feed</a>, whereas it does filter <a href="">/feed/atom</a> and <a href="http://michaeloldroyd/feed/rss">/feed/rss</a>. I don't know if this is intentional or a bug. It's something I plan to investigate a little more. I haven't tried this where the home page has been overridden as a static page, either.</p> Playing with Incarna in EVE Online 2011-04-21T00:00:00-00:00 <p><img src="" alt="" title="2011.">Although my online gaming time has been sparse recently, I thought it would be good to take some time out and look at the new features coming to <a href="">EVE Online</a> this year. After reading the <a href=";bid=874">devlog post</a> that <a href=";bid=860">Incarna</a> functionality was being tested on Duality, I decided to take a peek. After spending quite a lot of time crafting my character, I was quite happy to wait an hour to grab the Duality client (4GB+) and take my avatar for a spin.</p> <h3 id="interface">Interface <a class="direct-link" href="#interface" aria-hidden="true">#</a></h3> <p>You have access to most of the game windows through physical objects within the Captain's Quarters. There is a small screen on the &quot;coffee table&quot;, and a weird ship-like object which allows you to access the fitting and ship hangar. I'm sure that the tutorials will clarify what you can interact with (and how), but I didn't initially realise how you interacted with these objects. You right-click to access a context menu.</p> <h3 id="tutorials">Tutorials <a class="direct-link" href="#tutorials" aria-hidden="true">#</a></h3> <p>I haven't had a chance to preview the new user tutorials yet, so I can't really comment on them. These are available to try in the Emrayur system, <a href=";bid=874">read the devlog post</a> for more details.</p> <h3 id="performance">Performance <a class="direct-link" href="#performance" aria-hidden="true">#</a></h3> <p>I was quite surprised how well it rendered on my ageing hardware. I usually average around 25-40 FPS in and out of stations with everything on high and 2x anti-aliasing and no bloom, though that's across dual wide monitors. I can't compare this as you have to turn of AA to test Incarna, though I was getting around 25 FPS with the required settings.</p> <p>I don't have my machine spec's to hand so I'll have to update with these.</p> <h3 id="interaction-issues">Interaction Issues <a class="direct-link" href="#interaction-issues" aria-hidden="true">#</a></h3> <p>As advertised, the experience is a little clunky. I got stuck on two occasions; once on the steps leading down the the lowered floor section leading to the &quot;screens&quot;, and again outside the captains quarters leading to what seems to be the docking area. If you make your character walk to an inaccessible area (such as behind the central screen), they will continue the walking animation on the spot. The only other thing I found was that when you &quot;sit&quot; on the couch, you squat in front of it.</p> <p>Whether you love or hate the idea of the Ambulation / Walking in Stations / Incarna functionality coming to EVE online, it will provide an extra level of interaction with the game once it's introduced. I'm quite excited by the idea of it and can't wait to see what's coming down the line, though I am on the fence regarding how much it will add to the overall player experience for the majority of subscribers. It's got to be more interesting than <a href="">ship spinning</a> though, surely?</p> <p>Bear in mind that it's not complete, and that <a href="">CCP</a> have been kind enough to give access to the test server in the first place. The best thing you can do is to provide feedback on issues if you decide to test any pre-release software.</p> <h3 id="incarna-on-the-duality-test-server">Incarna on the Duality Test Server <a class="direct-link" href="#incarna-on-the-duality-test-server" aria-hidden="true">#</a></h3> <p><a href=""></a></p> <p>This isn't my video, just one recommended by a friend :).</p> File validation with jQuery and HTML5 2011-10-12T00:00:00-00:00 <p>I have been dealing with file uploads a lot recently, and I stumbled upon a few different methods for validating files which provide both basic file-type checking and file size checking before uploading.</p> <p>The web app was already making use of <a href="">jQuery</a> with the excellent <a href="">jQuery validation plugin</a>; so I wrote a couple of extended validators.</p> <h3 id="file-type-validation-(via-file-extension)">File type validation (via file extension) <a class="direct-link" href="#file-type-validation-(via-file-extension)" aria-hidden="true">#</a></h3> <p>This defines an regular expression of valid file extensions, which we currently define as gif, jpeg/jpg and png.</p> <p>This form of validation is quite widely supported; the file input exposes a file path (sometimes a fake path) along with the filename of the original file.</p> <p>/**<br> * Basic file type validation support for common web-safe image formats.<br> *<br> * @date 2011-09-22<br> * @author Michael Oldroyd<br> * @version 1.0<br> */<br> $.validator.addMethod(<br> &quot;image&quot;,<br> function(value, element) {<br> if ($(element).attr('type') == &quot;file&quot;<br> &amp;&amp; ($(element).hasClass('required')<br> || $(element).get(0).value.length &gt; 0))<br> return value.match(/\.([png|gif|jpg|jpeg])$/i);<br> else<br> return true;<br> },<br> &quot;Invalid file type. Only PNG, JPEG and GIF formats are supported&quot;<br> );</p> <h3 id="file-size-validation">File size validation <a class="direct-link" href="#file-size-validation" aria-hidden="true">#</a></h3> <p>This validator makes use of some of the HTML5 file APIs.This means that it won't work with browsers which haven't implemented the <a href="">HTML5 File API module</a>. The two methods we are interested with are window.File and window.FileList. We check for these first, and automatically &quot;pass&quot; the validator if they aren't available so that it gracefully degrades.</p> <p>The validator supports multiple files per input, and multiple file inputs per form. So each time the validator fires, we find all file inputs in the form; iterating over each, accessing the files property and iterating this too. Each time we increment the total size (in bytes) with the size of the file in the list.</p> <p>If the files attached exceed the MAX_FILE_SIZE input element's value, the validator fails; otherwise it passes. If no element named MAX_FILE_SIZE is found, the validator also passes as we have no basis for comparison.</p> <p>/**<br> * Provides file upload limit validation, according to server rules (i.e.<br> * MAX_UPLOAD_FILESIZE). This validator uses a MAX_FILE_SIZE input element<br> * and checks the total size of selected file(s) against this value.<br> *<br> * @date 2011-09-23<br> * @author Michael Oldroyd<br> * @version 1.0<br> */<br> $.validator.addMethod(<br>  &quot;filesize&quot;,<br>  function(value, element) {<br>   if (window.File &amp;&amp; window.FileList) {<br>    if ($(element).attr('type') == &quot;file&quot;<br>     &amp;&amp; ($(element).hasClass('required')<br>     || element.files.length &gt; 0)) {<br>     var size  = 0;<br>     var $form = $(element).parents('form').first();<br>     var $fel = $form.find('input[type=file]');<br>     var $max = $form.find('input[name=MAX_FILE_SIZE]').first();<br>     if ($max) {<br>      for (var j=0, fo; fo=$fel[j]; j++) {<br>       files  = fo.files;<br>       for (var i=0, f; f=files[i]; i++) {<br>        size += f.size;<br>       }<br>      }<br>      return size &lt;= $max.val();<br>     }<br>    }<br>   }<br>   return true;<br>  },<br>  &quot;The file(s) selected exceed the file size limit. Please choose another file.&quot;<br> );</p> <p>Click here for the demo</p> <p>The code for this example was in part inspired by the work of Eric Bidelman at <a href="">HTML5 Rocks</a>.</p> <p>It's also worth noting that I don't claim that these implementations are perfect, so I would welcome any improvements or suggestions to improve the validators above.</p> Understanding SEF routing for Joomla Components 2011-10-19T00:00:00-00:00 <p>Developing bespoke components for Joomla can be quite daunting. There are a lot of resources, books, dev articles and the API documentation; these try to give you a basic understanding of how things should be laid out. A lot of these work on the premise of learning by doing. Many of the documented examples from the API point developers to look at the core components to see how it works.</p> <p>Configuring an effective component router can be quite tricky, as it depends on how complex your component is. This article serves as a reference for how routing works in Joomla, why it's important, and how you could make better use of component routing in your projects.</p> <h3 id="routing-modes">Routing Modes <a class="direct-link" href="#routing-modes" aria-hidden="true">#</a></h3> <p>There three different implementations of SEF that Joomla supports;</p> <ol> <li>Rewrites via Apache mod_rewrite or IIS URL rewriter. (e.g. <a href=""></a>)</li> <li>Rewrites via php implementation. (e.g. <a href=""></a>)</li> <li>Rewrites disabled. (e.g. <a href=";view=view&amp;layout=layout">;view=view&amp;layout=layout</a>)</li> </ol> <p>Options 1 &amp; 2 would use your router to build and parse the route. The third would not require parsing, as we have the request variables in a raw form anyway. As far as I am aware, your routes won't be built either when SEF URLs are disabled. Without specifying menu item ids in your routes you won't be getting menu item matching. The &quot;.html&quot; suffix on the end of the link in the first two is an optional setting in the Joomla configuration. <a href="">Find out more about enabling these options here</a></p> <h3 id="integrating-with-menu-items">Integrating with Menu Items <a class="direct-link" href="#integrating-with-menu-items" aria-hidden="true">#</a></h3> <p>For the purposes of SEF URIs, there are two levels of support which you need to be aware of:</p> <ol> <li>SEF URIs without a menu item (e.g. <a href=""></a>)</li> <li>SEF URIs with a menu item. (e.g. <a href=""></a>)</li> </ol> <p>For basic routing, such as supporting menu item links, you can use a really basic 'build route' and ignore the 'parse route' function altogether. This means that you need to write some code which checks the route query string against a list of menu items for your component. If there is a successful match, the query string will be replaced with the menu link. Your router should remove the matched query items from the request array, which is passed in by reference. Any unmatched variables should be left untouched, and will be appended to the URI.</p> <h4 id="why-is-this-important%3F">Why is this important? <a class="direct-link" href="#why-is-this-important%3F" aria-hidden="true">#</a></h4> <p>There are several reasons why you should ensure that your component properly integrates with menu items.</p> <ul> <li>Modules are assigned directly to menu items. If your router doesn't match match up to a menu item then the end user can't assign modules to the layout.</li> <li>Menu items can be configured with access levels. If Joomla can't match a menu item, then the access restriction that was applied to the menu item won't apply.</li> <li>You can set up your router to match custom, application specific variables. This allows your component to have finer grained menu items of the same type. One example of this is the content core component, which allows you to link to specific content items.</li> </ul> <h3 id="special-variables">Special Variables <a class="direct-link" href="#special-variables" aria-hidden="true">#</a></h3> <p>You can include any GET variables in your query, however there are some which get special treatment from Joomla:</p> <ul> <li>option points to a component, and uses the full name (such as com_name)</li> <li>view points to a view folder within the component, the default is typically default</li> <li>layout points to a layout within a view, the default is typically default</li> <li>task points to a function within a view (e.g. view.html.php), the default is typically display</li> <li>tmpl allows you to choose the display mode. You can choose tmpl=component to capture just the component output without your template.</li> <li>Itemid is the menu item id from the menu table.</li> </ul> <p>The good thing about this is that it's down to you how you use variables. When you construct menu items in the Joomla, they will be created as links to view folders and layout files. Menu items will include the option name of your component, for internal routing. They will include a view, unless the link is to a view named default in which case it will be omitted. The layout name will similarly be included unless it is named default.</p> <h3 id="outputting-component-routes">Outputting component routes <a class="direct-link" href="#outputting-component-routes" aria-hidden="true">#</a></h3> <p>As you application grows, you will inevitably want to create links (or HTML redirects) to other areas of your application. This is where we use the JRoute::_() static method. This method basically acts as a URI renderer, and it will output relative URIs according to your site configuration. Here are a few examples:</p> <p>JRoute::_('index.php?view=cheese');</p> <p>This would be used internally to link to the default layout of the cheese view within the component. In Joomla 1.5, you had to specify the option for every call, but from 1.6+ this can be omitted for internal component links.</p> <p>JRoute::_('index.php?view=cheese&amp;layout=stilton');</p> <p>This would again internally link to the cheese view, but instead would invoke the stilton layout file.</p> <p>JRoute::_('index.php?option=com_content&amp;view=article&amp;id=5');</p> <p>This would be used to create a link to the core content component, and would link to the single article display page. It should also load and display article with id number 5 from the database. When linking to external components, that component's route functions are used to determine the correct route.</p> <p>JRoute::_() includes a second parameter, which is a switch for html encoding. By default, the method will html encode it's output. This doesn't work with HTTP redirects, because any ampersands will be converted to &amp; which breaks your variable names. So when using this function outside of output to a HTML page, this needs to be set to false.</p> <p>If you would like to see some examples in action, there are some in this <a href="">wiki entry</a> on the Joomla documentation wiki. I'll be following up with a couple of practical techniques that I have put together in due course.</p> Quantity vs Quality 2011-11-03T00:00:00-00:00 <p>As my reader may have noticed, I took a hiatus from posting on here until recently. I came back a couple of weeks ago to try and get back into the habit of writing posts, and I set myself the target of publishing a post every Wednesday. I spend around about 12 hours a day on the web, or coding for it. Being able to share some of the fruits of that time ought to come naturally.</p> <p>I suffer from a lack of confidence in my writing. My last post was just over two weeks ago — it's not for a lack of ideas that I missed my self-imposed schedule. I ended up sitting here on the netbook thinking of how to construct a post about responsive design techniques. It's something that gets me quite excited; a plethora of different techniques; a load of different frameworks and even more discussion on the web to reference. I just simply could not get it out into a post. I instead chose to watch the TV.</p> <p>The second problem I have is that I am a border-line perfectionist. A lot of effort goes into producing quality content. I also try to produce high quality code in my work, but code comes a lot more naturally to me than writing. I can spend an hour staring at a paragraph because it doesn't seem to be worded right, whole posts get discarded because the topic or content feel forced; It's frustrating to say the least.</p> <p>Trying to write a quality article a week is just not feasible for me. There are simply not enough hours in the day! So there will be no unrealistic, defeatist schedule. I'm going to aim to write something decent about once a month; a piece of work that I put a lot of effort into over an extended period of time.</p> <p>That's not to say that you should check in once a month, my faithful reader.</p> <p>One of my aims is to make this space a little more personal. I plan to share a lot more snippets of code and techniques that I pick up, and try to share a little more about who I am along the way too. My social media profiles have been gathering dust recently — Let's face it though, there are only so many sites one man can actively engage with regularly without fear of getting fired. So i'll probably end up choosing the core ones that I actually enjoy using (of which there are quote a few) and duplicate content to the rest.</p> <p>The last thing I am aiming for is a new design. I do like the existing theme to a point, but this is also where I want to put into practice some of the ideas that I don't get to put into practice at work. It's not going to be rushed, but the timeline is going to be less than it took me for the first design. We will hope for a release before the new year. I'm starting from the fundamentals, drawing up ideas in my gorgeous new Moleskine® notebook. The plan is to integrate the responsive design fundamentals that I have been researching and lose some of the crud that has crept into the current design.</p> A basic date_diff for PHP 5.2 2011-11-04T00:00:00-00:00 <p>I came across a problem having developed a site with a PHP 5.3 environment, when moving the site to the live environment the server was running PHP 5.2. Whilst the server gets upgraded I looked into getting the code to work in some form in the meantime. The main issues are the functions lcfirst() and date_diff(). The former is a <a href="">simple fix</a>, a function which lower-cases the first letter of a string — I was surprised this was only introduced in 5.3!</p> <p>if(function_exists('lcfirst') === false) {<br> function lcfirst($str) {<br> $str[0] = strtolower($str[0]);<br> return $str;<br> }<br> }</p> <p>The issue with date_diff is it's reliance on the DateInterval class, which as the name suggests is a native object introduced in 5.3 to represent the interval between two dates. The function ought to be functionally equivalent between both PHP 5.2 and 5.3, otherwise it would be useless. <a href="">I found something that I am fairly happy with</a>, in that it accepts the same inputs and outputs as the native 5.3 version. The calculations aren't correct though, unfortunately, and it's an incomplete implementation. I corrected the &quot;invert&quot; data type values returned, and also added support for &quot;days&quot; as these are the main things I am using.</p> <p>/**<br> * Workaround for PHP &lt; 5.3.0<br> */<br> if(!function_exists('date_diff')) {<br> class DateInterval {<br> public $y;<br> public $m;<br> public $d;<br> public $h;<br> public $i;<br> public $s;<br> public $invert;<br> public $days;</p> <pre><code>public function format($format) { $format = str\_replace('%R%y', ($this-&gt;invert ? '-' : '+') . $this-&gt;y, $format); $format = str\_replace('%R%m', ($this-&gt;invert ? '-' : '+') . $this-&gt;m, $format); $format = str\_replace('%R%d', ($this-&gt;invert ? '-' : '+') . $this-&gt;d, $format); $format = str\_replace('%R%h', ($this-&gt;invert ? '-' : '+') . $this-&gt;h, $format); $format = str\_replace('%R%i', ($this-&gt;invert ? '-' : '+') . $this-&gt;i, $format); $format = str\_replace('%R%s', ($this-&gt;invert ? '-' : '+') . $this-&gt;s, $format); $format = str\_replace('%y', $this-&gt;y, $format); $format = str\_replace('%m', $this-&gt;m, $format); $format = str\_replace('%d', $this-&gt;d, $format); $format = str\_replace('%h', $this-&gt;h, $format); $format = str\_replace('%i', $this-&gt;i, $format); $format = str\_replace('%s', $this-&gt;s, $format); return $format; } </code></pre> <p>}</p> <p>function date_diff(DateTime $date1, DateTime $date2) {</p> <pre><code>$diff = new DateInterval(); if($date1 &gt; $date2) { $tmp = $date1; $date1 = $date2; $date2 = $tmp; $diff-&gt;invert = 1; } else { $diff-&gt;invert = 0; } $diff-&gt;y = ((int) $date2-&gt;format('Y')) - ((int) $date1-&gt;format('Y')); $diff-&gt;m = ((int) $date2-&gt;format('n')) - ((int) $date1-&gt;format('n')); if($diff-&gt;m &lt; 0) { $diff-&gt;y -= 1; $diff-&gt;m = $diff-&gt;m + 12; } $diff-&gt;d = ((int) $date2-&gt;format('j')) - ((int) $date1-&gt;format('j')); if($diff-&gt;d &lt; 0) { $diff-&gt;m -= 1; $diff-&gt;d = $diff-&gt;d + ((int) $date1-&gt;format('t')); } $diff-&gt;h = ((int) $date2-&gt;format('G')) - ((int) $date1-&gt;format('G')); if($diff-&gt;h &lt; 0) { $diff-&gt;d -= 1; $diff-&gt;h = $diff-&gt;h + 24; } $diff-&gt;i = ((int) $date2-&gt;format('i')) - ((int) $date1-&gt;format('i')); if($diff-&gt;i &lt; 0) { $diff-&gt;h -= 1; $diff-&gt;i = $diff-&gt;i + 60; } $diff-&gt;s = ((int) $date2-&gt;format('s')) - ((int) $date1-&gt;format('s')); if($diff-&gt;s &lt; 0) { $diff-&gt;i -= 1; $diff-&gt;s = $diff-&gt;s + 60; } $start\_ts = $date1-&gt;format('U'); $end\_ts = $date2-&gt;format('U'); $days = $end\_ts - $start\_ts; $diff-&gt;days = round($days / 86400); if (($diff-&gt;h &gt; 0 || $diff-&gt;i &gt; 0 || $diff-&gt;s &gt; 0)) $diff-&gt;days += ((bool) $diff-&gt;invert) ? 1 : -1; return $diff; </code></pre> <p>}</p> <p>}</p> <p>It works pretty well (bar the inaccuracies), and hopefully I won't have a use for it again anyway.</p> Optimising WordPress: Caching 2011-12-27T00:00:00-00:00 <p><a href=""><img src="" alt="WordPress Logo"></a>Making your site as <a href="" title="Wikipedia - Caching">cache-able</a> as possible is vital to ensure a smooth browsing experience. WordPress in it's basic form is quite efficient, when you compare it to code-bases such as Magento. Adding functionality such as plug-ins, media, themes and widgets all have a negative effect on performance. As part of the process of making this domain as efficient as possible, a number of caching techniques have been considered and employed.</p> <h3 id="client-cache">Client Cache <a class="direct-link" href="#client-cache" aria-hidden="true">#</a></h3> <p>The first, easiest and probably most important aspect of caching is setting up your web server to serve sensible <a href="" title="W3C - HTTP/1.1 Header Field Definitions">Expires and Cache-control headers</a> on a file-type basis. This allows your visitor to have their browser cache large files upon subsequent page requests, and decreases the number of requests per visitor. It also cuts down on load times; if you set a lifetime for a resource, a browser (with a standard configuration) won't request the resource at all.</p> <p>You could use something like <a href="" title="Apache question on serverfault">this</a> for configuring Apache; the answer provides insight into why these headers need to be set.</p> <h3 id="output-caching">Output Caching <a class="direct-link" href="#output-caching" aria-hidden="true">#</a></h3> <p>As with anything WordPress related, there are many tools to perform a single task. The first is <a href="" title="WP Super Cache">WP Super Cache</a>, which I have configured to serve static HTML files. Load times are improved after the first visitor, as subsequent visitors will be served a pre-generated file from cache storage.</p> <p>Static caching is a trade-off though. Any dynamically generated content and widgets will also be cached. I use a relatively low cache lifetime, so that widgets still update quite often (such as the <a href=""></a>, Lifestream and Twitter widgets). You could also have the widgets update via AJAX calls to a non-cached script, though this depends on the Widget supporting this. Its also worth noting that the first user hitting the page will wait longer than subsequent visitors; the cache must be generated and served, which has a negative effect on load times on the first visit. WP Super Cache also invalidates a cache file when a post is updated or a comment posted, which causes a cache refresh.</p> <h3 id="page-optimisation">Page Optimisation <a class="direct-link" href="#page-optimisation" aria-hidden="true">#</a></h3> <p>The second tool called <a href="">Autoptimize</a>. This works from a different angle, in that it aims to make the page served as efficient as possible. It performs actions such as merging and minifying CSS / JavaScript resources, in-lining CSS background images (using data URIs), and optimising HTML output by removing white-space. It performs a similar task to <a href="" title="mod_pagespeed Overview">Google's mod_pagespeed</a>, but I recently removed this as an update caused issues.</p> <p>There are downsides to optimising output, as it is quite easy to over-optimise and impact on user experience. For example; Enabling conversion of background images in CSS to <a href="" title="CSS-Tricks: Data URIs">data URIs</a> caused load times to spike to around 20-30 seconds (for non-cached page requests). Static caching fixes the issue on subsequent visits, but each invalidation causes an unacceptable load time for the visitor. This could possibly be resolved in the plug-in, though for the time being I have the feature disabled.</p> <p>I encountered yet another issue with data URI images; malformed style-sheet parsing in Firefox Desktop and Mobile releases. This caused the stylesheet processing to end prematurely, as not only were background images missing but also basic style rules. This could be caused by the CSS implemented in my theme causing an incompatibility, though with the load time problem I haven't seen much point in debugging.</p> <h3 id="looking-forward">Looking Forward <a class="direct-link" href="#looking-forward" aria-hidden="true">#</a></h3> <p>I'm looking to integrate a caching server in the future, and it's looking like <a href="" title="Varnish Cache">Varnish</a> is a likely candidate. Varnish will act as a proxy to the web server, and there will be some in-depth configuration required for optimal operation. Varnish may replace WP Super Cache, as it's cache will be in memory via memcached. I have also found a suitable <a href="" title="WP-Varnish">Varnish WordPress integration</a>, which should ease the process of migration.</p> <p>The problem with integrating Varnish is that it acts as a proxy to the application (web) server. The application server would also have to be reconfigured with this in mind. It also introduces additional administration, as you may need to alter the ruleset to support specific plug-ins, widgets and core WordPress updates.</p> Disabling Apache Server Signature 2012-01-26T00:00:00-00:00 <p>I have been trying to disable the server signature for a while, but I found that turning off the ServerSignature directive didn't work for all servers. The signature might read something like:</p> <p>Apache/2.2.X (Ubuntu) mod_ssl/2.X.X OpenSSL/0.X.X</p> <p>If your server exposes this information, it's easier for an attacker to compromise a system based on flaws in a particular server software version (especially if your server software is allowed to become outdated, or your distribution is slow to release security updates). By default, it will display this on error pages in plain text, and also present it as a Server header on every request.</p> <p>To disable completely, you should set the following directives in your Apache configuration:</p> <p>ServerSignature Off<br> ServerTokens Prod</p> <p>via <a href="">Nixtechnica</a></p> Blueberry Slider with slideToggle workaround 2012-03-31T00:00:00-00:00 <p>I recently worked on a responsive design which required an image slider, with a toggle to &quot;minimise&quot; the block. <a href="" title="Blueberry Image Slider">Blueberry</a> is a great responsive jQuery slider, keeping everything scaled correctly at any resolution I threw at it.</p> <p>I needed to assign a slideToggle to the slider block, allowing the slider to be 'minimised'. Unfortunately this exposes an issue where if you first minimise the slider, resize the browser window (causing 'onresize' to recalculate the slider dimensions), then slideToggle it back open, it will have calculated a zero height for the slider and will refuse to maximize until you resize the browser window again.</p> <p>To trigger the slider's size calculation, you need to trigger the 'onresize' event in the callback for slideToggle.</p> <p>jQuery(button).click(function (e) {<br> $this = jQuery(this);<br> $this.toggleClass('slider-open');<br> jQuery('.slides',$this.parent())<br> .slideToggle(250,function() {<br> jQuery(window).trigger('resize');<br> }<br> ); <br> });</p> <p>This solution makes the transition noticeably less-smooth in the 'opening' toggle. It's most likely down to the fact that the slider has zero height until after the transition, after which the resize kicks in and restores the slider to full size.</p> Installing XHProf on Debian Squeeze 2012-04-17T00:00:00-00:00 <p>I was working on a project which was randomly failing to load certain views outside the development environment. It turns out that the system was running out of memory. After searching some of the error messages output by the script, I stumbled upon the <a href="">xhprof PHP module</a>. It was <a href="">originally created by Facebook</a>, and released under an open source license.</p> <p>xhprof provides profiling information, down to the function call. This includes execution time, CPU and memory usage for each operation. The module allows you to find and optimise bottlenecks in your application. The library includes a GUI output, you just use the classes provided to create the reports.</p> <p>I installed using PECL on a Debian Squeeze development server, and the first things you need are php5-dev and make;</p> <p>apt-get install php5-dev make</p> <p>However, when you try to run the PECL install, we fail because the package is beta. We therefore need to set PECL to install beta packages. Configure PECL to install beta packages:</p> <p>pecl config-set preferred_state beta<br> pecl install xhprof</p> <p>PECL will correctly fetch the source files, but fail to start compiling.</p> <p>[...]<br> 11 source files, building<br> running: phpize<br> Cannot find config.m4.<br> Make sure that you run '/usr/bin/phpize' in the top level source directory of the module</p> <p>ERROR: 'phpize' failed</p> <p>This error is caused by a <a href="">bug in the PEAR library</a> installed on the system. The bug was fixed in PEAR 1.9.3, but as of the time of writing PEAR 1.9.4 is the latest stable version. <a href="">Updating PEAR</a> fixes the issue and allows the module to be compiled correctly.</p> <p>pear install PEAR-1.9.4</p> <p>After installing 1.9.4 you can then compile and install xhprof on Debian Squeeze. To add the module to PHP, you need to add it to your configuration.</p> <p>The last thing to do is check the module is correctly loaded, check your phpinfo() output for an xhprof section. Then you can start profiling your projects.</p> <p>For more information on setting up and using xhprof, head over to this <a href="">ibuildings techportal article</a>.</p> API Callback URLs and HTTP Authentication 2012-06-22T00:00:00-00:00 <p>When developing web applications that use APIs, it is usually necessary to have the development site accessible for API callback URLs. A good example would be when working with payment gateway systems, which typically post back success or failure of transactions. In this event it is convenient to use HTTP authorisation to prevent outside access (users, crawlers, etc.). The issue with this is that API systems don't always work with the http://[user]@[password]:[url] method of manually passing through this authentication method.</p> <p>If this case, and you are using Apache, you can always bypass HTTP authentication altogether;</p> <p>Order deny,allow<br> Deny from all<br> AuthUserFile &quot;/path/to/htusers&quot;<br> AuthType Basic<br> AuthName &quot;Dev&quot;<br> require valid-user<br> allow from # Allow traffic from example internal network addresses<br> allow from 127 # Allow all traffic on loopback address<br> allow from X.X.X.0/24 # Allow all traffic from External IP address range<br> Satisfy Any</p> <p>The key here is the &quot;<a href="">Satisfy Any</a>&quot; directive. This instructs Apache to allow a connection to authenticate if any of the conditions are true. Traffic from the specified IP addresses will now bypass authentication, meaning that any API callbacks can poll the application endpoints you have set up. This method does rely on the callbacks coming from a specific IP address or IP range. It may not be a workable solution if the callbacks can originate from multiple unknown addresses, such as cloud services.</p> <p>The other thing to note; if placed directly into a VirtualHost directive, this gives full access to the site / virtual domain to these IP addresses. This could potentially be an issue, which can be resolved by placing them within <a href="">Location</a> or <a href="">Directory</a> directives. This allows you to restrict access to specific directories or URLs within the application. Which you require depends purely upon how your API callbacks are implemented.</p> Responsive Layout for Wordpress 2013-01-28T00:00:00-00:00 <p><img src="" alt="WordPress Logo">I've been working on a few tweaks to improve accessibility on mobile devices. Since obtaining a shiny new Samsung Galaxy Note 2, I'm finding myself using my mobile device more than ever. The redesign earlier last year allowed me to introduce some initial steps toward a responsive layout, but it was and still is an unfinished product.</p> <p>I have finally got round to implementing sidebar folding at low resolution. I'm the using adjacent sibling selector (+), the :checked pseudo-selector, and HTML <input> hacks (<label>, radio and check-box <input> elements). This technique also works great for folding the main navigation at low resolution using a check-box.</label></p> <h3 id="implementing-the-responsive-layout-elements">Implementing the Responsive Layout Elements <a class="direct-link" href="#implementing-the-responsive-layout-elements" aria-hidden="true">#</a></h3> <p>The folding technique is derived from <a href="">this CSS accordion</a> technique. I find the lack of reliance on JavaScript to be attractive; it remains to be seen whether this improves browser support due to the use of :checked and sibling selectors.</p> <p>The HTML structure below provides the basis for the sidebar. As is standard with Wordpress' sidebar output, each widget is wrapped within an <li>. A <label> element wraps the heading, which will target the hidden <input> element through the <code>for</code> attribute. The <input> element's <code>id</code> must match the <code>for</code> attribute value to allow the <label> to interact with it. You can implement either check-box or radio type <input>s, which will provide different interactions. Radio buttons allow just one section to be toggled; give them the same name to group them. Check-boxes allow multiple items to display simultaneously.</label></label></li></p> <pre><code> Widget Title </code></pre> <p>The input element will be hidden using CSS as we don't want them to be displayed. I only wanted the accordion functionality to apply when in single-column mode, so I wrapped the two selectors in a @media query. I've stripped it down to the bare minimum for sake of clarity;</p> <pre><code>.sidebar .accordion-selector { display: none; } @media screen and (max-width: 960px) { .sidebar .widget-content { display: none; } .sidebar input.accordion-selector:checked + .widget-content { display: block; } } </code></pre> <h3 id="implementing-the-markup-in-a-sidebar">Implementing the Markup in a Sidebar <a class="direct-link" href="#implementing-the-markup-in-a-sidebar" aria-hidden="true">#</a></h3> <p>The difficult part is getting the HTML structure in place, within the confines of Wordpress' template and widget system. You have to include a unique ID for the input as mentioned, so the <label> can include this in it's <code>for</code> attribute to activate the hidden <input>. This mark-up needs to be included in the before_title and after_title parameter of the register_sidebar function call. Unfortunately though, the Wordpress API doesn't perform variable substitution on these attributes.</label></p> <pre><code>register_sidebar( array( 'name' =&gt; __('Sidebar'), 'id' =&gt; 'sidebar', 'description' =&gt; __('Sidebar'), 'before_title' =&gt; '', 'after_title' =&gt; '', 'before_widget' =&gt; '', 'after_widget' =&gt; '', ) ); </code></pre> <p>As we need some substitutions performing on the before_title sidebar attribute, allowing us to hook into widget generation to force some values. We need to hook the <a href="">dynamic_sidebar_params</a> filter to alter this output. My implementation is far from complete but it allows the widget ID to be included in the &quot;title chrome&quot;:</p> <pre><code>function filter_sidebar_params($params) { $id = $params[0]['id']; $the_id = $params[0]['widget_id']; $title = $params[0]['before_title']; $after = $params[0]['after_title']; $params[0]['before_title'] = sprintf($title,$the_id,$id); $params[0]['after_title'] = sprintf($after,$the_id,$id); return $params; } add_filter('dynamic_sidebar_params','filter_sidebar_params'); </code></pre> <h3 id="implementing-for-navigation">Implementing for Navigation <a class="direct-link" href="#implementing-for-navigation" aria-hidden="true">#</a></h3> <p>Implementing a folding navigation bar is a simpler task; we simply need to embed a clickable <label> and hidden check-box input into the template directly before the navigation container.</label></p> <p>The functionality is the same here as it is with the sidebar; the <label> will toggle the <input>, in turn toggling the <nav> element's visibility. The only difference is that we need something to toggle. In the sidebar example, we used the title as the content of the label; this can be anything but it ought to be a clear navigation indicator.</nav></label></p> <p>It may not be the most semantic implementation, but anything must be little better than navigation by <select> element?</select></p> <h3 id="next-steps">Next Steps <a class="direct-link" href="#next-steps" aria-hidden="true">#</a></h3> <p>I'm not sure on the next evolution for the drop-down navigation. I'm not a fan of the horizontal navigation links as they are when in the &quot;low profile&quot; mode. When I figure out what's best I'll follow up if it's noteworthy.</p> Client Showcase - Qualitick Ltd - Juicy Media 2013-06-11T00:00:00-00:00 <p><a href=""></a></p> <p>I had the pleasure of writing up an overview of one of our clients at Juicy Media.</p> Twitter Widget for Wordpress 2013-06-27T00:00:00-00:00 <p><a href=""><img src="" alt="Twitter Logo"></a>I switched to the Jetpack Twitter Widget a year or so ago. Since Twitter fully switched to API version 1.1, as many developers know they now require OAuth for integration. This means that the Jetpack Twitter Widget is <a href="">no longer supported</a>. I'm not a fan of the &quot;official&quot; JavaScript  <a href="">Twitter widget</a>, for a few reasons;</p> <ul> <li>It requires JavaScript</li> <li>It's ugly as hell</li> <li>It's everywhere</li> </ul> <p>There is also the <a href="">timeline widget</a>, which is also implemented as the successor to the original in Jetpack, but this isn't a 1:1 replacement either. So I wondered how easily an <a href="">OAuth-enabled version</a> would be to both manage and implement.</p> <h3 id="integrating-oauth">Integrating OAuth <a class="direct-link" href="#integrating-oauth" aria-hidden="true">#</a></h3> <p>It turned out to be a reasonably simple task. I based it off the <a href="">Wickett Twitter Widget</a>, which I had available from before switching to the Jetpack version.</p> <p>The OAuth integration requires you create a Twitter Application; You can do that <a href="">here</a>. Twitter have been kind enough to provide all the necessary public and private keys, without implementing a full OAuth handshake with the service. The widget implements the simple to use <a href="">twitteroauth</a> library for PHP; This facilitates the OAuth calls, and is available on GitHub.</p> <h4 id="authorising-against-your-account">Authorising against your account <a class="direct-link" href="#authorising-against-your-account" aria-hidden="true">#</a></h4> <p>This initial version simply covers adding the 4 different OAuth keys from your developer application directly into the widget. I'm considering other options, such as a standalone options page, attaching data to authors—including a full OAuth handshake—, or application only authentication, but it's working as it stands.</p> <h4 id="a-word-of-caution">A Word of Caution <a class="direct-link" href="#a-word-of-caution" aria-hidden="true">#</a></h4> <p>The API keys you generate should be kept secret. If others have access to your installation and can manage widgets, they will be able to access these keys. As this is a personal blog this isn't an issue, but it is something that you may need to consider. The keys can be regenerated at any time, but of course you will need to update the new keys into the widget settings otherwise it will stop working. The application you create only requires read permissions to operate, so there's no need to allow write access.</p> <h3 id="ui-considerations">UI Considerations <a class="direct-link" href="#ui-considerations" aria-hidden="true">#</a></h3> <p><a href=""><img src="" alt="twitter-widget-sc"></a>Another caveat Twitter have thrown in; they insist that any integration must adhere to their <a href="">developer display requirements</a> when integrating the API. I have at least tried to interpret and adhere to the rules they have laid out in the widget. Basically they want anything that is a tweet to look and behave just like it would in their application. This includes the ability to Reply, Re-tweet and Favourite Tweets, follow a Twitter user, and navigate to the original tweet amongst other things.</p> <p>It is technically possible to just upgrade a widget that was using the v1.0 API to use v1.1 with minimal changes.  I would have to advise against this though, as you run the risk of having your API access cut off. I guess that means no more status update tickers?</p> <h4 id="web-intents">Web intents <a class="direct-link" href="#web-intents" aria-hidden="true">#</a></h4> <p>Web intents facilitate actions on tweets, and they can work with or without JavaScript. Twitter provide some hosted JavaScript that automatically enhances the web intents in your HTML. You don't have to add any classes or attributes, it works based on the link target. There are privacy concerns, as Twitter are can track users when this code is included. There is a method to opt-out of this, but I haven't yet implemented this option.</p> <h3 id="download-the-oauth-twitter-widget-for-wordpress">Download the OAuth Twitter Widget for WordPress <a class="direct-link" href="#download-the-oauth-twitter-widget-for-wordpress" aria-hidden="true">#</a></h3> <p>Download the zip or clone the <a href="">GitHub repository</a> and install the plugin as normal. You may need to tweak the CSS, as I have tested this on just one WordPress theme (this one). If you have an improvement, feel free to send a pull request. Questions and suggestions are always appreciated, if you have any post an issue.</p> Installable Webapps: Extend the Sandbox | Boris Smus 2013-07-18T00:00:00-00:00 <p><a href=""></a></p> RECESS - Twitter's CSS Hinter 2013-08-01T00:00:00-00:00 <p><a href=""></a></p> The slippery slope | 90 Percent Of Everything 2013-08-05T00:00:00-00:00 <p><a href=""></a></p> <p>An interesting read; I've visited Dark Patterns a few times over the years and not much has changed!</p> Essential considerations for crafting quality media queries 2013-08-07T00:00:00-00:00 <p><a href=""></a></p> Make the most of ARIA landmark roles 2013-08-08T00:00:00-00:00 <p><a href=""></a></p> Understanding the Zend Framework 2 event manager – Un blog sur tout et rien 2013-08-08T00:00:00-00:00 <p><a href=""></a></p> Varnish Edge Side Includes and Wordpress 2013-08-08T00:00:00-00:00 <p><a href=""></a></p> Flatstrap by 2013-08-20T00:00:00-00:00 <p><a href=""></a></p> Fluid Width YouTube Videos 2013-08-20T00:00:00-00:00 <p><a href=""></a></p> Please stop embedding Bootstrap classes in your HTML! 2013-08-20T00:00:00-00:00 <p><a href=""></a></p> squashing commits with rebase 2013-08-21T00:00:00-00:00 <p><a href=""></a></p> Developing Backbone.js Applications 2013-09-02T00:00:00-00:00 <p><a href=""></a></p> git - the simple guide - no deep shit! 2013-09-02T00:00:00-00:00 <p><a href=""></a></p> Learn Backbone.js Completely 2013-09-02T00:00:00-00:00 <p><a href=""></a></p> Quick Tip: Avoid FOUT by Adding a Web Font Preloader | Webdesigntuts+ 2013-09-05T00:00:00-00:00 <p><a href=""></a></p> Backbone.js able to do rest and localstorage? 2013-09-10T00:00:00-00:00 <p><a href=""></a></p> Pure 2013-09-11T00:00:00-00:00 <p><a href=""></a> A set of small, responsive CSS modules that you can use in every web project.</p> Responsive Data Table Roundup 2013-09-11T00:00:00-00:00 <p><a href=""></a></p> What is a router? - Backbone.js 2013-09-11T00:00:00-00:00 <p><a href=""></a></p> Zombies! RUN! (Managing Page Transitions In Backbone Apps) 2013-09-12T00:00:00-00:00 <p><a href=""></a></p> Are you a sucker? — How to use the internet 2013-09-14T00:00:00-00:00 <p><a href=""></a></p> how to blog about code and give zero fucks 2013-09-14T00:00:00-00:00 <p><a href=""></a></p> AirPrint on Debian GNU/Linux 2013-09-18T00:00:00-00:00 <p><a href=""></a></p> Pingback implementation for Zend Framework Applications 2013-09-18T00:00:00-00:00 <p><a href=""></a></p> Thinking Inside The Box With Vanilla JavaScript 2013-10-07T00:00:00-00:00 <p><a href=""></a></p> A simple Debian based dev environment 2013-10-08T00:00:00-00:00 <p><a href=""></a></p> How to install/setup latest version of PHP 5.5 on Debian Wheezy 7.0/7.1 2013-10-08T00:00:00-00:00 <p><a href=""></a></p> WordPress function to add the TinyMCE WYSIWYG editor to any custom field of type 'Textarea' 2013-10-08T00:00:00-00:00 <p><a href=""></a></p> A Rebase Workflow for Git 2013-10-18T00:00:00-00:00 <p><a href=""></a></p> Avoiding Git Disasters: A Gory Story 2013-10-18T00:00:00-00:00 <p><a href=""></a></p> gajus/ 2013-10-22T00:00:00-00:00 <p><a href=""></a></p> Can I set up system mail to use an external SMTP server? 2013-11-01T00:00:00-00:00 <p><a href=""></a></p> Disconnecting EGit from a Project in Eclipse 2013-11-01T00:00:00-00:00 <p><a href=""><img src="" alt="Eclipse Logo"></a>I have been using Git a lot more recently. Eclipse PDT is my editor of choice, and EGit has eased the transition to a version control centric workflow. Some of my project development environments are accessed via a Samba network share; unfortunately <a href="">Git over Samba is sub-optimal</a> at best. For a larger project I have been working on, a full repository refresh could take up to 30 minutes. This is obviously unacceptable, especially if you hope to commit often or create / switch feature branches.</p> <h3 id="disconnect-eclipse-team-providers">Disconnect Eclipse Team Providers <a class="direct-link" href="#disconnect-eclipse-team-providers" aria-hidden="true">#</a></h3> <p>The problem I was having is that EGit detects and refreshes the repository from the project at import time, and each time Eclipse started up. It is possible to fix the problem without losing the Git functionality of the IDE completely, as it can be helpful. The EGit functionality is provided by the Git Team Provider. This can be <a href="" title="Disabling EGit in Eclipse-CDT Juno">disconnected on a project basis</a> by right-clicking the project, in the team section labelled &quot;Disconnect&quot;. After doing this we now have to manage this repository directly within the development environment (i.e. via SSH). Alternately you could operate the Git command line using the Samba network mount, but the same problems will occur without any benefit (just not causing issues in the IDE).</p> <h3 id="disable-automatic-registration-of-the-team-provider">Disable Automatic Registration of the Team Provider <a class="direct-link" href="#disable-automatic-registration-of-the-team-provider" aria-hidden="true">#</a></h3> <p>Additionally, you can configure Eclipse to not register new projects with the team provider. This means that you won't have to waste time waiting for EGit to initialise itself just to disconnect the functionality afterwards. This can be toggled in Preferences &gt; Team &gt; Git &gt; Projects, by un-checking &quot;Auto share projects located in a git repository&quot;. Now for any project where you wish to use EGit, you have to right-click, under the team section labelled &quot;Share Project&quot; and follow the Share Project wizard.</p> Part-UUID - Debian Wiki 2013-11-01T00:00:00-00:00 <p><a href=""></a></p> javascript css check if overflow 2013-11-03T00:00:00-00:00 <p><a href=""></a></p> I'm Taking Part in Movember 2013 2013-11-05T00:00:00-00:00 <p><a href=""><img src="" alt="MO13 Primary Logo Ranged NEG copy"></a>This year I am participating in <a href="">Movember</a>. I know a few people who have done so in the past, however it's a <a href="">team effort</a> with everybody at Juicy Media taking part. Participation is not at all difficult, I don't have to run (or walk) a marathon, so there was no excuse but to sign up and get on with it. The only downside is taking those awful &quot;selfie&quot; photos; vanity aside my front-facing camera seems to be awful for the purpose.</p> <h3 id="isn't-it-movember-every-day-for-you%3F">Isn't it Movember every day for you? <a class="direct-link" href="#isn't-it-movember-every-day-for-you%3F" aria-hidden="true">#</a></h3> <p><a href=""><img src="" alt="Movember Mo - Day Zero"></a>Not any more! Being a (former) proud owner of decent volume of facial hair, day one has proven to be the most difficult. It was almost like looking at someone else in the mirror. The feeling of harsh cold on my lower face was difficult to bear, as should be apparent in the post-shave mugshot. At least I didn't get asked for ID at the local.</p> <p>My Movember facial growth is progressing quickly, though I wish it would grow faster. It's all for a good cause, and I hope my participation in some way helps, regardless of what we raise in terms of donations. Cancer in particular has affected many members of my family. It was awareness of the problem that helped them get the screening, advice and treatment they needed to ensure they would have the best chance of a full and healthy life.</p> <p>So if you feel the need spend money with <a href="#nb">nothing in return*</a>, the team and I would be happy to solicit your generous donations through the relevant Movember <a href="">pages</a>. Here's my personal progress at the end of day four:</p> <p><a href=""><img src="" alt="Movember Mo - Day Four"></a></p> <p>* Other than that warm, fuzzy feeling you get for doing something to help others, and my gratitude</p> Apache Cordova 2013-11-07T00:00:00-00:00 <p><a href=""></a></p> Copy to clipboard using JavaScript and Flash 2013-11-18T00:00:00-00:00 <p><a href=""></a></p> CSS Regions Considered Harmful 2014-01-23T00:00:00-00:00 <p><a href=""></a></p> GitLab CE 6.5 released 2014-01-23T00:00:00-00:00 <p><a href=""></a></p> PHP 5.6 alpha 2014-01-26T00:00:00-00:00 <p><a href=""></a></p> Why is Progressive Enhancement so unpopular? 2014-01-28T00:00:00-00:00 <p><a href=""></a></p> How I lost my $50,000 Twitter username 2014-01-29T00:00:00-00:00 <p><a href="!tMwKa">!tMwKa</a></p> Magento Checklist for Design Elements 2014-01-29T00:00:00-00:00 <p><a href=""></a></p> Security vulnerability in gitlab (CVE-2013-7316) 2014-02-02T00:00:00-00:00 <p><a href=""></a></p> Footage from the Titanic Struggle of B-R 2014-02-03T00:00:00-00:00 <p><a href=""></a></p> Making Time for Side Projects 2014-02-03T00:00:00-00:00 <p><a href=""></a></p> Qualys SSL Labs - Projects / SSL Server Test 2014-02-03T00:00:00-00:00 <p><a href=""></a></p> 7 Days to Die - Alpha 6.3 Patch Is Out! 2014-02-04T00:00:00-00:00 <p><a href=""></a></p> Adactio: Journal—Writing from home 2014-02-05T00:00:00-00:00 <p><a href=""></a></p> Of excellence and reputation — Joschi Kuphal 2014-02-05T00:00:00-00:00 <p><a href=""></a></p> The Better Way to Modify Magento Layouts 2014-02-06T00:00:00-00:00 <p><a href=""></a></p> Working With LESS and the Chrome DevTools 2014-02-06T00:00:00-00:00 <p><a href=""></a></p> Applying Transformations To Responsive Web Design 2014-02-07T00:00:00-00:00 <p><a href=""></a></p> Really simple responsive HTML email template 2014-02-07T00:00:00-00:00 <p><a href=""></a></p> Mapping Relational Databases and SQL to MongoDB 2014-02-09T00:00:00-00:00 <p><a href=""></a></p> PhpStorm - Top Productivity Hacks and Shortcuts 2014-02-09T00:00:00-00:00 <p><a href=""></a></p> Mounting Windows Shares with cifs in Debian Squeeze 2014-02-10T00:00:00-00:00 <p><a href=""><img src="" alt="openlogo-nd"></a>I run Debian Squeeze with Gnome 3 on a laptop. I mainly use this to offload my e-mail from the main machine, where I can concentrate my workflow across two screens without the constant need to ALT-Tab. This means that there is a need to share files between the Windows desktop machine and Laptop, using regular windows file shares.</p> <p>I had a series of &quot;bookmarks&quot; that allowed me to access various Samba shares. These bookmarks (<a href="">GVFS mounts</a>) are configured in Nautilus, and really simple to set up. Unfortunately, they are not so easy to use; you can't drag and drop a file from a GVFS share into a non-supporting application (such as Google Chrome), and they don't usually generate file previews in nautilus either. This could be due to the fact that they use protocol addresses (e.g. smb://)? That means that I was having to copy files—from the shares to a local directory—before being able to work with them.</p> <h3 id="configuring-cifs-shares-to-mount-on-boot">Configuring CIFS shares to mount on boot <a class="direct-link" href="#configuring-cifs-shares-to-mount-on-boot" aria-hidden="true">#</a></h3> <p>An alternative to GVFS is to mount shares via CIFS. This method allows you to specify your mounts in your /etc/fstab file. In my case, I had to install the cifs mount utilities first (assuming you have installed and configured sudo);</p> <p>sudo apt-get install cifs-utils</p> <p>Then you just need to configure the shares, adding them to the /etc/fstab file. Caution is required here of course; altering this file incorrectly can stop your system from booting. You will need to add new entries into the file that look like the following (substitute all <strong><values></values></strong> according to your configuration):</p> <p>/// cifs username=,password=,uid=,gid= 0 0</p> <h1 id="or-you-can-store-your-share-credentials-in-a-separate-file">or you can store your share credentials in a separate file <a class="direct-link" href="#or-you-can-store-your-share-credentials-in-a-separate-file" aria-hidden="true">#</a></h1> <p>/// cifs credentials=,uid=,gid= 0 0</p> <p>Finally, you need to create your mount point(s) and test that your shares work. You can do this by trying to mount them with -v for verbose output:</p> <p>sudo mount -v</p> <p>In testing this configuration, I am able to access network shares using this method as if I was working with local directories; there is just a bit more overhead to set up new shares. The mounts fail gracefully (the system still boots!) when the network locations aren't available. You would need to manually remount them with root privileges (or add the users option) if they become available after booting. There are a plethora of <a href="">further mount options</a> for cifs depending upon your configuration.</p> Choosing Vanilla JavaScript 2014-02-11T00:00:00-00:00 <p><a href=""></a></p> Reinstalling Magento Modules 2014-02-11T00:00:00-00:00 <p><a href=""></a></p> Learn How to Code For Free Without Leaving Your Browser 2014-02-12T00:00:00-00:00 <p><a href=""></a></p> Solving a Pound HTTPS Redirect Issue 2014-02-12T00:00:00-00:00 <p>I recently had a problem where a Magento store would infinitely redirect, from TLD to sub-domain (i.e. non-www to www). The server was configured behind a reverse proxy, which was handled by <a href="" title="REVERSE-PROXY AND LOAD-BALANCER">Pound</a> (acting as a load balancer and SSL wrapper). Pound is great for handling hand-overs between a caching proxy and application servers, in addition to load balancing multiple servers and wrapping SSL connections to the client. The issue was quite difficult to track down; redirects can be issued at any point in the application stack. We tested adding redirection at each level of the stack in isolation; web server, caching proxy and within the application. All exhibited the same problem, no matter where the redirect was executed from. The redirect loop didn't present itself when redirects were removed either.</p> <h3 id="configuring-a-solution">Configuring a Solution <a class="direct-link" href="#configuring-a-solution" aria-hidden="true">#</a></h3> <p>The solution was found by testing each back-end. As we were able to access each back-end independently, we were able to determine that redirection was in fact working. Pound must have been causing the redirects to loop, but we didn't see any reason why it should be doing so.</p> <p>After some research, Pound does in fact rewrite location headers returned from the server, under certain circumstances. This depends on the aptly named configuration option <code>RewriteLocation 0|1|2</code>. The <a href="" title="pound(8) linux man page">default setting is 1</a> when not specified, which means that any Location headers that seem to point to the application server (instead of pound) are rewritten to point to pound. In this case <code>RewriteLocation 0</code> needed to be set, which fixed the redirection to Magento.</p> <p>This is necessary where Pound is handling SSL—which it needs to do to function—and application server is configured with SSL offloaded. Pound communicates with the application server via HTTP, and translates any responses from the web server according to the protocol and server address that was initially requested.</p> The problem with CSS pre-processors 2014-02-12T00:00:00-00:00 <p><a href=""></a></p> HHVM and Hack - Can We Expect Them to Replace PHP? 2014-02-14T00:00:00-00:00 <p><a href=""></a></p> localForage: Offline Storage, Improved 2014-02-14T00:00:00-00:00 <p><a href=""></a></p> Protecting Against Link Rot While Embracing the Future 2014-02-14T00:00:00-00:00 <p><a href=""></a></p> How SVG Line Animation Works 2014-02-18T00:00:00-00:00 <p><a href=""></a></p> Live Editing Sass and Less in the Firefox Developer Tools 2014-02-18T00:00:00-00:00 <p><a href=""></a></p> Useful Learning Resources For Web Designers 2014-02-18T00:00:00-00:00 <p><a href=""></a></p> Magmi Wiki 2014-02-19T00:00:00-00:00 <p><a href=""></a></p> Responsive Design Frameworks: Just Because You Can, Should You? 2014-02-20T00:00:00-00:00 <p><a href=""></a></p> Tips for Public Speaking 2014-02-20T00:00:00-00:00 <p><a href=""></a></p> GitLab CE 6.6 released 2014-02-22T00:00:00-00:00 <p><a href=""></a></p> Kickstart Your Project With INIT And Grunt 2014-02-23T00:00:00-00:00 <p><a href=""></a></p> Deploying an Update Server 2014-02-24T00:00:00-00:00 <p><a href=""></a></p> Float Labels with CSS 2014-02-24T00:00:00-00:00 <p><a href=""></a></p> Medium-Style Page Transition 2014-02-24T00:00:00-00:00 <p><a href=""></a></p> Complaint-Driven Development 2014-02-25T00:00:00-00:00 <p><a href=""></a></p> CSS polyfills from the future 2014-02-26T00:00:00-00:00 <p><a href=""></a></p> Protect Against Humans.txt Query-String Scans 2014-02-27T00:00:00-00:00 <p><a href=""></a></p> Stackicons - Icon Fonts for Web Designers 2014-02-27T00:00:00-00:00 <p><a href=""></a></p> Atom 2014-03-02T00:00:00-00:00 <p><a href=""></a></p> The Battle for the Body Field 2014-03-02T00:00:00-00:00 <p><a href=""></a></p> Continuum 2014-03-02T00:00:00-00:00 <p><a href=""></a></p> Only 90s Web Developers Remember This 2014-03-04T00:00:00-00:00 <p><a href=""></a></p> Aerotwist - My Performance Audit Workflow 2014-03-05T00:00:00-00:00 <p><a href=""></a></p> Flexbox Bar Navigation Demo 2014-03-05T00:00:00-00:00 <p><a href=""></a></p> Better Dependency Management In Team-Based WordPress Projects With Composer 2014-03-07T00:00:00-00:00 <p><a href=""></a></p> Protect Against Malicious POST Requests 2014-03-07T00:00:00-00:00 <p><a href=""></a></p> What We Mean When We Say “responsive” 2014-03-09T00:00:00-00:00 <p><a href=""></a></p> The Art of HTML Semantics: Part 1 2014-03-10T00:00:00-00:00 <p><a href=""></a></p> Compile LESS Files with Grunt 2014-03-10T00:00:00-00:00 <p><a href=""></a></p> Using Sass To Semantically @extend Bootstrap 2014-03-11T00:00:00-00:00 <p><a href=""></a></p> Device-Agnostic 2014-03-12T00:00:00-00:00 <p><a href=""></a></p> How to Make an Awesome Maintenance Mode Screen for WordPress 2014-03-12T00:00:00-00:00 <p><a href=""></a></p> How to upgrade Magento to version 1.8 2014-03-12T00:00:00-00:00 <p><a href=""></a></p> Using Your Terminal From The DevTools 2014-03-12T00:00:00-00:00 <p><a href=""></a></p> WordPress Maintenance Mode Without a Plugin 2014-03-12T00:00:00-00:00 <p><a href=""></a></p> Improving Magento Maintenance Mode 2014-03-17T00:00:00-00:00 <p><a href=""></a></p> classyllama/Wiz 2014-03-18T00:00:00-00:00 <p><a href=""></a></p> Avoiding Optional Dependencies 2014-03-19T00:00:00-00:00 <p><a href=""></a></p> How To Create An Admin-Manageable Magento Entity For Brands 2014-03-19T00:00:00-00:00 <p><a href=""></a></p> Install Apache Solr 4.4 on Ubuntu 12.04 with Tomcat 7 and MySql Data Import 2014-03-19T00:00:00-00:00 <p><a href=""></a></p> Better integration for open web apps on Android 2014-03-20T00:00:00-00:00 <p><a href=""></a></p> HTTPS Mixed Content: Still the Easiest Way to Break SSL 2014-03-20T00:00:00-00:00 <p><a href=""></a></p> Brand Basics: The Importance of Having a Brand Platform 2014-03-21T00:00:00-00:00 <p><a href=""></a></p> Refactoring Legacy Code: Part 1 - The Golden Master 2014-03-23T00:00:00-00:00 <p><a href=""></a></p> In Depth Magento Dispatch: Rewrites 2014-03-26T00:00:00-00:00 <p><a href=""></a></p> Creating Custom Shipping Methods In Magento 2014-03-28T00:00:00-00:00 <p><a href=""></a></p> Magento Cachebuster 2014-03-28T00:00:00-00:00 <p><a href=""></a></p> FIX - Customer cannot login to magento 1.8.1 2014-04-07T00:00:00-00:00 <p><a href=""></a></p> Optimal Techniques for Strategically Displaying Web Forms 2014-04-07T00:00:00-00:00 <p><a href=""></a></p> Web Components and the Three Unsexy Pillars 2014-04-07T00:00:00-00:00 <p><a href=""></a></p> Creating Style Guides 2014-04-11T00:00:00-00:00 <p><a href=""></a></p> HTML5 Video Player | Video.js 2014-04-11T00:00:00-00:00 <p><a href=""></a></p> xkcd: Heartbleed Explanation 2014-04-11T00:00:00-00:00 <p><a href=""></a></p> Writing to vagrant synced folders 2014-04-13T00:00:00-00:00 <p><a href=""></a></p> drewhunter/EmptyHandles 2014-04-14T00:00:00-00:00 <p><a href=""></a></p> How to Deal with Merge Conflicts in Git 2014-04-17T00:00:00-00:00 <p><a href=""></a></p> 12 Little-Known CSS Facts 2014-04-20T00:00:00-00:00 <p><a href=""></a></p> The Ebb of the Web 2014-04-21T00:00:00-00:00 <p><a href=""></a></p> Improve the payment experience with animations 2014-04-22T00:00:00-00:00 <p><a href=""></a></p> netz98/n98-magerun 2014-04-29T00:00:00-00:00 <p><a href=""></a></p> Joomla! 3.3.0 Released 2014-04-30T00:00:00-00:00 <p><a href=""></a></p> Rewriting a Block Class 2014-05-09T00:00:00-00:00 <p><a href=""></a></p> Magento – display root category products with filters 2014-05-12T00:00:00-00:00 <p><a href=""></a></p> Using Lucene and MoreLikeThis to show Related Content 2014-05-23T00:00:00-00:00 <p><a href=""></a></p> Using an external SMTP server with GitLab 2014-05-29T00:00:00-00:00 <p><a href=""></a></p> Phabricator 2014-05-31T00:00:00-00:00 <p><a href=""></a></p> How to Create a .pem File for SSL Certificate Installations 2014-06-04T00:00:00-00:00 <p><a href=""></a></p> Javascript: encode(decode) html text into html entity 2014-06-05T00:00:00-00:00 <p><a href=""></a></p> A More Modern Scale for Web Typography 2014-06-17T00:00:00-00:00 <p><a href=""></a></p> 5 More Killer Firefox Addons for Designers 2014-06-28T00:00:00-00:00 <p><a href=""></a></p> The `time` Element 2014-06-30T00:00:00-00:00 <p><a href=""></a></p> Move Magento Private Files Outside the Docroot 2014-07-21T00:00:00-00:00 <p><a href=""></a></p> Git: changed files between two commits 2014-08-07T00:00:00-00:00 <p>If you need a list of all modified files between two commits, you can use the following command;</p> <p>git diff --name-only SHA1 SHA2</p> <p>You can also pipe the output to a file (in the parent directory) as follows;</p> <p>git diff --name-only SHA1 SHA2 &gt; ../filename.txt</p> <p>Via <a href=""></a></p> Our Magento Git Guide and Work Flow 2014-08-12T00:00:00-00:00 <p><a href=""></a></p> addAttributeToFilter Conditionals In Magento 2014-09-02T00:00:00-00:00 <p><a href=""></a></p> Editing Magento Translate.csv File 2014-09-02T00:00:00-00:00 <p><a href=""></a></p> Favicon to PNG with convert and streams 2014-09-09T00:00:00-00:00 <p>I have WordPress set up to load, parse and cache the favicons for sites added as bookmarks. The PHP class I was using struggled with certain ICO files, such as <a href=""></a> and <a href=""></a>.</p> <p>In the below example, we load a favicon from the filesystem into memory. For my use-case, the favicon has been loaded directly from the external site. Wanting to avoid unnecessary temporary files, I found that you can pipe data to and from the convert program from ImageMagick using streams in PHP. Assuming that you have determined that the favicon is an ico file;</p> <p>convert ico:- -thumbnail 16x16 -alpha on -background none -flatten png:-</p> <p>The command reads left to right, <code>ico:-</code> instructs <code>convert</code> to receive an ico formatted string from STDIN. <code>png:-</code> then instructs <code>convert</code> to output a png string to STDOUT. Below is a working example;</p> <p>$file = dirname(__FILE__);<br> $icon = file_get_contents(&quot;{$file}/favicon.ico&quot;);</p> <p>$descriptorspec = array(<br> 0 =&gt; array(&quot;pipe&quot;, &quot;r&quot;), // stdin is a pipe that the child will read from<br> 1 =&gt; array(&quot;pipe&quot;, &quot;w&quot;), // stdout is a pipe that the child will write to<br> 2 =&gt; array(&quot;file&quot;, &quot;/tmp/error-output.txt&quot;, &quot;a&quot;) // stderr is a file to write to<br> );</p> <p>$process = proc_open('convert ico:- -thumbnail 16x16 -alpha on -background none -flatten png:-', $descriptorspec, $pipes);</p> <p>if (is_resource($process)) {<br> // $pipes now looks like this:<br> // 0 =&gt; writeable handle connected to child stdin<br> // 1 =&gt; readable handle connected to child stdout<br> // Any error output will be appended to /tmp/error-output.txt</p> <pre><code>fwrite($pipes\[0\], $icon); fclose($pipes\[0\]); $conv = stream\_get\_contents($pipes\[1\]); fclose($pipes\[1\]); $err = $pipes\[2\]; fclose($pipes\[2\]); // It is important that you close any pipes before calling // proc\_close in order to avoid a deadlock $return\_value = proc\_close($process); if ($return\_value == 0) { header('Content-Type: image/png'); echo $conv; } else { echo $err; echo &quot;command returned $return\_value\\n&quot;; } </code></pre> <p>}</p> <p><a href="" title="Convert Pipe Example Demo">You can view a demo using convert here</a></p> <p>I can't take credit for the stream code; it is for the most part just the example code from the PHP manual.</p> The Dark Corners of Your UI 2014-09-09T00:00:00-00:00 <p><a href=""></a></p> CSS character escape sequences 2014-09-10T00:00:00-00:00 <p><a href=""></a></p> Recent Exploit using Fake Magento Extensions 2014-09-18T00:00:00-00:00 <p><a href=""></a></p> How To Configure Virtual Memory Swap File on a VPS 2014-09-23T00:00:00-00:00 <p><a href=""></a></p> PHP shell scripts for Magento 2014-09-26T00:00:00-00:00 <p><a href=""></a></p> HTML5 WordPress Starter Theme 2014-09-28T00:00:00-00:00 <p><a href=""></a></p> A Framework for deploying WordPress sites with Capistrano 3 2014-09-29T00:00:00-00:00 <p><a href=""></a></p> iOS fix for position fixed elements on input focus 2014-10-02T00:00:00-00:00 <p><a href=""></a></p> Search within a search in SOLR 2014-10-07T00:00:00-00:00 <p><a href=""></a></p> Magento: Remove auto breaks “br/” from product description 2014-10-28T00:00:00-00:00 <p><a href=""></a></p> Build Automation with Composer Scripts 2014-11-21T00:00:00-00:00 <p><a href=""></a></p> Busted: A WordPress Plugin to Force Cache Busting 2014-11-27T00:00:00-00:00 <p><a href=""></a></p> The Best Laravel and PHP Screencasts 2014-11-29T00:00:00-00:00 <p><a href=""></a></p> Install and Config Fail2Ban in Debian 7 Wheezy 2014-12-08T00:00:00-00:00 <p><a href=""></a></p> Permanently Ban Repeat Offenders With fail2ban 2014-12-09T00:00:00-00:00 <p><a href=""></a></p> Laravel 4 Quick Tip: Custom Error Pages 2014-12-15T00:00:00-00:00 <p><a href=""></a></p> thomaswelton/laravel-gravatar · GitHub 2014-12-15T00:00:00-00:00 <p><a href=""></a></p> Exim, DKIM and Debian Configuration 2014-12-16T00:00:00-00:00 <p><a href=""></a></p> How to Create a Facade in Laravel 4 2014-12-16T00:00:00-00:00 <p><a href=""></a></p> Laravel 4 Authentication 2014-12-16T00:00:00-00:00 <p><a href=""></a></p> Laravel Administrator Documentation 2014-12-16T00:00:00-00:00 <p><a href=""></a></p> How to redirect local root mail to an external email address on Linux 2014-12-16T00:00:00-00:00 <p><a href=""></a></p> Using syslog for your php applications 2014-12-16T00:00:00-00:00 <p><a href=""></a></p> A Simplified Laravel ACL 2014-12-18T00:00:00-00:00 <p><a href=""></a></p> Creating Customizable Packages With Routing 2015-01-03T00:00:00-00:00 <p><a href=""></a></p> Command line interface for WordPress 2015-01-06T00:00:00-00:00 <p><a href=""></a></p> Managing Databases with Migrations 2015-01-06T00:00:00-00:00 <p><a href=""></a></p> An Introduction to Content Security Policy 2015-01-07T00:00:00-00:00 <p><a href=""></a></p> Scaling CloudFlare's Massive WAF 2015-01-09T00:00:00-00:00 <p><a href=""></a></p> Updating a Debian package with a new upstream release 2015-01-09T00:00:00-00:00 <p><a href=""></a></p> Installing Magento 2 with Composer 2015-01-12T00:00:00-00:00 <p><a href=""></a></p> Data Validation in Laravel: The Right Way 2015-01-14T00:00:00-00:00 <p><a href=""></a></p> What’s new in Laravel 5 2015-02-15T00:00:00-00:00 <p><a href=""></a></p> Build ngx_pagespeed From Source 2015-02-25T00:00:00-00:00 <p><a href=""></a></p> Improving Laravel 5’s registrar service 2015-03-06T00:00:00-00:00 <p><a href=""></a></p> Pluralization for JavaScript 2015-03-17T00:00:00-00:00 <p><a href=""></a></p> PHP 7 Feature Freeze 2015-04-05T00:00:00-00:00 <p><a href=""></a></p> Laravel Forge - Hosting on AWS 2015-04-20T00:00:00-00:00 <p><a href=""></a></p> Strong SSL Security on nginx 2015-05-07T00:00:00-00:00 <p><a href=""></a></p> Creating Responsive Shapes With Clip-Path And Breaking Out Of The Box – Smashing Magazine 2015-05-11T00:00:00-00:00 <p><a href=""></a></p> Installation — Mockery 2015-05-11T00:00:00-00:00 <p><a href=""></a></p> OCSP Stapling with HAProxy 2015-05-21T00:00:00-00:00 <p><a href=""></a></p> Nginx + WordPress + fastcgi_cache with conditional purging 2015-05-29T00:00:00-00:00 <p><a href=""></a></p> On HTTP, Middleware, and PSR-7 2015-06-03T00:00:00-00:00 <p><a href=""></a></p> How To Use Docker To Run PHPUnit Tests In Parallel 2015-06-05T00:00:00-00:00 <p><a href=""></a></p> Pre-compile your Handlebars templates 2015-06-10T00:00:00-00:00 <p><a href=""></a></p> PHP7 beta1 build process for Debian Wheezy 2015-07-24T00:00:00-00:00 <p><a href=""><img src="" alt="PHP-logo"></a>Last weekend I decided to give PHP7 a try. Given it's beta, I had to compile from source. As I already run a stable package version, this guide doesn't cover the Debian packaging process. Instead we concentrate on installing PHP to an alternate directory.</p> <h3 id="building-php7">Building PHP7 <a class="direct-link" href="#building-php7" aria-hidden="true">#</a></h3> <p>Firstly, install the build dependencies;</p> <p>sudo apt-get -y build-dep php5<br> sudo apt-get -y install bison autoconf automake libtool pkg-config build-essential</p> <p>I decided to download PHP from version control, and checkout the php-7.0.0beta1 tag;</p> <p>git clone <a href=""></a><br> cd php-src/<br> git checkout php-7.0.0beta1<br> ./buildconf --force</p> <p>You have to force buildconf, as by default it believes itself to be a release package. Next, I saved my configure options to a shell script for replay;</p> <p>./configure \<br> --prefix=/opt/php7 \<br> --enable-fpm \<br> --enable-mysqlnd \<br> --enable-opcache \<br> --with-mysqli=mysqlnd \<br> --with-pdo-mysql=mysqlnd \<br> --with-openssl \<br> --with-gd \<br> --with-iconv \<br> --with-curl \<br> --with-mcrypt \<br> --enable-mbstring \<br> --enable-intl \<br> --enable-memcache \<br> --enable-zip \<br> --with-zlib</p> <p>This will compile PHP7 with some basic options, including php-fpm and php-mysqlnd. Finally, we can kick off the build process;</p> <p>make</p> <h3 id="installing-php7-%2F-php-fpm">Installing PHP7 / PHP-FPM <a class="direct-link" href="#installing-php7-%2F-php-fpm" aria-hidden="true">#</a></h3> <p>sudo make install</p> <p>Now PHP7 should be installed to /opt/php7.  We need to add PHP-FPM service management into Debian's init.d folder. PHP ships a init.d script which works fine, except you need to make a slight change;</p> <p>sudo cp ./sapi/fpm/init.d.php-fpm /etc/init.d/php7-fpm<br> sudo chmod +x /etc/init.d/php7-fpm<br> sudo sed -e 's/# Provides:\(\s*\)php-fpm/# Provides:\1php7-fpm/' -i /etc/init.d/php7-fpm<br> sudo update-rc.d php7-fpm defaults</p> <p>Debian will trip up on the LSB Provides: line if you already run the php5-fpm package version. We need to switch the name to something different (i.e. php7-fpm). Above we use sed to change this value, which allows the update-rc.d command to run successfully.</p> <p>Finally, I copy the php.ini-production example from the source distribution;</p> <p>cp ./php.ini-production /opt/php7/lib/php.ini</p> <h3 id="installing-php7-extensions">Installing PHP7 Extensions <a class="direct-link" href="#installing-php7-extensions" aria-hidden="true">#</a></h3> <p>This is where it gets interesting; we configured PHP to install to the /opt/php7 prefix, which leaves the Debian packaged version untouched. This does however complicate matters, as configure will use the package version of php-config. You will need to download packages manually, and configure them using the correct php-config. I required the imagick extension, which hasn't yet got a PHP7 compatible version in PECL;</p> <p>sudo apt-get -y install libmagick++-dev<br> git clone <a href=""></a><br> cd imagick<br> git checkout phpseven<br> /opt/php7/bin/phpize<br> ./configure --with-php-config=/opt/php7/bin/php-config<br> make<br> sudo make install</p> <p>Finally, add the extension to php.ini;</p> <p>sudo sh -c &quot;echo <a href=""></a> &gt;&gt; /opt/php7/lib/php.ini&quot;</p> <p>It's worth noting that the PECL extensions I checked don't seem yet to have been updated to facilitate the changes in the underlying Zend Engine.</p> Cattle vs Kittens - On Cloud Platforms No One Hears the Kittens Dying 2015-07-29T00:00:00-00:00 <p><a href=""></a></p> Vagrant LAMP Stack with Debian Jessie 2015-08-17T00:00:00-00:00 <p><a href=""><img src="" alt="Vagrant"></a>I thought it was about time I chucked my Vagrant <a href="">LAMP stack</a> into VCM, before I lost or broke it. For those unfamiliar with Vagrant, <a href="">go do some reading</a>!</p> <p>It's based upon a not-so-current version of <a href="">Laravel Homestead</a>. I switched it to the <a href="">debian/jessie64</a> base box, and added an <a href="">init shell script</a>. The init script will run every time you run vagrant provision, so I have carefully crafted the script to ensure it will not break things when run multiple times.</p> <h3 id="configure-folder-sharing">Configure Folder Sharing <a class="direct-link" href="#configure-folder-sharing" aria-hidden="true">#</a></h3> <p>You can configure individual VirtualHosts in the sites section, within the <a href="">sites.yaml</a> file. First of all, you can define a shared folder to map from your host machine into the guest;</p> <p>folders:<br> - map: ~/Projects<br> to: /var/www/</p> <p>The above maps the Projects folder in my desktop's &quot;home&quot; folder into debian at /var/www.</p> <h3 id="configure-virtualhosts">Configure VirtualHosts <a class="direct-link" href="#configure-virtualhosts" aria-hidden="true">#</a></h3> <p>You can now map folders within the share to Apache VirtualHosts. Add one per line, such as;</p> <p>sites:<br>     - { map:, to: &quot;/var/www/projects/public&quot; }<br> - { map:, to: &quot;/var/www/another/public&quot; }</p> <p>Note that indentation is important here; the dashes are indented with four spaces, not a single tab character. The map keyword will map a domain to a folder from the Virtualbox shared folder (/var/www) to a VirtualHost definition in Apache. You will of course need to add your development domains to host file.</p> <p>You can mount folders in the same way as you define sites (i.e. multiple shares).</p> <h3 id="limitations">Limitations <a class="direct-link" href="#limitations" aria-hidden="true">#</a></h3> <p>Currently the configuration doesn't handle database provisioning or persistence. The persistence aspect can be solved with a <a href="">plug-in;</a> bootstraping database creation would need to be added to the site provisioning script. It's simple enough to navigate to and create whatever databases are required.</p> Unit Testing - Introduction to PHPUnit 2015-10-13T00:00:00-00:00 <p><a href=""></a></p> Using Doctrine 2 in Zend Framework 2 2016-03-14T00:00:00-00:00 <p><a href=""></a></p> Comic Book FX Lettering with SVG Filters 2016-03-18T00:00:00-00:00 <p><a href=""></a></p> Mockery: A Better Way 2016-03-18T00:00:00-00:00 <p><a href=""></a></p> Getting Started · Spring Boot with Docker 2016-03-30T00:00:00-00:00 <p><a href=""></a></p> Paying Technical Debt - How To Rescue Legacy Code through Refactoring 2016-04-11T00:00:00-00:00 <p><a href=""></a></p> Faker is a PHP library that generates fake data for you 2016-04-12T00:00:00-00:00 <p><a href=""></a></p> Thoughts on Continuous Delivery and Agile development. 2016-04-12T00:00:00-00:00 <p><a href=""></a></p> Readability / Html Content / Article Extractor & Web Scrapping library written in PHP 2016-04-13T00:00:00-00:00 <p><a href=""></a></p> How to implement SRI in your build process 2016-04-20T00:00:00-00:00 <p><a href=""></a></p> Set Up Zend Framework 2 With Behat And Twig 2016-04-22T00:00:00-00:00 <p><a href=""></a></p> Building Domain Model 2016-05-04T00:00:00-00:00 <p><a href=""></a></p> A library of Phing targets for Behat 2016-05-13T00:00:00-00:00 <p><a href=""></a></p> Mock HTTP requests on the server side in your PHP unit tests 2016-05-13T00:00:00-00:00 <p><a href=""></a></p> Simple logging of ZF2 exceptions 2016-05-13T00:00:00-00:00 <p><a href=""></a></p> Standalone Doctrine Migrations redux 2016-07-11T00:00:00-00:00 <p><a href=""></a></p> How to replace the "Action" helper in ZF 2 2016-07-28T00:00:00-00:00 <p><a href=""></a></p> On Deprecating ServiceLocatorAware 2016-07-28T00:00:00-00:00 <p><a href=""></a></p> Zend Framework 2 : Getting Closer with PluginManager 2016-07-28T00:00:00-00:00 <p><a href=""></a></p> Decoupling the Framework 2016-09-15T00:00:00-00:00 <p><a href=""></a></p> phpspec/prophecy: Highly opinionated mocking framework for PHP 5.3+ 2016-10-19T00:00:00-00:00 <p><a href=""></a></p> Gatling Project, Stress Tool 2017-02-03T00:00:00-00:00 <p><a href=""></a></p> Story Mapping, Visual Way of Building Product Backlog 2017-02-03T00:00:00-00:00 <p><a href=""></a></p> Cucumber Spring Integration 2017-02-17T00:00:00-00:00 <p><a href=""></a></p> Envoy: C++ L7 proxy and communication bus 2017-02-17T00:00:00-00:00 <p><a href=""></a></p> Field Dependency Injection Considered Harmful 2017-02-17T00:00:00-00:00 <p><a href=""></a></p> Happy Releases with Maven and Bamboo 2017-02-17T00:00:00-00:00 <p><a href=""></a></p> Maven Versioning Strategy 2017-02-17T00:00:00-00:00 <p><a href=""></a></p> Redirect stdout from child processes to supervisord 2017-02-17T00:00:00-00:00 <p><a href=""></a></p> REST API testing with Cucumber 2017-02-17T00:00:00-00:00 <p><a href=""></a></p> Understanding The Tomcat Classpath 2017-02-17T00:00:00-00:00 <p><a href=""></a></p> Docker error starting userland proxy 2017-09-05T00:00:00-00:00 <p><a href=""></a></p> An introduction to immutable infrastructure 2017-11-15T00:00:00-00:00 <p><a href=""></a></p> Automating Server Setup with Ansible 2017-12-20T00:00:00-00:00 <p><a href=""></a></p> We're http/2 and ipv6 compatible! 2018-02-01T00:00:00-00:00 <p>Well, it seems that way anyway. Unfortunately both my residential broadband and mobile connections lack ipv6 connectivity, so I'm relying on the <a href="">Qualys SSL Test</a> and various other on-line tools to assure me of the ipv6 compatibility. http/2 was easy to test; firefox developer tools tells you which protocol was used for each downloaded asset. Please let me know if I have gotten something obviously wrong!</p> <h3 id="enabling-http%2F2">Enabling http/2 <a class="direct-link" href="#enabling-http%2F2" aria-hidden="true">#</a></h3> <p>Enabling http/2 was quite simple, given that I recently upgraded to Debian 9 and Apache 2.4. I've got both apache and nginx in use in different debian-based environments. I managed to add AAAA records to the relevant domains, and add the necessary http/2 configuration changes on the morning commute. Obviously the main prerequisite to doing so is having your web server configured for TLS connectivity (h2 requires TLS). For my sites, I have chosen to deploy Let's Encrypt X3 signed certificates. I migrated this blog after the <a href="">controversy</a> with Startcom's offering.</p> <h4 id="apache-2.4">Apache 2.4 <a class="direct-link" href="#apache-2.4" aria-hidden="true">#</a></h4> <p>For Apache, it was a case of enabling the <a href="">mod_http2</a> and adding a line of configuration;</p> <pre><code>$ a2enmod http2 $ service apache2 restart </code></pre> <pre><code>&lt;VirtualHost localhost:443&gt; Protocols h2 http/1.1 […] &lt;/VirtualHost&gt; </code></pre> <h4 id="nginx-1.10">Nginx 1.10 <a class="direct-link" href="#nginx-1.10" aria-hidden="true">#</a></h4> <p>It's even easier with <a href="">nginx</a>; the web server already has the http/2 support module included. You simply have to have a TLS-enabled virtualhost, and add the following;</p> <pre><code>listen 443 ssl http2; listen [::]:443 ssl http2; </code></pre> <p>I've not yet experimented with h2c for the TLS redirections on port 80 with either environment. That's a job for another day 😁. It's been a while since I posted here — a very long while. I've been concentrating on honing my craft and continuous improvement. I hope to start sharing some of my learning here once again.</p> … and we're DNSSEC signed too 👏 2018-02-02T00:00:00-00:00 <p>So enabling DNSSEC was easier than I thought. All I had to do was transfer my domains to a new registrar! So i'm now registered with <a href=""></a> — the transfer process was painless, and luckily I wasn't <a href="">charged for the privilege</a>. I think the most gruelling part was entering the IPS tag for my 10-year old domain, and having it disappear from the dashboard. The on-boarding process took ten minutes or less, but if felt like a lifetime.</p> <p>In fairness, 123-reg have been mostly okay for the last 10 years. They never <a href="">lost my domains</a> or anything like that. Unfortunately they haven't evolved much in that time either. They still don't offer two factor authentication, which I dislike, but I've also previously tried and failed to get a DS record added by their support team. Gandi support both OTP and UTF devices, and there's a simple form to enter the DS public key. Oh and no security questions and answers!</p> <p>I have hosted my DNS with cloudflare for a few years now, and all I had to do was copy-paste the public key. Behold!</p> <pre><code>&gt; dig +dnssec +multi ; &lt;&lt;&gt;&gt; DiG 9.10.3-P4-Debian &lt;&lt;&gt;&gt; +dnssec +multi ;; global options: +cmd ;; Got answer: ;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 28889 ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 512 ;; QUESTION SECTION: ; IN A ;; ANSWER SECTION: 299 IN A 299 IN RRSIG A 13 3 300 ( 20180203225447 20180201205447 35273 56Q8v9fn+7/I5dQ2PTbco22BubILf8bFlg2qaqkfzcR0 V53LT7G9K2LpYaWMjgQYwFrZYmBZG1wD5mB1Mgw+Dg== ) </code></pre> <p>Now let's see if I lose all my domains, and I rue the day I transferred them in the name of shiny signed DNS records 🙄</p> Splitting Read & Writes using Replication Driver in Spring Boot 2018-02-17T00:00:00-00:00 <p>My team has recently been working on a new Spring Boot application. One of the many best practice requirements—see <a href="">non-functional requirements</a>—is that we separate our read and write layers when utilising RDBMS (such as MySQL). As a newbie to this particular problem in Java, it seems that the easiest way is to use the official <a href="">replication driver</a> provided by the com.mysql package.</p> <p><strong>Note:</strong> We had some interesting issues with this method. I would not recommend ReplicationDriver for use in a spring boot application.</p> <h3 id="enabling-the-replication-driver">Enabling the Replication Driver <a class="direct-link" href="#enabling-the-replication-driver" aria-hidden="true">#</a></h3> <p>In Spring this is as simple as adding the following to your properties file;</p> <pre><code>spring.datasource.driverClassName=com.mysql.jdbc.ReplicationDriver </code></pre> <p>The replicaton driver assumes that the credentials on the write layer are the same as those for the read layer. To use the slave connection, you can annotate any repository method with <a href="">@Transactional</a> like so;</p> <pre><code>@Transactional(readOnly = true) </code></pre> <h3 id="testing-replicated-reads">Testing Replicated Reads <a class="direct-link" href="#testing-replicated-reads" aria-hidden="true">#</a></h3> <p>To test the solution, I created a replicated cluster launched with docker compose and a pair of <a href="">bitnami/mysql</a> containers. This supports master-slave replication using environment variables. The following configuration worked;</p> <pre><code>version: '2' </code></pre> <p>Bootstrapping the database using flyway, the application should now work and have a replicated slave. What did we used to do without docker 😍? However, you won't be able to easily tell if you are reading from the master or the slave! I ended up bashing into each container, enabling query logging, and tailing the logs whilst firing CRUD requests at the API. I'm pretty sure it's possible through application logs, though, but this was definitive proof that the correct database layer was receiving the queries as we expect.</p> <h3 id="final-thoughts">Final Thoughts <a class="direct-link" href="#final-thoughts" aria-hidden="true">#</a></h3> <p>This solution is far from ideal. The problem lies with the fact that the application and connection pool are unaware of the underlying separation. As far as the application is concerned all the connections are the same. The driver simply directs to one server or another based upon the readOnly flag above—the driver is handling the master / slave connections. You are unable to control the pool sizes between master and slave connections, which is handled automatically. If this functionality is desired then I would recommend a code-based solution.</p> Aspect Oriented Programming with AspectJ and Spring AOP 2018-07-31T00:00:00-00:00 <p>Recently I have been experimenting with aspect oriented programming, and the ease with which you can handle cross-cutting concerns. It's an extremely powerful tool, allowing you to insert pointcuts throughout your code, and inject bytecode around code without polluting your source code. <a href="">AspectJ</a> provides a great deal of flexibility in the methods through which you can instrument your code.</p> <p>For a live demo, take a look at the <a href="">example repository over at github</a>. The examples are using the dynamic proxy generation provided by <a href="">spring</a>. This library provides support for processing the AspectJ annotations as well as an xml-based DSL. Using this method, it is possible to easily collect arbitrary metrics, or inject logging. It can be achieved simply by adding an annotation, or a point-cut around a method.</p> <h3 id="spring-aspect-examples">Spring Aspect Examples <a class="direct-link" href="#spring-aspect-examples" aria-hidden="true">#</a></h3> <p>We will define an aspect which defines two sets of advice. One instruments an execution, which in this case is bound to a specific method. You are able to use interfaces here, or concrete classes. The other applies instrumentation to an annotation. This allows you to attach an annotation to any spring-managed bean, and all calls to the advised methods will be instrumented. The following code demonstrates a spring @Aspect.</p> <pre><code> @Aspect @Component public class ExampleAspect { private Logger log; public ExampleAspect(Logger log) { this.log = log; } @Around(&quot;execution(*;) public Object aspectAroundMethod(ProceedingJoinPoint pjp) throws Throwable {;Entering {}&quot;, pjp.getClass()); Object result = pjp.proceed();;After {}&quot;, pjp.getClass()); return result; } @Around(&quot;@annotation(exampleAnnotation)&quot;) public Object aspectAroundAnnotation(ProceedingJoinPoint pjp, ExampleAnnotation exampleAnnotation) throws Throwable {;Entering {}&quot;, pjp.getClass()); Object result = pjp.proceed();;After {}&quot;, pjp.getClass()); return result; } } </code></pre> <h3 id="annotation-based-aspect">Annotation-based Aspect <a class="direct-link" href="#annotation-based-aspect" aria-hidden="true">#</a></h3> <p>Component scanning the controller below, the call to SomeClassWithAspectedMethodCall.aspectedMethodCall() will be instrumented during object construction;</p> <pre><code> @Controller class ExampleController { SomeClassWithAspectedMethodCall thing; public ExampleController(SomeClassWithAspectedMethodCall thing) { thing.aspectedMethodCall(); // This would work } } @Component class SomeClassWithAspectedMethodCall { @ExampleAnnotation public boolean aspectedMethodCall() { return true; } } </code></pre> <h3 id="execution-based-aspect">Execution-based Aspect <a class="direct-link" href="#execution-based-aspect" aria-hidden="true">#</a></h3> <p>Assuming a web endpoint is hit, the call from the servlet to the mapped method below would be instrumented;</p> <pre><code> @Component class ExampleController { @RequestMapping(&quot;/&quot;) public void exampleRequest(SomeClassWithAspectedMethodCall thing) { return; // This would work } } </code></pre> <h3 id="internal-method-calls">Internal method calls <a class="direct-link" href="#internal-method-calls" aria-hidden="true">#</a></h3> <p>Spring AOP support requires that that aspected instances are spring beans managed by the IoC container. Demonstrated below is a limitation to this approach;</p> <pre><code> @Component class SomeComponent { public SomeComponent() { annotatedMethodCall(); // This won't work } @ExampleAnnotation public boolean annotatedMethodCall() { return true; } } </code></pre> <p>As you can see from the examples, aspect oriented programming can provide clean way to decouple your cross-cutting concerns from implementation. I find this approach to be a great tool in any programmer's toolkit, and look forward to exploring AspectJ further.</p> Handling local certificates with mkcert 2018-08-13T00:00:00-00:00 <p><a href=""></a></p> Using Servlet Filter and Spring Web with Jetty 2018-08-20T00:00:00-00:00 <p>I've been experimenting with servlet filter, along the same lines as my <a href="">experimentation with aspects</a>. Aspects are quite an interesting topic, however for the request instrumention task it seems that filters are much more suitable. Realising your approach is wrong and pivoting is, I feel, an important skill to learn in software development.</p> <p>I must point out something up-front. In writing the <a href="">example for this article</a>, I came to the realisation that spring boot takes care of a lot. I decided to write these examples without using spring boot, in hope that I would learn more about how servlets and containers work — I wasn't disappointed. It handles all the complexities of servlet initialisation, filter registration, embedded servlet containers and so much more.</p> <h3 id="registering-servlet-filters-with-spring">Registering Servlet Filters with Spring <a class="direct-link" href="#registering-servlet-filters-with-spring" aria-hidden="true">#</a></h3> <p>With Spring Boot, you may define an implementation of <code>Filter</code> as a <code>@Bean</code>. A Spring Boot application will—by default—register this as a servlet filter. Alternately, you can define a <code>FilterRegistrationBean</code> which allows you to modify the filter configuration, such as path matching and execution ordering. If you do both, spring seems to automatically register the <code>FilterRegistrationBean</code> over the <code>Filter</code>, giving you a great deal of flexibility.</p> <pre class="language-java"><code class="language-java"><span class="highlight-line"><span class="token annotation punctuation">@Bean</span></span><br><span class="highlight-line"><span class="token keyword">public</span> <span class="token class-name">FilterRegistrationBean</span> <span class="token function">getExampleFilter</span><span class="token punctuation">(</span><span class="token class-name">ExampleServletFilter</span> exampleServletFilter<span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token class-name">FilterRegistrationBean</span> registrationBean</span><br><span class="highlight-line"> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FilterRegistrationBean</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> registrationBean<span class="token punctuation">.</span><span class="token function">setFilter</span><span class="token punctuation">(</span>exampleServletFilter<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> registrationBean<span class="token punctuation">.</span><span class="token function">setOrder</span><span class="token punctuation">(</span><span class="token class-name">Ordered</span><span class="token punctuation">.</span>HIGHEST_PRECEDENCE<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token keyword">return</span> registrationBean<span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre> <p>With Spring Web, you must register a filter before a <code>ContextLoaderListener</code> initializes the context. The servlet context must become read-only after being initialised, except as dictated by the Servlet 3 specification section 4.4. <code>WebApplicationInitializer::onStartup</code> is typically used to initialize both the application and servlet contexts, so filters must be registered during this process. Normally, this would stop you from registering bean instances as filters from the spring IoC container. Spring provides <code>DelegatingFilterProxy</code> to allow spring-managed beans be used as filters, as demonstrated below;</p> <pre class="language-java"><code class="language-java"><span class="highlight-line"><span class="token class-name">DelegatingFilterProxy</span> exampleServletFilter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DelegatingFilterProxy</span><span class="token punctuation">(</span><span class="token string">"exampleServletFilter"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line">servletContext<span class="token punctuation">.</span><span class="token function">addFilter</span><span class="token punctuation">(</span><span class="token string">"exampleServletFilter"</span><span class="token punctuation">,</span> exampleServletFilter<span class="token punctuation">)</span></span><br><span class="highlight-line"> <span class="token punctuation">.</span><span class="token function">addMappingForUrlPatterns</span><span class="token punctuation">(</span></span><br><span class="highlight-line"> <span class="token class-name">EnumSet</span><span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span><span class="token class-name">DispatcherType</span><span class="token punctuation">.</span>REQUEST<span class="token punctuation">)</span><span class="token punctuation">,</span></span><br><span class="highlight-line"> <span class="token boolean">true</span><span class="token punctuation">,</span></span><br><span class="highlight-line"> <span class="token string">"/example-filtered/*"</span><span class="token punctuation">,</span></span><br><span class="highlight-line"> <span class="token string">"/example-exception/*"</span></span><br><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre> <p>You must refer to your bean by a defined name, rather than it's <code>Class&lt;&gt;</code>. The initialization is the main difference. Your application code must set up any <code>Filter</code> up front, whereas with Spring Boot this can be wired up in configuration or external dependencies.</p> <h3 id="a-note-on-servlet-filter-ordering">A note on Servlet Filter Ordering <a class="direct-link" href="#a-note-on-servlet-filter-ordering" aria-hidden="true">#</a></h3> <p>Spring Boot's <code>FilterRegistrationBean</code> and <code>@Bean</code> support explicit ordering. This is demonstrated above, using <code>FilterRegistrationBean::setOrder</code>, or alternately the <code>@Order</code> annotation. Servlet filters don't support ordering when using Java-based servlet configuration, which means that there's no explicit way to order filters in our example.</p> Designing robust and predictable APIs with idempotency 2019-04-24T00:00:00-00:00 <p><a href=""></a></p> Getting started with a Static Site Generator 2019-10-08T00:00:00-00:00 <h1 id="finding-a-framework">Finding a Framework <a class="direct-link" href="#finding-a-framework" aria-hidden="true">#</a></h1> <p>There are a plethora of options when it comes to static site generators. There's even a <a href="">website</a> to find one best suited to you. A few options were considered, based on stumbling around and trying a few out;</p> <h2 id="jekyll"><a href="">Jekyll</a> <a class="direct-link" href="#jekyll" aria-hidden="true">#</a></h2> <p>Jekyll was one of the first to come to mind. Sadly, if I need to learn a new programming language to make changes then that's a non-starter for me.</p> <p>So that's a nope.</p> <h2 id="gatsbyjs"><a href="">GatsbyJS</a> <a class="direct-link" href="#gatsbyjs" aria-hidden="true">#</a></h2> <p>Gatsby is feature rich and widely supported, with a plethora of community created plugins. It was very simple to get a starter blog up and running in a relatively short time, using the gatsby blog starter.</p> <p>Unfortunately, I don't want to have to learn ReactJS and GraphQL to make basic changes. The sites generated are also JS heavy, and I have always been a progressive enhancement type.</p> <p>So again, nope.</p> <h2 id="orchid"><a href="">Orchid</a> <a class="direct-link" href="#orchid" aria-hidden="true">#</a></h2> <p>I looked for a toolchain which may map closer to my current skillset, in the Java / Maven / Groovy toolchains. This lead to Orchid, which is billed as a promising documentation framework and SSG. Orchid is written in Kotlin, and so supports any JDK language. The toolchain is based upon gradle.</p> <p>Whilst it uses familiar technologies, there are not many community developed extensions or tutorials available. My love of learning aside, handcrafting every single feature myself is counter-productive to my goal. This project looks quite promising, and may well be revisited in the future.</p> <p>But again, nope.</p> <h2 id="eleventy-(11ty)"><a href="">Eleventy (11ty)</a> <a class="direct-link" href="#eleventy-(11ty)" aria-hidden="true">#</a></h2> <p>I stumbled across this framework whilst looking for help with Gatsby. It's JavaScript-based, and supports a lot of the same plug-ins that you would use for a Gatsby site.</p> <p>The learning curve here is much lower, in my opinion, but you get much less to begin with. I may have to dust off my front-end a little sooner than I hoped 😁</p> Eleventy - Sorting 2019-10-18T00:00:00-00:00 <p>I'm learning Eleventy. Coming from writing (mostly) strictly typed code, JS is really frustrating. It's possibly because I'm using VS Code instead of Intellij, but I don't know how method or type hinting could work. I'll try it out at some point.</p> <p>I figured out a basic method to sort Eleventy collections;</p> <pre class="language-javascript"><code class="language-javascript"><span class="highlight-line"><span class="token comment">// Sort with `Array.sort`</span></span><br><span class="highlight-line">eleventyConfig<span class="token punctuation">.</span><span class="token function">addCollection</span><span class="token punctuation">(</span><span class="token string">"sortedNav"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">collection</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token keyword">return</span> collection<span class="token punctuation">.</span><span class="token function">getFilteredByTag</span><span class="token punctuation">(</span><span class="token string">"nav"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token keyword">return</span> a<span class="token punctuation">.</span>data<span class="token punctuation">.</span>priority <span class="token operator">-</span> b<span class="token punctuation">.</span>data<span class="token punctuation">.</span>priority<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre> <p>I'm using this to sort the <code>nav</code> collection, which is the main navigation at the top of the page. You simply add a frontmatter item named <code>priority</code>;</p> <pre class="language-yaml"><code class="language-yaml"><span class="highlight-line"><span class="token key atrule">title</span><span class="token punctuation">:</span> Some Page</span><br><span class="highlight-line"><span class="token key atrule">tags</span><span class="token punctuation">:</span></span><br><span class="highlight-line"><span class="token punctuation">-</span> nav</span><br><span class="highlight-line"><span class="token key atrule">priority</span><span class="token punctuation">:</span> <span class="token number">1</span></span></code></pre> <p>Finally, you need to switch whatever code is loading the nav collection to use the new <code>sortedNav</code> collection instead;</p> <pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>nav page-header__nav<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> {%- for nav in collections.sortedNav -%}</span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>nav-item{% if nav.url == page.url %} nav-item-active{% endif %}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{{ nav.url | url }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>{{ }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> {%- endfor -%}</span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span></span></code></pre> Working with Gradle Modules 2019-10-21T00:00:00-00:00 <p>Since my adventures with Java began, I have always used Maven as a build and dependency management tool. I had an awareness and even used gradle as a consumer, but never used it for anything but simple use-cases. Whilst building an example application the need arose to migrate a simple gradle app to a multi-module build.</p> <h2 id="why-use-gradle-modules%3F">Why use Gradle Modules? <a class="direct-link" href="#why-use-gradle-modules%3F" aria-hidden="true">#</a></h2> <p>Moving to a modular design helps to keep your code cleaner. Whilst it can seem simpler to maintain a monolithic project, over time changing requirements and developers often lead to a tangle developing within a project. Modules can help maintain clear boundaries within a project, and to aid better understand the intentions of the design. Not only that, your dependencies become explicit rather than implicit.</p> <h2 id="bootstraping-gradle-modules">Bootstraping Gradle Modules <a class="direct-link" href="#bootstraping-gradle-modules" aria-hidden="true">#</a></h2> <h3 id="declare-dependency-on-sibling-project">Declare dependency on sibling project <a class="direct-link" href="#declare-dependency-on-sibling-project" aria-hidden="true">#</a></h3> <pre class="language-groovy"><code class="language-groovy"><span class="highlight-line"><span class="token comment">// project/example-app/settings.gradle</span></span><br><span class="highlight-line">rootProject<span class="token operator">.</span>name <span class="token operator">=</span> <span class="token string">'example-app'</span></span><br><span class="highlight-line"><span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">':example-library'</span><span class="token punctuation">)</span></span></code></pre> <p>When declaring the dependency on the library, we need to ensure that the library is compiled before attempting to compile our application. We declare this requirement using <code>evaluationDependsOn</code>.</p> <pre class="language-groovy"><code class="language-groovy"><span class="highlight-line"><span class="token comment">// project/example-app/build.gradle</span></span><br><span class="highlight-line"><span class="token function">evaluationDependsOn</span><span class="token punctuation">(</span><span class="token string gstring">":example-library"</span><span class="token punctuation">)</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">dependencies <span class="token punctuation">{</span></span><br><span class="highlight-line"> compile <span class="token function">project</span><span class="token punctuation">(</span>path<span class="token punctuation">:</span> <span class="token string">':example-library'</span><span class="token punctuation">)</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre> Deploying Eleventy to IPFS 2019-10-30T00:00:00-00:00 <p>Today saw an milestone achievement made with this blog! We completed our first automated deployment 😎. Naturally the only way to celebrate such an achievement is to share how it was done.</p> <h2 id="ipfs%3F">IPFS? <a class="direct-link" href="#ipfs%3F" aria-hidden="true">#</a></h2> <p><a href="">IPFS</a> is a p2p network, which acts as a distributed file system. It is developed by protocol labs. It is one of several products that form part of the <a href="">decentralized web</a> movement.</p> <h3 id="hosting-content-on-ipfs">Hosting Content on IPFS <a class="direct-link" href="#hosting-content-on-ipfs" aria-hidden="true">#</a></h3> <p>The initial choice was to host an IPFS node. It's rather simple to bootstrap your own node, and publish to it. You can even do so on your <a href="">local machine</a>. There is a caveat if you take this approach; your content is only available when your local machine is online and connected to the network. Therefore I chose to set up an IPFS daemon, running on a low cost VPS. I installed the <a href="">go-ipfs</a> binary and set up as a <a href="">systemd service</a><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> to keep it running.</p> <p>The alternative is a third-party &quot;pinning&quot; service. There are a number available. They provide you with the IPFS hosting, and you upload your content using a web interface or API. I have now opted to switch from self-hosting to a service called <a href="">Pinata</a>. This means that you don't have to maintain an IPFS node, or pay a hosting bill to maintain it. If the site grows, you have to pay for storage.</p> <h3 id="accessing-ipfs-content">Accessing IPFS Content <a class="direct-link" href="#accessing-ipfs-content" aria-hidden="true">#</a></h3> <p>IPFS is a file system. So how do our users gain access to the content that is being posted? In truth, I have no idea how people discover content published on IPFS alone. There is of course a search engine, but I wasn't able to find much of relevance on it at the time of writing.</p> <p>Cloudflare IPFS gateway allows the site to operate from the domain root. This allows an IPFS deployed site to operate just like a regular website. The site only works on a top level domain, so it won't display properly on an IPFS HTTP gateway at present (try <a href=""></a>).</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line">$ <span class="token function">dig</span> -t A</span><br><span class="highlight-line"><span class="token punctuation">;</span><span class="token punctuation">;</span> ANSWER SECTION:</span><br><span class="highlight-line"> <span class="token number">68</span> IN CNAME</span><br><span class="highlight-line"> <span class="token number">300</span> IN A <span class="token number">104.18</span>.253.167</span><br><span class="highlight-line"> <span class="token number">300</span> IN A <span class="token number">104.18</span>.254.167</span><br><span class="highlight-line"> <span class="token number">300</span> IN A <span class="token number">104.18</span>.255.167</span><br><span class="highlight-line"> <span class="token number">300</span> IN A <span class="token number">104.18</span>.64.168</span><br><span class="highlight-line"> <span class="token number">300</span> IN A <span class="token number">104.18</span>.252.167</span></code></pre> <p>IPFS supports a mutable linking system called <a href="">IPNS (Interplanetary Naming Service)</a>. This allows you to create a &quot;permanent&quot; link to a resource which can be changed.</p> <h3 id="deploying-to-ipfs">Deploying to IPFS <a class="direct-link" href="#deploying-to-ipfs" aria-hidden="true">#</a></h3> <p>As mentioned above, you can host on your own node or pin directly (or both).</p> <h4 id="using-a-node">Using a Node <a class="direct-link" href="#using-a-node" aria-hidden="true">#</a></h4> <p>Deployment involves uploading to an IPFS node (or cluster). Using the command-line, you can deploy a directory and save it to IPNS using the following commands;</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line"><span class="token comment"># Assuming your site is in a folder called "site" in the current working directory</span></span><br><span class="highlight-line">$ ipfs <span class="token function">add</span> -r site</span><br><span class="highlight-line">added Qmb4MR6U6Vxk28xDJVc1vxpsGAKpH7Rwt7HF9aaxG9vTzD site/index.htm</span><br><span class="highlight-line">added QmWEpPCPzNJMMeGgvFb4sNx642ZoJowBwAtSr3sTeYeN3v site</span><br><span class="highlight-line"> <span class="token number">22</span> B / <span class="token number">22</span> B <span class="token punctuation">[</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token punctuation">]</span> <span class="token number">100.00</span>%</span></code></pre> <p>Now it's possible to publish your pin to your IPFS node;</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line">$ ipfs name publish QmWEpPCPzNJMMeGgvFb4sNx642ZoJowBwAtSr3sTeYeN3v</span><br><span class="highlight-line">Published to QmdND4XcS6aYcTZxogeJMmVBxqDar4H1wh7AKwf2E1SYzn: /ipfs/QmWEpPCPzNJMMeGgvFb4sNx642ZoJowBwAtSr3sTeYeN3v</span></code></pre> <p>Which you can possibly view on the cloudflare gateway;</p> <pre><code> </code></pre> <h4 id="using-a-pinning-service">Using a Pinning Service <a class="direct-link" href="#using-a-pinning-service" aria-hidden="true">#</a></h4> <p>A popular tool for automated pinning is ipfs-deploy. It is a JavaScript zero-configuration tool, which will attempt to locate your site, pin it and update your DNS provider;</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line"><span class="token comment"># Install ipfs globally</span></span><br><span class="highlight-line">$ <span class="token function">npm</span> -g <span class="token function">install</span> ipfs-deploy</span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token comment"># then deploy your generated site</span></span><br><span class="highlight-line">$ npx ipfs-deploy -p pinata site -d cloudflare</span></code></pre> <p>The documentation for the tool is pretty straight forward, but I plan on covering deployment in more detailed in a follow-up.</p> <h4 id="ipns">IPNS <a class="direct-link" href="#ipns" aria-hidden="true">#</a></h4> <p>IPNS in turn supports a technology called <a href=""><code>DNSLink</code></a>. This feature allows you to create a DNS <code>TXT</code> record linking to an IPFS URL, allowing IPNS to link a domain to a IPFS resource.</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line">$ <span class="token function">dig</span> -t TXT</span><br><span class="highlight-line"><span class="token punctuation">;</span><span class="token punctuation">;</span> ANSWER SECTION:</span><br><span class="highlight-line"> <span class="token number">120</span> IN TXT <span class="token string">"dnslink=/ipfs/QmZ6R943z1QhNaeLrAM4QjftDRLgsyNBQwSGZe32AQfHYC"</span></span></code></pre> <p>Allows you to access a resource through an IPFS gateway like this;</p> <pre><code> </code></pre> <p>Which you can <a href="">try here</a>. It may or may not load, depending on how distributed the current hash is in the network.</p> <h3 id="why-i-won't-be-using-ipfs-yet">Why I won't be using IPFS yet <a class="direct-link" href="#why-i-won't-be-using-ipfs-yet" aria-hidden="true">#</a></h3> <p>Initial loading of the site after deployment is too slow in my opinion. This is due to the need to distribute and cache through the network. Sadly the HTTP gateway doesn't serve old content, so as soon as the dnslink record is updated the site can be inaccessible for a while afterward. The above is not a huge problem if you are using <code>/ipfs</code> links directly.</p> <p>You may also want to provide a mutable IPNS pointer to keep a consistent URL between deployments. This is currently slow to the point of being unusable. With DNSLink, you can either point to an immutable IPFS hash or a mutable IPNS hash.</p> <p>I'm considering a second deployment target. This could allow the site to work both on IPFS gateway services and classical hosting.</p> <hr class="footnotes-sep"> <section class="footnotes"> <ol class="footnotes-list"> <li id="fn1" class="footnote-item"><p>Please don't use root, set up a dedicated service user] <a href="#fnref1" class="footnote-backref">↩︎</a></p> </li> </ol> </section> Gitlab CI from Scratch 2019-11-05T00:00:00-00:00 <p>I only recently discovered how useful <a href="">Gitlab CI</a> is. <a href="">Gitlab</a> is an open source Github competitor. I've used it quite a lot over the years. After signing up for a <a href=""></a> account, you gain access to 2000 free build minutes per month for private projects. You can also run your own private CI runner if you are so inclined.</p> <h2 id="creating-a-basic-ci-build">Creating a Basic CI Build <a class="direct-link" href="#creating-a-basic-ci-build" aria-hidden="true">#</a></h2> <p>We're going to configure Gitlab to create our <a href="">Eleventy</a> static site, and deploy it to <a href="">Gitlab pages</a>. Create a <code>.gitlab-ci.yml</code><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> file in the project's root.</p> <pre class="language-yaml"><code class="language-yaml"><span class="highlight-line"><span class="token key atrule">image</span><span class="token punctuation">:</span> <span class="token string">"node:12"</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token key atrule">before_script</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token punctuation">-</span> npm install</span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token key atrule">site</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token key atrule">script</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token punctuation">-</span> npx eleventy</span><br><span class="highlight-line"> <span class="token key atrule">artifacts</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token key atrule">paths</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token punctuation">-</span> _site</span></code></pre> <p>This will instruct the CI runner to obtain the latest node 12 docker image. <code>before_script</code> is a setup stage to run before all other tasks — in this case we are runing <code>npm install</code>, after which the steps in the <code>site</code> task will execute. <code>artifacts</code> defines a path which will be persisted from the build result, and can be downloaded from the UI.</p> <h2 id="deploying-to-gitlab-pages">Deploying to Gitlab Pages <a class="direct-link" href="#deploying-to-gitlab-pages" aria-hidden="true">#</a></h2> <p>The build process now includes the command to build eleventy. That was easy 😃. If all you need is to build the artifact, then you are done. Our stated goal is deployment to Gitlab Pages, so we have a little more work to do. You will need to enable pages for your project.</p> <pre class="language-yaml"><code class="language-yaml"><span class="highlight-line"><span class="token key atrule">image</span><span class="token punctuation">:</span> <span class="token string">"node:12"</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token key atrule">before_script</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token punctuation">-</span> npm install</span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token key atrule">pages</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token key atrule">stage</span><span class="token punctuation">:</span> deploy</span><br><span class="highlight-line"> <span class="token key atrule">environment</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token key atrule">name</span><span class="token punctuation">:</span> production</span><br><span class="highlight-line"> <span class="token key atrule">url</span><span class="token punctuation">:</span> https<span class="token punctuation">:</span>//</span><br><span class="highlight-line"> <span class="token key atrule">script</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token punctuation">-</span> npx eleventy <span class="token punctuation">-</span><span class="token punctuation">-</span>output=public</span><br><span class="highlight-line"> <span class="token key atrule">cache</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token key atrule">paths</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token punctuation">-</span> node_modules/</span><br><span class="highlight-line"> <span class="token key atrule">artifacts</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token key atrule">paths</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token punctuation">-</span> public</span><br><span class="highlight-line"> <span class="token key atrule">only</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token punctuation">-</span> master</span></code></pre> <p>There is quite a lot going on here.</p> <p>We have changed our build task to <code>pages</code>, which is specifically used by Gitlab Pages. We have updated the output directory to be <code>public</code>, because <a href="">pages requires it</a>. Eleventy output directory is easily configurable using <code>--output=public</code>, and we can then just change the artifact directory to <code>public</code>.</p> <h3 id="resource-caching">Resource Caching <a class="direct-link" href="#resource-caching" aria-hidden="true">#</a></h3> <pre><code>cache: paths: - node_modules/ </code></pre> <p>We have also enabled caching. This aims to reduce the build time, as the <code>node_modules</code> folder will be loaded from previous builds—and updated if necessary—and re-cached after a successful build.</p> <h3 id="pipeline-filter">Pipeline filter <a class="direct-link" href="#pipeline-filter" aria-hidden="true">#</a></h3> <pre><code>only: - master </code></pre> <p>You can define one of more branches for which the pipeline task will execute. Our build will only run if the change was committed to the master branch.</p> <h3 id="bring-your-own-domain">Bring Your Own Domain <a class="direct-link" href="#bring-your-own-domain" aria-hidden="true">#</a></h3> <pre><code>environment: name: production url: </code></pre> <p>Finally, we can configure Gitlab pages to work with your own domain, supporting TLS. You will need to configure your domain in the CI script; You will also need to <a href="">configure your DNS records</a> for your domain.</p> <p>Have fun!</p> <hr class="footnotes-sep"> <section class="footnotes"> <ol class="footnotes-list"> <li id="fn1" class="footnote-item"><p><a href="">Whats a yaml</a>? <a href="#fnref1" class="footnote-backref">↩︎</a></p> </li> </ol> </section> Using POSIX Access Control Lists 2019-11-28T00:00:00-00:00 <p>For most unix-based filesystems, file permissions are baked into the filesystem itself. Each file has an owner, a group-level and public access levels, with the ability to restrict <code>read</code>, <code>write</code> and <code>execute</code> for each level. This is sufficient flexibility for most use-cases.</p> <p>There might be some situations where you would like a separate user or group to maintain a different level of access to certain parts of the filesystem.</p> <p>On debian, install the command-line tools;</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line">$ <span class="token function">apt-get</span> <span class="token function">install</span> facl</span></code></pre> <h2 id="imaginary-convoluted-scenario!">Imaginary convoluted scenario! <a class="direct-link" href="#imaginary-convoluted-scenario!" aria-hidden="true">#</a></h2> <p>Now you have access to apply ACLs. Enter our first actor <code>paula</code>, who works on a shared environment with <code>steve</code>. She has some important files that she wishes to allow <code>steve</code> full access, without providing access to all of her other files. Here are the top-level folder contents;</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line">$ <span class="token function">ls</span> -alh</span><br><span class="highlight-line">total 20K</span><br><span class="highlight-line">drwxr-xr-x <span class="token number">3</span> paula paula <span class="token number">4</span>.0K Nov <span class="token number">25</span> <span class="token number">17</span>:59 <span class="token builtin class-name">.</span></span><br><span class="highlight-line">drwxr-xr-x <span class="token number">16</span> paula paula <span class="token number">4</span>.0K Nov <span class="token number">25</span> <span class="token number">10</span>:05 <span class="token punctuation">..</span></span><br><span class="highlight-line">drwxr-xr-x <span class="token number">2</span> paula paula <span class="token number">4</span>.0K Nov <span class="token number">25</span> <span class="token number">10</span>:05 .dat</span><br><span class="highlight-line">-rw-r--r-- <span class="token number">1</span> paula paula <span class="token number">131</span> Nov <span class="token number">25</span> <span class="token number">10</span>:05 dat.json</span><br><span class="highlight-line">-rw-r--r-- <span class="token number">1</span> paula paula <span class="token number">64</span> Nov <span class="token number">25</span> <span class="token number">17</span>:59 index.html</span></code></pre> <p>We can check the current ACL settings of the current directory;</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line">$ getfacl <span class="token builtin class-name">.</span></span><br><span class="highlight-line"><span class="token comment"># file: .</span></span><br><span class="highlight-line"><span class="token comment"># owner: paula</span></span><br><span class="highlight-line"><span class="token comment"># group: paula</span></span><br><span class="highlight-line">user::rwx</span><br><span class="highlight-line">group::r-x</span><br><span class="highlight-line">other::r-x</span></code></pre> <p>The directory has no ACLs configured. <code>paula</code> would like <code>steve</code> to have write access to everything. She don't want <code>steve</code> to be able to read or modify other files she owns, so ACLs could be an appropriate choice in this case;</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line">$ setfacl -Rm u:steve:rwx,d:u:steve:rwx <span class="token builtin class-name">.</span></span><br><span class="highlight-line">$ getfacl <span class="token builtin class-name">.</span></span><br><span class="highlight-line"><span class="token comment"># file: .</span></span><br><span class="highlight-line"><span class="token comment"># owner: paula</span></span><br><span class="highlight-line"><span class="token comment"># group: paula</span></span><br><span class="highlight-line">user::rwx</span><br><span class="highlight-line">user:steve:rwx</span><br><span class="highlight-line">group::r-x</span><br><span class="highlight-line">mask::rwx</span><br><span class="highlight-line">other::r-x</span></code></pre> <p>As you can now see, <code>user:steve:rwx</code> now has <code>rwx</code> permissions on the directory.</p> <ul> <li><code>-R</code> requests permissions be applied recursively</li> <li><code>-m</code> appends this ACL to existing ACLs, as opposed to replacing.</li> <li><code>u:steve:rwx</code> applies the ACL to all existing files <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></li> <li><code>d:u:steve:rwx</code> applies the default ACL for new files and folders added.</li> <li><code>.</code> is the path, which in this case is the current working directory.</li> </ul> <p>Steve is now able to modify and create new files within Paula's directory.</p> <h2 id="acl-masking">ACL Masking <a class="direct-link" href="#acl-masking" aria-hidden="true">#</a></h2> <p>It's worth noting that an ACL may never exceed the access level granted to the actual owner and group;</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line">$ getfacl</span><br><span class="highlight-line"><span class="token comment"># file:</span></span><br><span class="highlight-line"><span class="token comment"># owner: paula</span></span><br><span class="highlight-line"><span class="token comment"># group: paula</span></span><br><span class="highlight-line">user::rw-</span><br><span class="highlight-line">user:steve:rwx <span class="token comment">#effective:rw-</span></span><br><span class="highlight-line">group::r-x <span class="token comment">#effective:r--</span></span><br><span class="highlight-line">mask::rw-</span><br><span class="highlight-line">other::r--</span></code></pre> <p>So as you can see above, although the ACL <code>user:steve:rwx</code> grants <code>rwx</code>, the <code>user</code> default grant provides only <code>rw-</code> as demonstrated by the <code>#effective:rw-</code>.</p> <hr class="footnotes-sep"> <section class="footnotes"> <ol class="footnotes-list"> <li id="fn1" class="footnote-item"><p><a href="">Thanks, StackOverflow</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p> </li> </ol> </section> Install Tools in Alpine Linux 2019-12-25T00:00:00-00:00 <p><a href="">Alpine Linux</a> is a lightweight, secure linux distribution. It's very popular for container-based application deployments.</p> <pre class="language-bash"><code class="language-bash"><span class="highlight-line">$ apk <span class="token function">add</span> <span class="token punctuation">[</span>package<span class="token punctuation">]</span></span></code></pre> <h2 id="useful-packages">Useful Packages <a class="direct-link" href="#useful-packages" aria-hidden="true">#</a></h2> <p>I'll possibly update this page if it's useful. Ive been hopping into our dev cluster to test connectivity recently. You can install packages to containers using the <code>apk</code> command. You would normally do so during the creation of the container image.</p> <h3 id="telnet"><code>telnet</code> <a class="direct-link" href="#telnet" aria-hidden="true">#</a></h3> <pre><code>$ apk add busybox-extras </code></pre> <p>Run tools using the <code>busybox-extras</code> command, e.g.</p> <pre><code>$ busybox-extras telnet localhost 8080 </code></pre> <h3 id="curl"><code>curl</code> <a class="direct-link" href="#curl" aria-hidden="true">#</a></h3> <pre><code>$ apk add curl </code></pre> <h3 id="host"><code>host</code> <a class="direct-link" href="#host" aria-hidden="true">#</a></h3> <pre><code>$ apk add bind-tools </code></pre> <h3 id="mysql"><code>mysql</code> <a class="direct-link" href="#mysql" aria-hidden="true">#</a></h3> <pre><code>$ apk add mysql-client </code></pre> <p>I think this one actually installs <code>mariadb-client</code></p> Goodbye Wordpress 2020-06-30T00:00:00-00:00 <p>After nearly a decade, I have made the switch from <a href="">WordPress</a> to a <a href="">static site generator</a>. When I first fired up my personal blog, I had such high hopes for the possibilities it afforded. My hope is that this reboot breathes new life into my technical blogging.</p> <h2 id="background">Background <a class="direct-link" href="#background" aria-hidden="true">#</a></h2> <p>I could build a CMS in PHP, or so I imagined. Then one day in 2011, I decided WordPress would be good enough for me. For a time, it was. I managed to optimise the death out of it over the years; adding reverse proxy caching, backend caching, opcode caching. I managed to get sub-200ms responses for the entire site. Sadly that stuff is all a nightmare to manage. <a href="">Varnish</a> needs memory for cache storage, and didn't support TLS connections<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>. That necessitated another reverse-proxy to terminate TLS, which was initially <a href="">Pound</a> and later <a href="">HAProxy</a>. These are all worthwhile technologies to learn. I've used a lot of the knowledge I learned at work. For a simple technical blog though, it's way outside of the realms of simple. After ripping most of that out, we are left with mostly sub-second latency — which I <em>was</em> okay with.</p> <p>The next problem is with WordPress itself. It's a piece of relatively complex software which executes on a server. Whatever your opinions on the underlying codebase, this is a risk. At least it is maintained, and the updates can be completely automated. Plugins however are not always so well supported, and sometimes carry security risks of their own. I am lucky that my blog was never caught up in any automated exploits — to my knowledge.</p> <p>Finally—<em>and I feel most importantly</em>—I didn't enjoy the experience of editing in the WordPress UI. It has changed somewhat over the years, including the switch to the Gutenberg page editor. I think I used it a couple of times in the end. I just want to be able to write some words about some code; a new research topic. Markdown is perfect for this pursuit, in my humble opinion.</p> <h2 id="compromises">Compromises <a class="direct-link" href="#compromises" aria-hidden="true">#</a></h2> <p>Naturally, moving from a dynamically generated CMS to a statically generated one exposes some limitations. You can overcome these using client-side rendering, or just don't do the things in the first place. I mean, did anybody really care what Steam games I was playing or music I listened to at any given time? I very much doubt it, other than maybe the people selling the games or streaming the music.</p> <h3 id="ipv6-support-%E2%9C%85">IPv6 Support ✅ <a class="direct-link" href="#ipv6-support-%E2%9C%85" aria-hidden="true">#</a></h3> <p>Gitlab Pages <a href="">doesn't support IPv6</a><sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>. Though to be fair, <em>neither does <a href="">Github Pages</a></em>. The site employs Cloudflare for DNS; after enabling their proxy, the site is now available to end-users on both IPv4 and IPv6 networks. I may have to keep an eye on caching issues with pages though now, for the time being.</p> <h3 id="content-management-%E2%9D%97%EF%B8%8F">Content management ❗️ <a class="direct-link" href="#content-management-%E2%9D%97%EF%B8%8F" aria-hidden="true">#</a></h3> <p>Moving to a static generator, the whole system relies on a CI/CD pipeline to generate and deploy new content to the site. There's no CMS interface to edit content, it's all Markdown in a git repository. This presents a barrier to content publishing; whilst welcomed in some respects, I consider it a bit inconvenient. Thankfully there is a W3C standard for that. I am researching methods to implement a micropub endpoint, which will allow a standards-based approach to content management. More on this soon, hopefully.</p> <h3 id="search-%E2%9D%97%EF%B8%8F">Search ❗️ <a class="direct-link" href="#search-%E2%9D%97%EF%B8%8F" aria-hidden="true">#</a></h3> <p>I have yet to implement a client-side search solution for the blog. Of course, the site is public and should maintain rankings in SERPs from the old website. I put a lot of effort into the migration of content, particularly in maintaining archive-content permalink structure.</p> <p>A potential solution comes in the way of <a href="">Lunr</a>. It's a simplistic weighted search based upon Solr, and relies on a pre-generated index. I plan on adding a search result page to incorporate this, as the index may become quite heavy.</p> <h3 id="bookmark-favicon-support-%E2%9D%97%EF%B8%8F">Bookmark Favicon Support ❗️ <a class="direct-link" href="#bookmark-favicon-support-%E2%9D%97%EF%B8%8F" aria-hidden="true">#</a></h3> <p>One of the small things which I put loads of effort into; Dynamically loading and caching favicons from link-type posts. These would be included anywhere the link post was displayed, such as the link widget or Links category. It was a nice touch, in my opinion. I'm looking at options for how this functionality could be incorporated in a performance-tolerant manner. Possibly using a file-based database like sqlite.</p> <h3 id="examples-%E2%9D%97%EF%B8%8F">Examples ❗️ <a class="direct-link" href="#examples-%E2%9D%97%EF%B8%8F" aria-hidden="true">#</a></h3> <p>Some of my older posts included PHP examples which were deployed to a directory on the host I shut down yesterday. These will now be broken. Soz m8. I may push them to a repository somewhere...</p> <h3 id="comments-%2F-pingback-%2F-trackback-%2F-webmention-%E2%9D%97%EF%B8%8F">Comments / Pingback / Trackback / Webmention ❗️ <a class="direct-link" href="#comments-%2F-pingback-%2F-trackback-%2F-webmention-%E2%9D%97%EF%B8%8F" aria-hidden="true">#</a></h3> <p>I've dropped comments from the site. They have not been migrated, and they are not planned to make a comeback in the near future. Whilst I appreciate feedback, I'll find a better way to incorporate it in <a href="">a more indieweb fashion</a>. None of the above are incorporated into the workflow at this point.</p> <h2 id="to-dos">To-dos <a class="direct-link" href="#to-dos" aria-hidden="true">#</a></h2> <p>I've set up a hit-list in the form of Gitlab issues, the top of which are Accessibility and Metadata. This was released as an <a href="">MVP</a>, once I thought that I had done just enough to ensure that content was migrated well enough to be passable.</p> <ul> <li><strong>Accessibility changes;</strong> whilst it's quite simple in nature, I need to undertake an accessibility review to ensure that my content is accessible to all.</li> <li><strong>Metadata improvements;</strong> I haven't added twitter card or opengraph tags, amongst other things that I probably should have.</li> <li><strong>Mobile / desktop improvements;</strong> the site was built mobile-first, but very little has been done to ensure an optimal experience other than spot checks.</li> </ul> <p>I'm quite happy with the results so far, and there is so much that could be done. I'm happy with where it is right now, which is great progress.</p> <hr class="footnotes-sep"> <section class="footnotes"> <ol class="footnotes-list"> <li id="fn1" class="footnote-item"><p><a href="">It does now</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p> </li> <li id="fn2" class="footnote-item"><p>but <a href="">it is close</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p> </li> </ol> </section> Webmention & WebSub 2020-07-09T08:00:00-00:00 <h2 id="webmention-support">Webmention Support <a class="direct-link" href="#webmention-support" aria-hidden="true">#</a></h2> <p>I've added webmention support through <a href=""></a>. The service is configured to receive webmention requests and store them. They won't yet be displayed in any way, but I will at least be aware of any conversations around the content I publish from compatible web citizens.</p> <p>How could we store and implement webmention into the 11ty workflow? As this site is statically generated, it doesn't have a database. I guess commentary could be progressively enhanced, or we could consider some form of datastore. Another option is json data files. If each post is contained within it's own folder, you can drop in a file with the name of the folder;</p> <pre><code>. └── posts └── a-great-post ├── a-great-post.json └── </code></pre> <p>We can then populate this file with some webmention data;</p> <pre class="language-json"><code class="language-json"><span class="highlight-line"><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token property">"webmentions"</span><span class="token operator">:</span> <span class="token punctuation">[</span></span><br><span class="highlight-line"> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token punctuation">[</span>…<span class="token punctuation">]</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span></span><br><span class="highlight-line"> <span class="token punctuation">]</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre> <p>The payload would need to be atomic to avoid performance concerns. I'm not entirely sure how you go about removing content yet, other than deleting from the JSON file. Also not sure how we go about populating the webmention data into the data file. This may well be re-imagined once things become clearer.</p> <h2 id="limited-websub-support">Limited WebSub Support <a class="direct-link" href="#limited-websub-support" aria-hidden="true">#</a></h2> <p>I have added support for WebSub through the <a href=""></a> service. This is limited as I had to hard-code update notifications on the RSS feeds within the CI pipeline. This was added by modifying the feed output;</p> <pre class="language-xml"><code class="language-xml"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>feed</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token comment">&lt;!--[…]--></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>[](<span class="token punctuation">"</span></span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>hub<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></span></code></pre> <p>It also required updates to the GitLab CI pipeline;</p> <pre class="language-yaml"><code class="language-yaml"><span class="highlight-line"><span class="token key atrule">websub</span><span class="token punctuation">:</span></span><br><span class="highlight-line"> <span class="token key atrule">stage</span><span class="token punctuation">:</span> notify</span><br><span class="highlight-line"> <span class="token key atrule">script</span><span class="token punctuation">:</span> </span><br><span class="highlight-line"> <span class="token punctuation">-</span> curl <span class="token punctuation">-</span>d 'hub.mode=publish<span class="token important">&amp;hub</span>.url=https<span class="token punctuation">:</span>//' https<span class="token punctuation">:</span>//</span></code></pre> <p>This allows subscribers to receive update notifications in near real-time, rather than polling the feed.</p> Implementing and Testing Delayed Processing in Java 2020-09-30T00:00:00-00:00 <p>A common pattern for event driven programs is a delay and retry mechanism. A recent problem I encountered involved delayed processing of messages from a kafka stream. The approach explored was through use of <code>ScheduledExecutorService</code>, which is one of the parts of <a href="">Java's concurrency package</a>. One concrete implementation is <code>ScheduledThreadPoolExecutor</code>. This implementation manages a thread pool, as the name suggests.</p> <p>To start, you will need an instance of the <code>ScheduledThreadPoolExecutor</code> for use in your application. You set the minimum idle threads for the thread pool during construction.</p> <pre class="language-java"><code class="language-java"><span class="highlight-line"><span class="token keyword">public</span> <span class="token class-name">ScheduledExecutorService</span> <span class="token function">scheduledThreadPoolExecutor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ScheduledThreadPoolExecutor</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre> <p>Approaching the problem from a test-driven perspective meant finding a method to reliably test that the processing was in fact delayed. This can be achieved using a <code>CountDownLatch</code>.</p> <pre class="language-java"><code class="language-java"><span class="highlight-line"><span class="token keyword">class</span> <span class="token class-name">BasicScheduledThreadExecutorTest</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">long</span> DELAY_TIME <span class="token operator">=</span> <span class="token number">1_000_000L</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> <span class="token class-name">ScheduledExecutorService</span> scheduledThreadPoolExecutor <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ScheduledThreadPoolExecutor</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> <span class="token annotation punctuation">@Test</span></span><br><span class="highlight-line"> <span class="token keyword">void</span> <span class="token function">testADelayedExecution</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token class-name">CountDownLatch</span> countDownLatch <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CountDownLatch</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> scheduledThreadPoolExecutor<span class="token punctuation">.</span><span class="token function">schedule</span><span class="token punctuation">(</span></span><br><span class="highlight-line"> countDownLatch<span class="token operator">:</span><span class="token operator">:</span><span class="token function">countDown</span><span class="token punctuation">,</span></span><br><span class="highlight-line"> DELAY_TIME<span class="token punctuation">,</span></span><br><span class="highlight-line"> <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span>NANOSECONDS</span><br><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> <span class="token class-name">LocalDateTime</span> start <span class="token operator">=</span> <span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> countDownLatch<span class="token punctuation">.</span><span class="token function">await</span><span class="token punctuation">(</span><span class="token number">2L</span><span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span>SECONDS<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token class-name">LocalDateTime</span> finish <span class="token operator">=</span> <span class="token class-name">LocalDateTime</span><span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> <span class="token keyword">long</span> duration <span class="token operator">=</span> <span class="token class-name">Duration</span><span class="token punctuation">.</span><span class="token function">between</span><span class="token punctuation">(</span>start<span class="token punctuation">,</span> finish<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toNanos</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> <span class="token class-name">MatcherAssert</span><span class="token punctuation">.</span><span class="token function">assertThat</span><span class="token punctuation">(</span>duration<span class="token punctuation">,</span> <span class="token class-name">Matchers</span><span class="token punctuation">.</span><span class="token function">greaterThanOrEqualTo</span><span class="token punctuation">(</span>DELAY_TIME<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>duration<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre> <p>Essentially, the test will wait until the countdown has been exceeded or the latch times out. If it takes longer than 2 seconds the latch throws an <code>InterruptedException</code>, failing the test.</p> <h2 id="problems-with-conversions">Problems with conversions <a class="direct-link" href="#problems-with-conversions" aria-hidden="true">#</a></h2> <p>The above code works well, and provides a fairly stable outcome. When I was experimenting with millisecond durations, I was seeing results which were almost always failing. It seemed like the execution of the task was supposedly always earlier than the delay time defined in the test;</p> <pre><code>java.lang.AssertionError: Expected: a value equal to or greater than &lt;1000L&gt; but: &lt;996L&gt; was less than &lt;1000L&gt; </code></pre> <p>Using nanoseconds fixed this problem. Just something to bear in mind 😉.</p> <h2 id="test-double-with-mockito">Test Double with Mockito <a class="direct-link" href="#test-double-with-mockito" aria-hidden="true">#</a></h2> <p>Obviously this is a toy example. You would likely distribute the executor within your production code, and trigger <code>countDownLatch.countDown()</code> with a test double. If you use Mockito, you can use a spy for this purpose. The syntax is a bit fiddly;</p> <pre class="language-java"><code class="language-java"><span class="highlight-line"><span class="token function">doAnswer</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">Answer</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Void</span><span class="token punctuation">></span></span><span class="token punctuation">)</span> invocation <span class="token operator">-></span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> countDownLatch<span class="token punctuation">.</span><span class="token function">countDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">when</span><span class="token punctuation">(</span>someMockedInterface<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">someMethodThatGetsCalled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre> <p>Essentially, our test double invokes the latch when it is invoked as part of the test harness instead of the production implementation.</p> <h2 id="example-project">Example Project <a class="direct-link" href="#example-project" aria-hidden="true">#</a></h2> <p>Example code for this article can be found over at <a href=""></a></p>