Charl van Niekerk » Blog

Main

Latest

Archives

Powered by Blogger

George Geek Breakfast February 2009

The Geek Breakfast is similar in nature to the Geek Dinner except that it takes place in the morning and is aimed at smaller groups of people. Focus is not really placed on talks given by the presenters but instead on casual conversation between like-minded people.

The George Geek Breakfast now has its own Facebook group and we're planning another meeting this coming week on Friday 6 February 2009 starting at 09:00 usually until around 11:00 in the morning at the Hungry Camel in Meade Street.

We have a Facebook event, iJol event and Google Calendar entry to make it convenient for you.

hCalendar

Tags: geekbreakfast and georgegeekbreakfast.

How to pronounce Ubuntu

Since the debate regarding the pronunciation of Linux has now been laid to rest, I also need to vent some frustration regarding the pronunciation of Ubuntu. Being half African, I know pretty well how to pronounce this word. However, it seems like our brothers over in Europe and the States don't know it too well and think they can get away with "uh-bun-too" or even "uh-bun-tuh" without us noticing. From the Ubuntu FAQ:

Ubuntu, an African word from Zulu and Xhosa, is pronounced "oo-BOON-too".

There we have it. "Oo-boon-too Lehneeks" is the way to go, right? Raait!

Openfire on Ubuntu Hardy

I needed to setup a new Jabber server on an Ubuntu 8.04 (Hardy) server. There are tons of packages for Jabber servers on Ubuntu, but I opted to go for Openfire because of the nice web-based administrative interface and plugins.

The server in question was recently set up and still fairly clean, so no Java installed. I firstly installed Sun's own Java 6 runtime environment:

sudo apt-get install sun-java6-bin

Then, I downloaded the latest Debian package (at the time). Once you have the file, the installation is trivial:

sudo dpkg -i openfire_3.6.3_all.deb

It's a pity they don't seem to have an APT source, then upgrades would have been much more transparent.

Now to run through the setup procedure. Go to the server address, for example:

http://myserver.charlvn.za.net:9090

I set it up to work together with a MySQL database backend.

My iptables configuration also needed some changing. Had to add the following rules:

ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:9090 
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:xmpp-server 
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:xmpp-client

There we go!

Removed Adgator

I temporarily removed the Adgator code from my blog a few weeks ago because there were no advertisers at the time and I wasn't earning any money. A few days ago I put it back as they got some actual ads up, but now I removed it again, for the following two reasons.

The first reason is that it simply does not make financial sense. During December, over the period of about three of four days, I earned R14,27. If I do the maths correctly, this should earn me well over R100 a month. Although this won't even come close to paying the bills, at least it pays for some coffee and beer, perhaps even a pizza or two.

However, now I look at January. I got 2 329 ads served during the month, which isn't too bad in my opinion considering that I didn't even have the code on my blog for a good part of the month. I earned R4,73. I thought it would have gone better over the past five days, but I'm not even earning a whole rand a day. I think this is because there are way too many bloggers and not enough actual ads.

If Adgator had the option to only display an ad when it actually has one, and stop displaying their own ads, perhaps I would have left the code. However, ads decrease the value of my blog. Every ad means the page load time gets worse and the page gets uglier. Also, animated gifs are not cool anymore, and actually haven't been for a long time now.

Although it might not always look the best, I regard this blog as containing premium content. These place holder ads, although not so ugly on their own, are just letting my blog look silly and cheap in my opinion.

Now, on to the second reason: I am in violation of their terms and conditions and am not willing to comply to them either.

It is explicitly stated that You must not edit the code in any way. However, look at the code they gave me:

<!--/* Adgator.co.za Javascript Tag v2.6.3 */-->

<script type='text/javascript'><!--//<![CDATA[
   var m3_u = (location.protocol=='https:'?'https://ads.adgator.co.za/delivery/ajs.php':'http://ads.adgator.co.za/delivery/ajs.php');
   var m3_r = Math.floor(Math.random()*99999999999);
   if (!document.MAX_used) document.MAX_used = ',';
   document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
   document.write ("?zoneid=981");
   document.write ('&amp;cb=' + m3_r);
   if (document.MAX_used != ',') document.write ("&amp;exclude=" + document.MAX_used);
   document.write (document.charset ? '&amp;charset='+document.charset : (document.characterSet ? '&amp;charset='+document.characterSet : ''));
   document.write ("&amp;loc=" + escape(window.location));
   if (document.referrer) document.write ("&amp;referer=" + escape(document.referrer));
   if (document.context) document.write ("&context=" + escape(document.context));
   if (document.mmm_fo) document.write ("&amp;mmm_fo=1");
   document.write ("'><\/scr"+"ipt>");
//]]>--></script><noscript><a href='http://ads.adgator.co.za/delivery/ck.php?n=a868d081&cb=INSERT_RANDOM_NUMBER_HERE' target='_blank'><img src='http://ads.adgator.co.za/delivery/avw.php?zoneid=981&n=a868d081' border='0' alt='' /></a></noscript>

There are unencoded ampersands that break validation. These are simple issues that can easily be corrected, so I did. (Please note that this was before their conditions were published.) Also, they seem to be using target="_blank"! And the border attribute! Please no!!! Please let's not go back into the discussion about what's wrong with those.

Also, why all this other bloat? If you look at Google Adsense for comparison, their code is much cleaner and simpler. What about this?

<script type="text/javascript" src="http://adgator.co.za/adscripts/square/12345"></script>
<noscript>...</noscript>

(12345 could be the client id or so.)

Ok, I know this articles sounds reasonably negative. Just to make things clear, I think it's a brilliant initiative. We really needed something like this that focuses on ad impressions instead of actual click-throughs. However, it's just not working for me right now. I sincerely hope the situation changes so that I can rejoin though.

Mozilla Labs Aurora

Quite a few months ago there was a post on the microformats-discuss list titled Mozilla Labs Aurora mock-up depends on RDFa/uF-like technology. Although I didn't give it much attention at the time due to work pressure and left it in my inbox to follow up later, I rediscovered it a few days ago and decided to take a look at the videos. Frigging wow!

Essentially this is related largely to user interface design, but with a twist. It allows the user to interact in various rich ways with the underlying data in a presentation-independent way. In other words, the user can pull in data from one source and push it into the next, combining and "mashing up" the data as they like.

Obviously, that's much easier said than done. But the concept is a good start.

The other interesting thing is how they see the web seamlessly integrating with the desktop. Indeed, in today's world of web applications, the desktop will probably soon essentially become one big portal to the web.

What particularly excites me is to be able to carry your desktop with you by transferring all your existing "windows" (or whatever you call them) directly to a mobile device.

It seems like Mozilla is looking to build this on top of RDF, which some people might like and some people not. They have a page titled RDF: Aurora about this but there isn't much useful info on it yet.

But, if you have some spare time one evening, I would definitely recommend you to check these out:

  1. Collaboration, History, Data Objects, Basic Navigation
  2. Geo-location-based browsing
  3. Integrating Web w/ Physical Environment
  4. Personal Data Portability

Milk

Roughly a year back, I spoke to Ian Gilfillan at a 27 Dinner in Cape Town. During the evening he told me about milk homogenisation and the dangers thereof to a person's health. Since I knew nothing about this, I was quite interested and decided to do some of my own research. Although Google has a lot on this, I couldn't find anything conclusive at the time, but I definitely agree that there is much room for concern.

Staying in George, we have access to the "real" thing. Unhomogenised milk is not that hard to come by. I never understood this before, but now it all makes sense. The milk you buy in a shop, even though it says "full cream", never has a layer of cream drifting on top. Anybody that is familiar with real "farm" milk would know that if you let the milk stand for only a day, a thick layer of cream will form on top and the rest of the milk will become somewhat watery.

The interesting thing is also that, except for some of the milk from Woolworths and Sally Milk in Botswana, I never see "homogenised" together with "pasteurised" on the label.

Then I read an interesting post recently titled 8 things you didnt know about milk. This does not even mention the homogenisation.

I'm not the kind of person that believes everything I see; I like things to be backed up by some solid scientific evidence. There are a lot of things out there though that is concerning enough to make me very careful. Personally, I started limiting my milk intake over the last month or two and I have to say that my sinuses are already seeing the benefit (unless it's due to some other reason, but it is rather coincidental).

But, what about calcium? It is true that milk contains both calcium and Vitamin D which is essential for the absorption of calcium in the body. Here dolomite is a good alternative. It has lots of calcium and magnesium and the pills I bought from Vital also contains a good dose of Vitamin D.

hProduct and Service Discovery

One of the relatively new microformats is hProduct:

The web is a thriving consumer marketplace with a plethora of manufacturers and vendors scattered across the globe. Sifting through massive amounts of unstructured product data is a difficult task for both browsers and machines alike. The hProduct microformat gives structure to this metadata by providing constructs to identify universal product attributes, plus a mechanism to accommodate the wide variation in product attributes.

The hProduct microformat can assist consumers, manufacturers and retailers in a number of ways. If a browser or application understands that a specific web page contains the hProduct microformat, it can inform and help facilitate the best product choice by the consumer to fulfil their needs. An adopted, open standard will ensure the correct product data is published between manufacturers and retailers, to the benefit of the consumer. Bringing order and structure to product data makes it easy for people and organizations to list the products and services they offer, and easy for web services to consume that product data for analysis and syndication.

In a recent post to the microformats-discuss mailing list titled hProduct draft now available, Jay Myers (the lead web development engineer at BestBuy.com) notifies us that he uploaded a list of online shopping sites he researched to the hProduct Examples wiki page.

Further up on the page, he linked to a blog post of his titled Reviving “Shop URLs” with hProduct. This actually reminded me of the concepts behind SDSN which we discussed roughly a year back as well as the NoseRub API.

Essentially, it seems like cross-site standardisation of an URI scheme is where things are going, instead of focusing too much on XRDS and Yadis for service discovery. It seems like a general trend I'm picking up in the data portability circles these days. Perhaps it's a bit similar to SLP except that it's not for use on a LAN.

MD5 vs SHA-1 RSA Certificates

I just read an article titled Forging RSA-MD5 SSL Certs on Securology. They are advocating the use of SHA-1 above MD5:

There are many CAs that use RSA-MD5 instead of RSA-SHA1. Ripping them from the CA list is probably a good idea, even if it breaks a few web apps. If you are the admin of an e-commerce site using a cert issued by one of these RSA-MD5 CAs, you should probably: 1) Ask for your money back and switch to a different CA, 2) Ask for a new cert issued by an RSA-SHA1 CA, or 3) Forego the purchased certs in lieu of new RSA-SHA1 issued certs, probably in that order of effectiveness.

I'm no cryptography expert, but I am really concerned about SHA-1 too. I did some research on this a while back, and although MD5 suffers from collision issues, SHA-1 does as well. Here's some extracts form an article titled SHA-1 hash function under pressure on Heise Security:

Although the demonstration was restricted to the reduced SHA-1 variant in 64 steps, it can, according to the experts, also be generalised to the standard 80 step variant. This means that SHA-1 must also be considered as cracked in principle. [...]

The latest violation means that the attacks against the reduced step SHA-1 variant has reached the same level as, for example, the attack against the old MD5 algorithm. [...]

The SHA-1 algorithm is still the most commonly used hash algorithm, even though rapid progress in SHA-1 attacks has been being made for some time. The new attack method has yet to be successfully implemented for the unreduced SHA-1 standard, but it is now high time that a suitable successor were found. [...] Until a new standard has been found, more secure alternatives such as SHA-256 and SHA-512 can at least raise the bar for attackers. [...]

Time Zone Weirdness

After a discussion on IRC I was reading up on time zones and found the following on the List of time zones:

Some zones north-south of each other in the mid Pacific differ by 24 hours in time: they have the same time of day but differ by a full day. The two extreme time zones on Earth (both in the mid Pacific) differ by 26 hours. A particular day starts earlier in countries with a more positive UTC offset. Thus the first occurrence of a date will be in UTC+14 and the last of the same date in UTC−12. This gives the interesting feature that during one hour each day there are three different dates in use on land around the world, e.g. at 10:30 UTC Monday it is already 00:30 Tuesday in the Line Islands (UTC+14) while the time is 23:30 Sunday in Samoa (UTC-11).

Luckily, those are islands that are probably not highly developed, but in a more developed region I can imagine that this can create quite a bit of confusion. I'm still getting ready for the weekend while you are already busy with the weekend, even though we are almost on the same longitude. And the business of having more than a 24 hour difference is totally ridiculous in my opinion.

And the funny thing about it is that time zones don't even really exist. They are totally man-made. If you stand next to me, except if you stand directly north or south of me (thus putting us on the same longitude) there is, technically speaking, a slight difference between the time "zone" you're in and the one I'm in. There is then essentially a virtually infinite amount of time zones on the earth. However, us humans like to group things together, like grouping a huge range of time "zones" into one larger "zone".

Why can't we just all work according to UTC zero time? Wouldn't that make it all so much simpler?

In other words, instead of going to work at 08:00 SAST (UTC+02:00) we go to work at 06:00 UTC zero time. It's the same time actually, but we just call it differently. What's the big deal? It should make no difference to anybody's life except that it will eliminate a lot of confusion during international communication.

That's the thing I could never get of daylight savings either. Why not just get up at 07:00 instead of 08:00? That's what we are doing anyway, but we want our clocks to say it's 08:00. Are we humans that dumb?

Beanstalk and FogBugz

Screenshot of FogBugz Ticket List Screenshot of FogBugz Ticket Details

A bit earlier I posted an article about Beanstalk and Harvest, now on to the second thing I played with: FogBugz.

Essentially, this is mainly a bug tracking solution, again with good integration with Beanstalk.

What is very nice is that you can alter the status of bugs directly using your commit message. You can essentially fix a bug and by committing automatically close the associated ticket in FogBugz. You can also specify individual files for review, which appears in the ticket and links directly back to the source code in Beanstalk. The URI of the changeset details in Beanstalk is also given.

I am loving the new era of integrated development tools. However, there is still a lot to be done in this field. We ideally need a solution that can integrate time sheets, invoicing, bug tracking and source code version control in one. It's already now possible to fix a bug and just by committing, close the ticket in FogBugz and add an entry to your time sheet in Harvest. However, next we want to have the time sheet entry to link directly to the bug and back to the source code. We also want the bug ticket to link through not only to the source code but also to the item in the time sheet. Etc etc...

Baruch's Coffee

A few weeks ago, just after returning from a trip out of the country, my mom and I stopped by the Red Berry Farm in George. Considering myself to be a bit of a connoisseur when it comes to coffee (it's even more severe than the way I am about lager and scotch), I'm always on the look-out for a good brew.

They had quite a few different types of coffee from Baruch's Coffee. I never heard of them before, so I picked up a packet and read on the back. Apparently these coffee roasters are located not too far form us in the town of Mossel Bay. The coffee was packaged in plastic, which to be honest I find quite unusual as normally the best way to preserve these types of things is aluminium.

Despite this, being an old cappuccino addict, I decided to give their espresso a try, and I have to say that I am not at all disappointed. For the price, this is definitely not bad coffee (in my opinion). I'll be sure to make a draai there next time I find myself in the Mossel Bay region.

Beanstalk and Harvest

Screenshot of Harvest Reports Summary Screenshot of Harvest Reports Details

I have been using Beanstalk for a while now for non-public subversion hosting. There isn't much I am working on that isn't open source, but for the stuff I'm not able / ready to release yet, there's a good chance I have it sitting on their servers.

One of the things I like about Beanstalk is the integration with other services such as Twitter, Basecamp, Campfire, Lighthouse, FogBugz and Harvest.

We tried out Harvest's free trial to see what it's all about. Harvest essentially helps you manage your time sheets and invoice accordingly. You can set up various task categories (coding, design, admin, etc), some that needs to be charged for and some that are not chargeable so that it knows what to add to the invoices and what not. For example, you can decide that coding and design is chargeable but not admin.

You can have multiple projects and each project can have its own task categories, although you can have "standard" tasks that you can easily reuse between your various projects.

Each person that performs work on the project can then add their various tasks to their time sheet and decide under which categories they fall. Each task can have a description and a duration so that you know who needs to be invoiced what.

There are various views depending on the level you want to see. You can get an overview of the different projects; then you can drill down into each individual project to get an overview of the various task categories, and then you can go right down to the individual task items.

What is especially cool about Harvest is the API. Beanstalk makes use of this and setting the two up to talk to each other is very simple. You can then add items to your task sheet by simply making a commit and formatting your commit message in a specific way.

What would be even cooler though is if there would be a way to easily and cleanly associate the task in Harvest back to the commit changeset in Beanstalk; perhaps something for a future version.

Although I'm not sure if I'll get myself a proper account anytime soon, thumbs up for the idea!

RPMforge on CentOS 5.2

CentOS is quite irritating as far as their packages goes. The standard version of PHP that comes with CentOS is 5.1.x which is unfortunately not acceptable anymore (Zend Framework does not even want to run on anything less than 5.2). Because of this, I have been using Jason Litka's yum repository for some time on a dev box I used to admin. Later on, I just needed more, so started using RPMforge. You can read more about RPMforge on CentOS on the wiki.

However, now I had this really strange problem recently. While trying to run a sudo yum upgrade, I had the following:

Loading "priorities" plugin
Loading "fastestmirror" plugin
Loading mirror speeds from cached hostfile
 * utterramblings: www.jasonlitka.com
 * rpmforge: fr2.rpmfind.net
 * base: centose.centos.org
 * updates: centosj-msync-dvd.centos.org
 * addons: centosa3.centos.org
 * extras: centosj-msync-dvd.centos.org
rpmforge                  100% |=========================| 1.1 kB    00:00     
primary.xml.gz            100% |=========================| 3.0 MB    00:03     
rpmforge  : ################################################## 8243/8243
0 packages excluded due to repository priority protections
Setting up Upgrade Process
Resolving Dependencies
--> Running transaction check
---> Package perl-DBD-Pg.i386 0:2.11.5-1.el5.rf set to be updated
--> Processing Dependency: perl(version) for package: perl-DBD-Pg
---> Package perl-Net-DNS.i386 0:0.63-1.el5.rf set to be updated
---> Package perl-IO-Compress-Zlib.noarch 0:2.015-1.el5.rf set to be updated
---> Package perl-RadiusPerl.noarch 0:0.13-1.el5.rf set to be updated
---> Package perl-IO-Socket-INET6.noarch 0:2.56-1.el5.rf set to be updated
---> Package subversion.i386 0:1.5.5-0.1.el5.rf set to be updated
---> Package perl-Carp-Clan.noarch 0:6.00-1.el5.rf set to be updated
---> Package syslinux.i386 0:3.72-2.el5.rf set to be updated
---> Package geoip.i386 0:1.4.5-1.el5.rf set to be updated
---> Package perl-Mail-POP3Client.noarch 0:2.18-1.el5.rf set to be updated
---> Package perl-Net-SSLeay.i386 0:1.32-1.el5.rf set to be updated
---> Package mtr.i386 2:0.75-1.el5.rf set to be updated
---> Package perl-Archive-Tar.noarch 0:1.40-1.el5.rf set to be updated
--> Processing Dependency: perl(Package::Constants) for package: perl-Archive-Tar
---> Package perl-Crypt-SSLeay.i386 0:0.57-1.el5.rf set to be updated
---> Package perl-Archive-Zip.noarch 0:1.26-1.el5.rf set to be updated
---> Package perl-Compress-Zlib.noarch 0:2.015-1.el5.rf set to be updated
---> Package mod_dav_svn.i386 0:1.5.5-0.1.el5.rf set to be updated
---> Package perl-HTML-Parser.i386 0:3.59-1.el5.rf set to be updated
---> Package perl-Date-Manip.noarch 0:5.54-2.el5.rf set to be updated
---> Package perl-DBD-mysql.i386 0:4.010-1.el5.rf set to be updated
---> Package perl-IO-Compress-Base.noarch 0:2.015-1.el5.rf set to be updated
---> Package perl-XML-Parser.i386 0:2.36-1.el5.rf set to be updated
---> Package perl-DBI.i386 0:1.607-1.el5.rf set to be updated
---> Package perl-IO-Zlib.noarch 0:1.09-1.el5.rf set to be updated
---> Package perl-XML-SAX.noarch 0:0.16-1.el5.rf set to be updated
---> Package perl-IO-String.noarch 0:1.08-1.2.el5.rf set to be updated
---> Package perl-Net-IMAP-Simple-SSL.noarch 0:1.3-1.el5.rf set to be updated
--> Processing Dependency: python-imaging = 1.1.5-5.el5 for package: python-imaging-devel
---> Package perl-MailTools.noarch 0:2.04-1.el5.rf set to be updated
---> Package perl-XML-Twig.noarch 0:3.32-1.el5.rf set to be updated
---> Package perl-Net-IMAP-Simple.noarch 0:1.17-1.el5.rf set to be updated
---> Package perl-XML-Simple.noarch 0:2.18-1.el5.rf set to be updated
---> Package rsync.i386 0:3.0.5-1.el5.rf set to be updated
---> Package lighttpd-fastcgi.i386 0:1.4.20-1.el5.rf set to be updated
---> Package perl-Socket6.i386 0:0.20-1.el5.rf set to be updated
---> Package python-imaging.i386 0:1.1.6-2.el5.rf set to be updated
---> Package lighttpd.i386 0:1.4.20-1.el5.rf set to be updated
---> Package perl-Compress-Raw-Zlib.i386 0:2.015-1.el5.rf set to be updated
---> Package lftp.i386 0:3.7.7-1.el5.rf set to be updated
---> Package perl-Convert-ASN1.noarch 0:0.22-1.el5.rf set to be updated
---> Package perl-IO-Socket-SSL.noarch 0:1.17-1.el5.rf set to be updated
---> Package perl-Data-HexDump.noarch 0:0.02-1.2.el5.rf set to be updated
--> Running transaction check
---> Package perl-version.i386 0:0.74-1.el5.rf set to be updated
--> Processing Dependency: python-imaging = 1.1.5-5.el5 for package: python-imaging-devel
---> Package perl-Package-Constants.noarch 0:0.01-1.el5.rf set to be updated
--> Finished Dependency Resolution
Error: Missing Dependency: python-imaging = 1.1.5-5.el5 is needed by package python-imaging-devel

I could however run sudo yum install unrar without any trouble.

For some reason, previously I manually removed mirrors-rpmforge and rpmforge.repo out of /etc/yum.repos.d. I had to clean this up by uninstalling RPMforge properly by running sudo rpm -e rpmforge-release and then reinstalling it (sudo rpm -i rpmforge-release-0.3.6-1.el5.rf.i386.rpm). This seems to have worked fine; I don't know if this is somehow related to the problem. It's definitely RPMforge though because if I disable it again, yum upgrade runs fine.

Ubuntu, Support and Informedness

There was a post high on muti today titled Worlds Dumbest Woman Blames Ubuntu for College Failure. This links back to an article titled Woman blames Dell for missing online classes.

The fact that the woman was so uninformedness is definitely quite shocking and her approach to the situation even more, but this is not so surprising taking the average so-called "dumb user" into account. (For those not in the industry, "dumb user" does not necessarily mean that the user is all-round dumb, but would generally refer to a user that has very limited experience with computers or technology in general.)

The thing that was very interesting to me though is how Dell recommended the use of Ubuntu. That is definitely a huge step in the right direction! Also, how Verizon was willing to accommodate her is equally positive. Remember the days of "What are you using?!?!" when contacting technical support?

However, one thing does still bother me a little bit. Are people preferring Ubuntu due to its relative stability, security and ease of use, or are they preferring it because of the advantage of cost? Look for example at the following paragraph from the second article linked to above:

That's an operating system for your computer similar to Windows that contains Linux. It's highly regarded among some people and extremely popular with certain circles of computer users because it's free.

I just hope most people reading this will understand what "free" really means (in terms of freedom) and won't see free as in free beer. I can understand that in really impoverished areas, the cost factor is very important, but this should not be the primary reason for switching over under normal circumstances IMHO. Let's hope most individuals get the full picture though.

JSON Creation in C# .NET

One of the very powerful features of PHP is its arrays. Actually, PHP arrays are more like a combination of named and unnamed tree structures than actual arrays.

Getting the same type of structure in C# can be a bit more complicated. You can play with HashTable class but it doesn't quite cut it.

Luckily, C# is quite powerful and allow you to make your own structures that are quite similar to PHP arrays. The LitJson library has done something along those lines with their JsonData class. For example:

using System;
using LitJson;

namespace JsonTest
{
  class Program
  {
    static void Main(string[] args)
    {
      JsonData data = new JsonData();
      data["people"] = new JsonData();
      data["people"].Add(new JsonData());
      data["people"][0]["name"] = "Charl";
      data["people"][0]["age"] = 22;
      data["people"].Add(new JsonData());
      data["people"][1]["name"] = "Jannie";
      data["people"][1]["age"] = 20;
      string json = JsonMapper.ToJson(data);
      Console.WriteLine(json);
    }
  }
}

Here is the output of this console application:

{"people":[{"name":"Charl","age":22},{"name":"Jannie","age":20}]}

Not too bad!

JSON Consumption: PHP vs .NET

I made here a small comparison of how JSON web services are consumed between PHP (using the standard JSON PECL library) and .NET 3.5 (using the third-party LitJson library).

Firstly, the PHP version:

#!/usr/bin/php
<?php
$uri = 'http://ws.geonames.org/weatherJSON?north=44.1&south=-9.9&east=-22.4&west=55.2';
$json = file_get_contents($uri);
$data = json_decode($json, true);
foreach ($data['weatherObservations'] as $i) {
  echo $i['stationName'] . "\n";
}

This is supposed to be executed as a shell script.

Then, the .NET version:

using System;
using System.IO;
using System.Net;
using LitJson;

namespace JsonTest
{
  class Program
  {
    static void Main(string[] args)
    {
      string uri = "http://ws.geonames.org/weatherJSON?north=44.1&south=-9.9&east=-22.4&west=55.2";
      string json = new StreamReader(WebRequest.Create(uri).GetResponse().GetResponseStream()).ReadToEnd();
      JsonData data = JsonMapper.ToObject(json);
      foreach (JsonData i in data["weatherObservations"])
      {
        Console.WriteLine(i["stationName"].ToString());
      }
    }
  }
}

This is a console C# application.

Muti .NET 3.5 Application Example

Screenshot of the application

I need to get myself up to scratch again with Microsoft .NET so I have been playing with .NET 3.5 in Visual Studio 2008.

Back when I used to play with .NET 1.1 and Visual Studio 2003 I used .NET's XML tools quite a bit, so that's where I started.

I created a little application that uses a System.Windows.Forms.DataGridView with three columns ("Votes", "Title" and "Link") and then populates the Muti hotlist into it like so:

string uri = "http://muti.co.za/hot?output=xml";
XPathNodeIterator links = new XPathDocument(uri).CreateNavigator().Select("//link");
Posts.Rows.Add(links.Count);
int count = 0;
foreach (XPathNavigator link in links) {
  string votes = link.Evaluate("string(votes)").ToString();
  string title = link.Evaluate("string(title)").ToString();
  string url = link.Evaluate("string(url)").ToString();
  Posts.Rows[count].SetValues(votes, title, url);
  count++;
}

Posts is the DataGridView control and this was using System.Xml.XPath;.

Oh well, it's a start. :)

Open Inviter

I need to implement a system whereby users on a WordPress-based site can easily invite others to the same site. The idea is to allow them to import their existing address books and select all the people they want to include. The first step is to allow them to import their address books from online web services such as Gmail.

Luckily, there seems to be a quite sophisticated system to take care of this task called "Open Inviter".

At first, I downloaded a WordPress plugin from OpenInviter.com. After extracting this plugin into the wp-content/plugins directory, WordPress picked it up as version 1.5.1 but when I tried to install it, it generated the following error:

Fatal error: Cannot redeclare class wp in openinviter-for-wordpress/oi_includes/plugins/wp.php on line 155

I did a few greps but couldn't actually find any place where this file was specifically used or required. It seems like all the files in the plugin's own plugin directory (wp-content/plugins/openinviter-for-wordpress/oi_includes/plugins/) are just loaded by default, so I moved this file (wp.php) out of the directory and afterwards I could install the plugin without any hassles.

Then I saw a notice where WordPress was telling me that there is a new version of the plugin available. I then found this page on WordPress.org where I downloaded version 1.7.0 of the plugin. This version installed without any problems first-time so decided to stick to it instead.

I then had to figure out how to get this to display somewhere on the public front-end. It ended up that it was just a widget I had to place in the sidebar.

It imported contacts from Gmail perfectly, but in the list it displays, there was one check box right at the top with no e-mail address next to it. I then hacked the main plugin script (wp-content/plugins/openinviter-for-wordpress/openinviter.php) so that it does a sanity check to only display check boxes that does actually have a value next to it.

However, I still was not satisfied. I don't just want to have the e-mail addresses of the contacts displayed, I also want to have their names displayed. So I made another change to the script to do just that.

Then, progressing to the next stage, sending the actual invites did not work. Firstly, the check boxes were passing a list of contact names to the step while it actually required the e-mail addresses. This also makes more sense as you can have duplicate contact names, but an e-mail address should be unique. Another small change to the code, and that's sorted.

But, there's more. For some reason, the script wanted to start sending mails to all the contacts. This renders the previous script pointless; so instead of looping through $contacts it should loop through $selected_contacts.

There was something else that was rather strange to me. First, a string value is assigned to $message, and later it was transformed into an array with one of the elements on the array containing the previous string value. But when it comes to sending the mail, it's used as a string again, and the text Array ends up in the mail that gets sent instead of the actual message. I just removed the line of code where an array was assigned to it; another bug fixed.

After the bugs got sprayed with my handy can of Doom, I got on to making some further customisations. Although I'm not sure about this change, I do feel it's a bit silly to try to force something like this down on potential users of the plugin. This should just be part of one of the settings in my opinion. A message This invite was sent using OpenInviter technology. was appended to the bottom of each invite sent.

Now by all means, I would love to support Open Inviter and spread the word about it, but this doesn't feel necessary to me. So I adjusted the code not to include this message. If we end up putting it back later, we'll do it as part of the settings.

Second customisation, and again I'm not sure about this one but thought I should give it a shot, is to make the "From:" e-mail address in the invite mail that gets sent the e-mail address of the person actually sending the invite instead of the site administrator's address.

Please find the full subversion diff below:

Index: wp-content/plugins/openinviter-for-wordpress/openinviter.php
===================================================================
--- wp-content/plugins/openinviter-for-wordpress/openinviter.php (revision 7)
+++ wp-content/plugins/openinviter-for-wordpress/openinviter.php (working copy)
@@ -340,7 +340,6 @@
      $settings=get_option('openinviter_settings');global $openinviter_options;
      $message=(empty($settings['message_body'])?$openinviter_options['message_body']:$settings['message_body']);
      $subject=(empty($settings['message_subject'])?$openinviter_options['message_subject']:$settings['message_subject']);
-     $message=array('subject'=>$subject,'message'=>$message);
      $contacts=array();$selected_contacts=array();
      foreach ($_POST as $key=>$val)
       if(strpos($key,'check_')!==false)
@@ -361,11 +360,9 @@
       {
       if (!function_exists("wp-mail"))
        require_once(ABSPATH.'wp-includes/pluggable.php');
-      $message_footer="\r\n\r\nThis invite was sent using OpenInviter technology.";
-      $message_headers = 'From: "' .get_option('blogname'). '" <wordpress@' .get_option('siteurl'). '>';
-      $temp=$message.$message_footer;
-      foreach($contacts as $email=>$name)
-       wp_mail( $email, sprintf($subject,$_POST['inviter_email_box']), $temp, $message_headers);
+      $message_headers = "From: \"$_POST[inviter_email_box]\" <$_POST[inviter_email_box]>";
+      foreach($selected_contacts as $email=>$name)
+       wp_mail( $email, sprintf($subject,$_POST['inviter_email_box']), $message, $message_headers);
       $oks['sent']=__("Invites sent successfully");
       }
      elseif ($sendMessage===false)
@@ -440,8 +437,11 @@
      foreach ($contacts as $email=>$name)
       {
       $counter++;
-      $contents.="<tr><td><input type='checkbox' name='check_{$counter}' checked value='{$counter}' /><input type='hidden' name='name_{$counter}' value='{$name}' /><input type='hidden' name='email_{$counter}' value='{$name}' /></td><td>{$email}</td></tr>";
+      if ($email) {
+       $displayName = trim($name) ? "$name ($email)" : $email;
+       $contents.="<tr><td><input type='checkbox' name='check_{$counter}' checked value='{$counter}' /><input type='hidden' name='name_{$counter}' value='{$name}' /><input type='hidden' name='email_{$counter}' value='{$email}' /></td><td>$displayName</td></tr>";
       }
+      }
      $contents.="<tr><td colspan='2' align='center'><input type='submit' name='inviter_submit' value='".__("Send Invites")."' /></td></tr>";
      }
     }

Chisimba Patch Descriptions

Here is a cool enhancement to Chisimba. Ticket #2636 (Descriptions associated with patches) has been closed as a solution has been implemented by Paul Scott recently.

Each module (both core and packages) has a register.conf file in the module root containing a MODULE_VERSION value. When some changes to a module has been made and the changes were adequately tested, this value is incremented and thereby a patch is automatically created. This means that the new version of the module will automatically be made available via the module catalogue so that site admins can easily upgrade.

However, up until now we could not provide site admins with a reason for the upgrade. That has changed with the new UPDATE_DESCRIPTION string we can place inside the register.conf files. As two examples, see the module catalogue register.conf and the skin register.conf.

As later confirmed, going forward each register.conf should have one UPDATE_DESCRIPTION string containing the upgrade message associated with the latest MODULE_VERSION value, so the two values can be changed together in the same file. This helps to prevent bloat and makes it quick and easy for developers.

Thanks Paul! :)

Quick and Easy? Right!

Although I can't nearly claim to be the expert on software engineering (or business for that matter), I like to think I have a reasonable academic and professional record and I believe I have a pretty good amount of experience, including dealing with clients.

Sometimes you get great clients, sometimes you get the average ones, and sometimes you pick up the nightmare ones. However, one of the bad types I've encountered suffer from (what I call) the "quick and easy" syndrome.

Every now and then, you get clients that seem to assume the worst. They think even basic things will take forever and be extremely expensive. They might have run into the wrong crowd before and have had some really bad experiences with software development companies that made them fear the industry. It is quite fun to help these clients as they are almost always highly impressed dealing with an organised, professional development firm that can actually deliver on their promises.

However, more often you tend to run into clients that seem to think they can get the world for nothing. This type you especially tend to pick up in "economically disadvantaged" cities such as George and the surrounding areas, but you'll find them everywhere around the country (and probably the world).

I'm not sure if it's cluelessness, an attempt at human manipulation, or both, but these clients you need to manage very carefully. I understand the economy is going through a tough time right now, but at the end of the day, if people do work they need to get paid for it.

The cold, hard fact is that software development is generally a long-winded, difficult, frustrating and expensive process. And in South Africa, as in many other places in the world (although perhaps Africa in general suffers from this particularly badly) we also need to contend with the growing skills shortage.

It is very important to note that, when I speak about skill, I don't just mean coding skill. Writing the actual code is just a small part of the software engineering process. You also need business analysts that can translate the client's business requirements into technical requirements; you need project managers; you need specialists in systems architecture and design; and of course the code monkeys that can translate the technical specification into code. Of course, especially in smaller teams, individuals often take on various roles, but they still need all the relevant skills in order to participate according to expectation in the processes they are part of.

Although I painted a pretty bad picture a paragraph or two up, it doesn't really need to be that bad. If you have all the right skill, and most importantly, manage that skill well right throughout the project, software development can be a very fun and exciting process for everybody involved (including the client). However, if the budget is too limiting to get all the necessary resources (in terms of people or otherwise) or the time line doesn't allow the gathering of those resources, then the project is actually not feasible.

Of course, it isn't wrong to take on a project that strictly is not feasible, sometimes you can compromise and make it work. However, the more you need to compromise, the bigger your risk becomes, and the more careful you must be in managing the client (and your own situation).

The problem is that, in poorer areas where people are often hard-up for work, they tend to compromise too much. And the ironic thing is that, when that happens, instead of making money, they often end up losing money.

But now I'm already going off topic. Getting back onto the "quick and easy" clients.

The problem with these clients seems to be that they often either have unrealistic expectations or otherwise they think they can get away paying you for half (or less) of what they should be paying you to get their work done. It happens quite sneaky. Here's an example.

"So, we have this little job that needs to be finished. It's already half done, but unfortunately the guy [left the company|went on holiday|immigrated to another country|went to visit his sick grandmother]. We just need somebody to pick up where he left off and finish the product. It's really not that much, only about a day or so of work left. But I'm sure for somebody like you it will be a piece of cake."

Then, after you were fooled into quoting on this biased information, you walk in and realise but the whole product is a total mess and even the little bit that has already been done is begging for refactoring. But now the quote has been signed, deposit has been paid. What do you do? Possible way of avoidance: insist on doing proper inspection before even considering to quote!

Also, note how they like to make you feel important. It's a very well known fact: make people feel important and they will eat out of your hand like a puppy. After all, everybody wants to feel important, right? It's a human defect and a result of our selfish nature. (This sounds a bit harsh, but let's face it, aren't we all guilty of this to some degree? I know I am!) :)

Or, take this scenario:

"We have this little product we need to build. However, the problem is that our client is pushing very hard, so we need to get it delivered in a week's time. We have some junior developers involved, but in order to get it done in time, we need to source a more experienced developer to help us out on this one. It's not much work, but we need to get it done ASAP because we can't afford to lose the client."

Yeah sure, sounds reasonable. Until you walk in and realise that the so-called "juniors" are total fools that have no experience nor qualification in any aspect of software development. You find out that there are no specs for what needs to be done, no development environment set up, and apparently nobody has any reasonable idea of what's expected. You can't even really determine what has already been done because code is lying all over the show on different people's workstations; nothing appears to be consolidated; there has been no apparent internal communication between the parties involved; and essentially nobody can give you a straight answer of what goes for what.

So now you go in and be the "nice guy", the "professional", the guy with the experience that will "sort these noobs out". Right. You end up doing all the work yourself, as the rest are too incompetent and you realise it will take you more time to teach them how to do their jobs right than just doing it yourself. You end up missing the deadline because there is delay upon delay that is outside of your control (getting servers set up, getting requirements together, etc) and the manager is mostly inaccessible as he is constantly in meetings or travelling all the time. And from there everything goes down the drain.

Now automatically, after you missed the deadline you are no longer in the position of power but you now need to take the defensive position, as you should have rang the bell earlier if you think you couldn't make it. But now you took it as a challenge, and got so pulled into their problems that you didn't take the time to sit back and think about the situation sanely. In addition, now you invested a lot of time and obviously want to get paid.

What always surprises me though is how manipulating some clients can get, especially when they are desperate. They will employ any and all means at their disposal to make you feel like you're the guilty one, you are letting them down, etc. Without directly kakking on you, they will act as if they are the victims and make you feel even more guilty.

But the manipulation doesn't just start afterwards, this has been going on from day #1. There are ways of putting things, and then there are ways of putting things. You can say exactly the same thing in lots of different ways, but the listener doesn't just form an opinion of the situation by what is actually being said, but also by the way it's said. The way things are phrased, careful choosing the right adjectives and adverbs, the tone of voice, the facial expressions, the body language - all of those can be utilised. At the end of the day, what is being said matters very little - it's all about the perception of the situation. This is also when you realise that half truths are much more dangerous than whole lies, even if it's just because they are more convincing and therefore more difficult to detect.

Ok, perhaps I have been dwelling a bit off the actual topic again, but all of these things are related. What is important is to go in without bias or fear. Don't try to impress anyone; you are a professional just trying to do his job, that's it. Have a clear state of mind; that helps immensely. Always remember who you are and what you came to do. At the end of the day, it's just business. If it doesn't make business sense, don't get involved. You don't owe anybody anything. Just stay professional and factual through it all, don't be afraid to take the time to think things through logically and never be afraid to put up some borders. You need to protect your own interests, just as they are protecting theirs.

Installing MDB2_Schema on Ubuntu Hardy

In order to install a fresh copy of Chisimba on my local PC for development purposes, I needed to get my PHP PEAR setup right.

All went well, until it came to installing MDB2_Schema:

charlvn@charlvn-desktop:~/Desktop$ sudo pear install --alldeps -f MDB2_Schema
WARNING: failed to download pear.php.net/MDB2_Schema within preferred state "stable", will instead download version 0.8.4, stability "beta"
WARNING: failed to download pear.php.net/MDB2 within preferred state "stable", will instead download version 2.5.0b1, stability "beta"
warning: pear/MDB2_Schema requires package "pear/MDB2" (version >= 2.5.0b1), installed version is 2.4.1
downloading MDB2_Schema-0.8.4.tgz ...
Starting to download MDB2_Schema-0.8.4.tgz (76,430 bytes)
.................done: 76,430 bytes
Validation Error: File "www/mdb2_schematool/result.php" in directory "<dir name="/">" has invalid role "www", should be one of data, doc, php, script, test
Validation Error: File "www/mdb2_schematool/index.php" in directory "<dir name="/">" has invalid role "www", should be one of data, doc, php, script, test
Validation Error: File "www/mdb2_schematool/class.inc.php" in directory "<dir name="/">" has invalid role "www", should be one of data, doc, php, script, test
Validation Error: File "www/mdb2_schematool/action.php" in directory "<dir name="/">" has invalid role "www", should be one of data, doc, php, script, test
Parsing of package.xml from file "/tmp/pear/cache/package.xml" failed
Download of "pear/MDB2_Schema" succeeded, but it is not a valid package archive
Error: cannot download "pear/MDB2_Schema"
Download failed
install failed

Ok, fine, so I made a copy of the /tmp/pear/cache/package.xml file and hacked it:

charlvn@charlvn-desktop:~/Desktop$ diff /tmp/pear/cache/package.xml ./package.xml -u
--- /tmp/pear/cache/package.xml 2008-11-18 01:29:37.000000000 +0200
+++ ./package.xml 2009-01-11 05:51:26.000000000 +0200
@@ -120,10 +120,10 @@
    <file baseinstalldir="/" md5sum="3c4d2c9d89398c5692d36299d98f9c6e" name="tests/tests.css" role="test" />
    <file baseinstalldir="/" md5sum="ab4fa5f6da10bed373a5d52ab9b011bf" name="tests/testUtils.php" role="test" />
    <file baseinstalldir="/" md5sum="b7f20bbc8ea7869cf3ad83de7151ebc6" name="tests/test_setup.php.dist" role="test" />
-   <file baseinstalldir="/" md5sum="6d5cbd93111746651a55f8a5295fafca" name="www/mdb2_schematool/action.php" role="www" />
-   <file baseinstalldir="/" md5sum="f56fa369958a6ac0943d235c0c3c6bfd" name="www/mdb2_schematool/class.inc.php" role="www" />
-   <file baseinstalldir="/" md5sum="7fe5faec973ff9743d5a7e2d9bbeb5a2" name="www/mdb2_schematool/index.php" role="www" />
-   <file baseinstalldir="/" md5sum="d10e54dd5e44155c8470d8f3a1856bee" name="www/mdb2_schematool/result.php" role="www" />
+   <file baseinstalldir="/" md5sum="6d5cbd93111746651a55f8a5295fafca" name="www/mdb2_schematool/action.php" role="php" />
+   <file baseinstalldir="/" md5sum="f56fa369958a6ac0943d235c0c3c6bfd" name="www/mdb2_schematool/class.inc.php" role="php" />
+   <file baseinstalldir="/" md5sum="7fe5faec973ff9743d5a7e2d9bbeb5a2" name="www/mdb2_schematool/index.php" role="php" />
+   <file baseinstalldir="/" md5sum="d10e54dd5e44155c8470d8f3a1856bee" name="www/mdb2_schematool/result.php" role="php" />
   </dir>
  </contents>
  <dependencies>

However, now I am going to have to get my hacked copy installed. Firstly, I tried this:

charlvn@charlvn-desktop:/tmp/pear/cache$ sudo pear install MDB2_Schema-0.8.4.tgz
Failed to download pear/MDB2 within preferred state "stable", latest release is version 2.5.0b1, stability "beta", use "channel://pear.php.net/MDB2-2.5.0b1" to install
pear/MDB2_Schema requires package "pear/MDB2" (version >= 2.5.0b1), installed version is 2.4.1
No valid packages found
install failed

Bah. Thought I should fix this first.

charlvn@charlvn-desktop:/tmp/pear/cache$ sudo pear install -f channel://pear.php.net/MDB2-2.5.0b1
downloading MDB2-2.5.0b1.tgz ...
Starting to download MDB2-2.5.0b1.tgz (128,945 bytes)
.............................done: 128,945 bytes
install ok: channel://pear.php.net/MDB2-2.5.0b1
MDB2: Optional feature fbsql available (Frontbase SQL driver for MDB2)
MDB2: Optional feature ibase available (Interbase/Firebird driver for MDB2)
MDB2: Optional feature mssql available (MS SQL Server driver for MDB2)
MDB2: Optional feature mysql available (MySQL driver for MDB2)
MDB2: Optional feature mysqli available (MySQLi driver for MDB2)
MDB2: Optional feature oci8 available (Oracle driver for MDB2)
MDB2: Optional feature pgsql available (PostgreSQL driver for MDB2)
MDB2: Optional feature querysim available (Querysim driver for MDB2)
MDB2: Optional feature sqlite available (SQLite2 driver for MDB2)
MDB2: To install optional features use "pear install pear/MDB2#featurename"

Now we hit the old problem again:

charlvn@charlvn-desktop:/tmp/pear/cache$ sudo pear install MDB2_Schema-0.8.4.tgz
downloading MDB2_Schema-0.8.4.tgz ...
Starting to download MDB2_Schema-0.8.4.tgz (76,430 bytes)
.................done: 76,430 bytes
Validation Error: File "www/mdb2_schematool/result.php" in directory "<dir name="/">" has invalid role "www", should be one of data, doc, php, script, test
Validation Error: File "www/mdb2_schematool/index.php" in directory "<dir name="/">" has invalid role "www", should be one of data, doc, php, script, test
Validation Error: File "www/mdb2_schematool/class.inc.php" in directory "<dir name="/">" has invalid role "www", should be one of data, doc, php, script, test
Validation Error: File "www/mdb2_schematool/action.php" in directory "<dir name="/">" has invalid role "www", should be one of data, doc, php, script, test
Parsing of package.xml from file "/tmp/pear/cache/package.xml" failed
Download of "pear/MDB2_Schema" succeeded, but it is not a valid package archive
Error: cannot download "pear/MDB2_Schema"
Download failed
install failed

Ok fine. Replaced /tmp/pear/cache/package.xml with my hacked copy and tried this:

charlvn@charlvn-desktop:/tmp/pear/cache$ sudo pear install package.xml
ERROR: file ./docs/examples/parse.php does not exist

So we try it with a different working directory:

charlvn@charlvn-desktop:/tmp/pear/cache/MDB2_Schema-0.8.4$ sudo pear install ../package.xml
ERROR: file ../docs/examples/parse.php does not exist

Ah, so the script is smarter than we thought! It calculates the path to the other files relative to the directory containing the package.xml file.

Simply move the package.xml file into the MDB2_Schema-0.8.4 directory and try again:

charlvn@charlvn-desktop:/tmp/pear/cache/MDB2_Schema-0.8.4$ sudo pear install package.xml
install ok: channel://pear.php.net/MDB2_Schema-0.8.4

Cool! Refreshed the installer in Chisimba and it picks it up fine.

Managing person credit using LOLCODE

Ok, this is my first experiment using LOLCODE. I am still a LOLCODE noob, so please excuse if this is wrong. I am busy trying to work my way through the 1.2 spec:

I HAS A charlvn_score ITZ 0                BTW initialise charlvn_score to type NUMBR and value zero
charlvn_score R SUM OF charlvn_score AN 1  BTW increment charlvn_score with 1
charlvn_score R DIFF OF charlvn_score AN 1 BTW decrement charlvn_score with 1
VISIBLE charlvn_score                      BTW output the value of charlvn_score

The spec does not seem to be clear whether UPPIN or NERFIN could be employed within this context, so I decided to take the "long route" for safety.

In PHP, this could be done as follows:

$charlvn_score = 0;
$charlvn_score++;
$charlvn_score--;
echo $charlvn_score;

Of course, you will do the initialisation once, then give (or take) credit as necessary, and output whenever needed. This would not be done in a typical script but instead as an ongoing interactive scripting session.

Any suggestions for improvement are very welcome!

George Geek Breakfast January 2009

Friday morning we had the first Geek Breakfeast in George for 2009. Many thanks to Nikki and Barry from Flexible Developments as well as Elalie, Greg and Zac from GardenRoute.com for attending. We definitely kicked off the year with a bang!

In related news, we now have our own Facebook group. In addition, you can also join the international group if you like. We would even have had some pictures now if it wasn't for my horrible forgetful nature. I put the camera right next to my bed on the table and still forgot to grab it before running out.

The next one should be announced in the following week or two.

Tags: geekbreakfast and georgegeekbreakfast.

Holiday Connectivity Frustrations

Over the past week, my internet connectivity has been unusually bad, but over the past month, cellular communication in particular has been a problem.

This seems to happen every year. When the December holiday season strikes, cellular communication becomes an absolute pain. Landline in general doesn't seem to get too bad, but reaching people mobilly is a biiig challenge.

Obviously, I can understand that a lot of people are making calls to family and friends. However, the decrease in the amount of business calls should (in my mind) help to balance this out.

However, I am speaking about the George area in particular. I suspect that this situation is especially bad over here because of all the people streaming into the area this time of year in order to have a vacation close to the beach.

This is a problem for me though because I constantly get people complaining to me via e-mail that I am not reachable on the phone. I don't even bother to send SMSes anymore because they either get delivered extremely late or otherwise not at all. When I say extremely late, I mean just that. How's three or four days for you? What, to send barely a kilobyte or two of data? You got to be joking me.

I am a Vodacom customer but it seems like MTN customers etc have similar frustrations. Are the networks really that badly flooded? What irritates me even more is that I know about Neotel's fibre infrastructure right through the area. When will they open this up to the cellular service providers? Please guys, I honestly believe we pay more than enough for cellular services. I'm not blaming this on any particular company; this is a market-wide problem, but it needs to be addressed.

Also, does anybody know if the rumour that contract customers get preference is true or not?

Firefox Reported Attack Site / Web Forgery

Screenshot of Firefox 3 Attack Warning Screenshot of Firefox 3 Attach Warning Explanation

I found one or two screenshots on my old PC while doing some clean-up. Firefox, together with Google, implemented a system to help protect users from websites that attempt to install malicious software. Imel ran into some trouble with this last year but managed to sort out his site and all is back to normal again.

Screenshot of Firefox 3 Fishing Warning

Twitter recently ran into some serious phishing trouble, and now it seems like they are doing the same for websites that are guilty of "web forgery". You can read more about it on mozilla.com

Excellent work people, I like!

Back on Skype

Screenshot

If you have been following this blog for some time, you will know that Skype and I are not friends. For some reason, there is only a link to the 32-bit Ubuntu package on the Linux download page. However, today I discovered a section AMD64 in the Skype page on the Ubuntu Community Wiki. Downloaded, installed, and all worked perfectly.

Now you should be seeing me back on Skype much more often. :)

Kate

Screenshot of Kate on Ubuntu Dapper

One of the first things I did after moving my primary workstation over to Linux about two years back was to find a decent code editor. I tried Quanta for a while but got a bit fed up with the performance (my now-old workstation wasn't totally up to scratch) and then moved over to using Kate as it is much faster and still gave me everything I wanted.

Now I am about to reformat this workstation and turn it into a server. However, just before I did that, I took another look at my Kate configuration for the sake of it. I did quite a bit of customisation back then in terms of the colours and made my own colour schema for some languages such as HTML (see screenshot above). Yes, I know the Mac-look-alike theme looks very cheesy now but back then I thought it was cool.

Obviously, before reformatting, I wondered where it stores this type of stuff. This comes out of ~/.kde/share/config/kateschemarc:

[Charl]
Color Background=0,0,0
Color Highlighted Bracket=255,255,153
Color Highlighted Line=76,78,81
Color Icon Bar=234,233,232
Color Line Number=0,0,0
Color MarkType1=0,0,255
Color MarkType2=255,0,0
Color MarkType3=255,255,0
Color MarkType4=255,0,255
Color MarkType5=160,160,164
Color MarkType6=0,255,0
Color MarkType7=255,0,0
Color Selection=2,130,198
Color Tab Marker=174,174,174
Color Word Wrap Marker=119,122,127
Font=Monospace,10,-1,2,50,0,0,0,0,0

And this comes out of ~/.kde/share/config/katesyntaxhighlightingrc (the actual HTML styling - there are also others but there's no point pasting them all):

[Highlighting HTML - Schema Charl]
Alerts:Alert=10,,,,,,,,,---
Alerts:Normal Text=0,,,,,,,,,---
CSS:Alert=10,,,,,,,,,---
CSS:At Rule=3,,,,,,,,,---
CSS:Comment=8,,,,,,,,,---
CSS:Error=13,,,,,,,,,---
CSS:Important=1,,,,,,,,,---
CSS:Media=3,,,1,,,,,,---
CSS:Normal Text=0,,,,,,,,,---
CSS:Property=1,,,,,,,,,---
CSS:Region Marker=12,,,,,,,,,---
CSS:Selector Attr=6,,,,,,,,,---
CSS:Selector Class=5,,,,,,,,,---
CSS:Selector Id=5,,,1,,,,,,---
CSS:Selector Pseudo=3,,,,,,,,,---
CSS:String=7,,,,,,,,,---
CSS:Unknown Property=1,,,,1,,,,,---
CSS:Value=2,,,,,,,,,---
HTML:Attribute=9,ff00e61b,ff00ff00,,,,,,,---
HTML:CDATA=4,ffc800ff,ff1eff00,0,,,,,,---
HTML:Comment=8,ffffbb00,ff37ff00,,0,,,,,---
HTML:Doctype=2,ff9b04ff,ff1eff00,0,,,,,,---
HTML:Element=1,ff88ff00,ff2bff00,0,,,,,,---
HTML:EntityRef=3,ff01ffe1,ff2bff00,,,,,,,---
HTML:Error=13,ffff2200,ff00ff1e,,,,,,,---
HTML:Normal Text=0,ffe3e3e3,ff09ff00,0,,,,,,---
HTML:PEntityRef=3,ff01ffe1,ff2bff00,,,,,,,---
HTML:Processing Instruction=1,ffff016f,ff2bff00,0,,,,,,---
HTML:Value=7,ff00c8ff,ff2bff00,,,,,,,---
JavaScript:Char=6,,,,,,,,,---
JavaScript:Comment=8,,,,,,,,,---
JavaScript:Data Type=2,,,,,,,,,---
JavaScript:Decimal=3,,,,,,,,,---
JavaScript:Events=1,fff766d5,ff00ffff,0,0,,,,,---
JavaScript:Float=5,,,,,,,,,---
JavaScript:Function=11,,,,,,,,,---
JavaScript:Keyword=1,,,,,,,,,---
JavaScript:Math=1,ffdba716,ff00ffff,0,0,,,,,---
JavaScript:Normal Text=0,,,,,,,,,---
JavaScript:Objects=1,ff008000,ff00ffff,0,0,,,,,---
JavaScript:Pattern Character Class=4,,,,,,,,,---
JavaScript:Pattern Internal Operator=5,,,,,,,,,---
JavaScript:Region Marker=12,,,,,,,,,---
JavaScript:Regular Expression=9,,,,,,,,,---
JavaScript:String=7,,,,,,,,,---
JavaScript:String Char=6,,,,,,,,,---
JavaScript:Symbol=0,,,,,,,,,---

This was on Kate 2.5.2 (KDE 3.5.2).

Go Stii!

As most of my regular readers will know, I don't often make posts of a personal nature and I'm not one to promote individuals and definitely not for sobby sentimentals. However, I met Stii roughly two years ago in George and have always had a great deal of respect for him ever since; when I read the below this evening it really made my generally frustrating day a whole lot better.

Like many other engineers, I am slowly but surely getting somewhat fed up with all the hype and excessive self promotion going on in the ICT industry in this country. This is indeed a very fresh change.

From Stii's about page:

I’m not a PHP Ninja as I don’t believe that you should fight with code. Neither am I a rockstar since I cannot sing nor can I play guitar like Eric Clapton or Eddie van Halen. Legends are people with legacies. Linus Torvalds, Eric S. Raymond, Guido von Rossum, Mark Shuttleworth and Rasmus Lerdorf, amongst a few, are legends. I’m simply the Code Monkey at Afrigator.

Stii, you might not be a rockstar but I think your attitude rocks millions! :)

Open Yale Courses

Paul Scott posted to the nextgen-online list a bit earlier today about Yale Open Courses. It's always great to see more of such excellent material being released under a Creative Commons license.

They have a lot of downloadable course material as well as video lectures. Unfortunately the downloads are a bit on the large side though, so I would most certainly like to see this mirrored in South Africa on mirror.ac.za and/or mirror.is.co.za. I'll start sending some mails later on tonight requesting this to the admins of those services. Considering that we already have a local MIT OpenCourseWare mirror, I figure we stand a fairly good chance of getting this too, especially with some beer/coffee bribing on the side (just kidding). :)

Activity Streams in Atom

Another step forward in distributed social networking! A few days ago, Martin Atkins wrote a post to atom-syntax introducing plans for Some Draft Atom-related Specs for Activity Streams.

They already have the following drafts going:

New Twitter Phishing Scam

I got a direct message from one of my contacts, who I assume has been exploited, less than a day ago saying Hey, i found a website with your pic on it... LOL check it out here http://twitterblog.access-logins.com/login. I thought this was rather fishy, so I opened this in Lynx. It would appear that they set up a Twitter clone site to gather unsuspecting people's Twitter usernames and passwords, probably to use for spamming or who knows what.

When I did a whois on the access-logins.com domain, I found the following:

Domain Name: ACCESS-LOGINS.COM
Registrar: XIN NET TECHNOLOGY CORPORATION
Whois Server: whois.paycenter.com.cn
Referral URL: http://www.xinnet.com
Name Server: NS.XINNET.CN
Name Server: NS.XINNETDNS.COM
Status: ok
Updated Date: 16-dec-2008
Creation Date: 16-dec-2008
Expiration Date: 16-dec-2009

What? A Chinese lot? Definitely suspicious. And I mean, access-logins.com? That is such a poor attempt at Phishing. It's like they tell you what they want to do, they want to access your login details. Bah.

After Googling I also found another post about this New Phishing Scam: access-logins.com.

RSS/Atom in Chisimba Courses

I currently have a ticket assigned to me titled More use of RSS in courses:

We need to make more use of RSS in courses. At the very minimum, the course content pages should have feeds, perhaps one for the course as a whole, and separate ones for each chapter. In addition, the lecturer needs to be able to generate a RSS feed for the blogs of all students in his class, containing the latest posts for each student. What are some of the other areas where RSS can be useful?

Now, the question would be, exactly which types of feeds would you like to see? One of changes to the existing content? One of new content added? Should it be on a chapter level, course level, or both? Also, note the related ticket implement RSS feeds for changes to individual pages, sections, and the whole site.

Also, any preference between RSS and Atom? Should we support either one, or both? Your opinion matters, so please feel free to voice it on the nextgen-online mailing list and/or by commenting on the ticket in trac and/or by commenting on this post. All your feedback will be taken into consideration. Many thanks in advance!

Lucene Frustration

A few days ago I have been playing with the Chisimba file manager module and found myself frustrated by Lucene. For some reason, it keeps tuning me with errors regarding file permissions. This particularly happened when deleting files and directories. For example:

Fatal error: Uncaught exception 'Zend_Search_Lucene_Exception' with message 'chmod() [function.chmod]: Operation not permitted' in /var/www/vhost/chisimba.charlvn.za.net/core_modules/search/resources/1.5.2/Search/Lucene/Storage/Directory/Filesystem.php:189 Stack trace: #0 /var/www/vhost/chisimba.charlvn.za.net/core_modules/search/resources/1.5.2/Search/Lucene/LockManager.php(85): Zend_Search_Lucene_Storage_Directory_Filesystem->createFile('read.lock.file') #1 /var/www/vhost/chisimba.charlvn.za.net/core_modules/search/resources/1.5.2/Search/Lucene.php(410): Zend_Search_Lucene_LockManager::obtainReadLock(Object(Zend_Search_Lucene_Storage_Directory_Filesystem)) #2 /var/www/vhost/chisimba.charlvn.za.net/core_modules/search/classes/indexdata_class_inc.php(219): Zend_Search_Lucene->__construct('/var/www/vhost/...') #3 /var/www/vhost/chisimba.charlvn.za.net/core_modules/search/classes/indexdata_class_inc.php(174): indexdata->checkIndexPath() #4 /var/www/vhost/chisimba.charlvn.za.net/core_modules/file in /var/www/vhost/chisimba.charlvn.za.net/core_modules/search/resources/1.5.2/Search/Lucene/Storage/Directory/Filesystem.php on line 189

Another strange thing is that, when I try to unzip files, I just get a blank screen (no output returned)!

At first, I gave it the finger and disabled it using the following patch:

Index: core_modules/filemanager/classes/dbfile_class_inc.php
===================================================================
--- core_modules/filemanager/classes/dbfile_class_inc.php (revision 12013)
+++ core_modules/filemanager/classes/dbfile_class_inc.php (working copy)
 -321,7 +321,7 @@
         $extra= array('basefolder'=>$folder[0].'/'.$folder[1]);
         
         $objLucene = $this->getObject('indexdata', 'search');
-        $objLucene->luceneIndex($docId, $docDate, $url, $title, $contents, $teaser, $module, $userId, $tags, $license, $context, $workgroup, $permissions, $dateAvailable, $dateUnavailable, $extra);
+        //$objLucene->luceneIndex($docId, $docDate, $url, $title, $contents, $teaser, $module, $userId, $tags, $license, $context, $workgroup, $permissions, $dateAvailable, $dateUnavailable, $extra);
     }
     
     /**
 -762,7 +762,7 @@
         
         if ($result) {
             $objLucene = $this->getObject('indexdata', 'search');
-            $objLucene->removeIndex('filemanager_file_'.$fileId);
+            //$objLucene->removeIndex('filemanager_file_'.$fileId);
             $this->objQuota = $this->getObject('dbquotas');
             $this->objQuota->updateUsage($filePath);
         }

Then, after cooling down, I gave it another shot and it seems like the solution was fairly simple. My Apache instance is running as the apache user, so:

sudo chown -R apache:apache usrfiles

(usrfiles is obviously located in the root of the Chisimba installation)

Now everything seems to work fine, except that it's much slower with Lucene enabled.

Chisimba Module Loading

As noted in a post by James Scoble to the nextgen-online mailing list back in November, currently if somebody attempts to load a module in Chisimba it will execute the module even if the module has not been installed. Please note that by "installed" I mean properly installing the module via the catalogue and having it create the necessary tables, install language items, etc for that particular module. Obviously it has no chance of executing if the source code isn't even there. For example, my (development) installation is a full checkout of all the modules in the subversion repository, so the code is there for all of them even though they might not all be installed.

Because of these "dependencies" (tables, language items, etc), calling many modules will result in an error being displayed, but some of the simpler modules will execute without any problems.

In my response to James's post, I linked to a patch I created to make Chisimba do this check. This is essentially a modification to the Chisimba core (engine class). As noted in my response, it would be problematic putting this into subversion though, as this is likely to break existing sites if they upgrade. Here is a copy of the patch though, for easy reference.

Index: classes/core/engine_class_inc.php
===================================================================
--- classes/core/engine_class_inc.php   (revision 11528)
+++ classes/core/engine_class_inc.php   (working copy)
 -225,6 +225,14 @@
      */
        private $_objDbConfig;
 
+    /**
+     * The modules object of the module catalogue.
+     *
+     * @access private
+     * @var    object
+     */
+    private $_objModules;
+
        /**
      * The layout template default
      *
 -505,6 +513,9 @@
                //Load the Skin Object
                $this->_objSkin = $this->getObject('skin', 'skin');
 
+               // Load the modules object of the module catalogue.
+               $this->_objModules = $this->getObject('modules', 'modulecatalogue');
+
                //Get default page template
                $this->_pageTemplate = $this->_objSkin->getPageTemplate();
                // Get Layout Template from Config files
 -1672,6 +1683,12 @@
                                throw new customException("Default module not found!");
                        }
                        $this->setErrorMessage("Module {$requestedModule} not found");
+               } elseif (!$this->_objModules->checkIfRegistered($requestedModule)) {
+                       $this->_loadModule('_default');
+                       if (!$this->_objActiveController) {
+                               throw new customException("Default module not found!");
+                       }
+                       $this->setErrorMessage("Module {$requestedModule} not installed");
                }
                // ensure no caching
                if ($this->_objActiveController->sendNoCacheHeaders($this->_action)) {
 -1828,4 +1845,4 @@
                }
        }
 }
-?>
\ No newline at end of file
+?>

Copyright © 2004-2009 Charl van Niekerk. All articles are released under the Creative Commons Attribution 2.5 South Africa licence, unless where otherwise stated.