Wednesday, April 21, 2010

Extra BoardGameGeek Record a Play links.

This is a simple Greasemonkey script that adds "Record a Play" links to the played games lists. This is useful when browsing your friends Recent Plays list as it allows you to directly add a play from the page without having to first open the game. It also sets the play date to that of the original item so if you both played the same game its a simple two click to copy their play to your own account.

A picture is worth a thousand words:

Installation Instructions:
Install the Greasemonkey plug-in for your web browser. Or use this version for Safari(Click NinjaKit for Safari.)
Restart your web browser as necessary.
Install the Greased MooTools and Extra Log Plays Links scripts into Greasemonkey.

Friday, April 16, 2010

PHP MySQL database abstraction class

I wrote a very elegant database interaction class for PHP some time ago.  The class is a simple layer between a PHP application and its database and provides a very clean and efficient interface to the database.  It does not generate the SQL code for you, but rather it makes a cleaner method of calling your SQL code.  It allows you to generate repeatable queries as objects, provides parameter substitution in queries, and allows reading a record via class accessors.  Some samples of these features are shown below.

I have not posted the source code itself as I feel this is one of my more exquisite projects and I don't want to see it taken without credit.  I may be willing to provide the code on request though.



Examples: A simple reader query. (Select)
$users = new query("SELECT userID, username, lastAccess, enabled FROM users;");

if(!$users->is_valid())
   return false;

while($users->fetch())
{
    echo $users->usersID;
}


A simple non-reader query. (Insert, Update, Delete)
if(!query::run("UPDATE users SET session = '$userSessionID', lastAccess = NOW() WHERE userID = $userID;"))
    throw new Exception("Database update failed.");


The same update only using parameters instead of string substitution. There are two ways to do this and both generate identical SQL code.
return query::run("UPDATE users SET session = @1, lastAccess = NOW() WHERE userID = @2;", $userSessionID, $userID);

return query::run("UPDATE users SET session = @sessionID, lastAccess = NOW() WHERE userID = @usersID;",
   array(sessionID => $userSessionID, usersID => $userID));


All three of the above update calls will generate the following statement. Notice how the second two statements automatically quote string and escape any special characters in strings.

UPDATE users SET session = 'SESSION', lastAccess = NOW() WHERE userID = UID;


You can also use parameters in reader queries exactly the same way as above. Also you can prepare the query and then set parameters/execute the query as a seperate step. Again the folloing are identical.
$users = new query("SELECT userID, username, lastAccess, enabled FROM users WHERE username = @username;", false);
$users->username = $username;
$users->execute();

$users = new query("SELECT userID, username, lastAccess, enabled FROM users WHERE username = @1;", false);
$users->execute(true, $username);

$users = new query("SELECT userID, username, lastAccess, enabled FROM users WHERE username = @username;", false);
$users->execute(true, array(username => $username);


To read the results there are a few other options as well.
$users = new query("SELECT userID, username, lastAccess FROM users WHERE username = @username;", false);

foreach($users as $user) {
    $users->username = $user;

    if(!$users->execute())
        continus;

    echo $users->usersID; // Get a coulmn value.
    print_r($users->get_row()); // Print the entire row.
    echo $users->get_md5(); // Time dependent hash.
    echo $users->get_md5('userID', 'username'); // UserID/Username dependent hash.
    echo $users->get_columns(); // Get a list of loaded columns.
}


Also there are a few other calls that may be useful. You can get the number or records and raw SQL statement like so.
$users = new query("SELECT userID, username, lastAccess, enabled FROM users;");
echo $users->get_length();
echo $users->get_last_sql();

query::run("UPDATE users SET groupName = @1, lastEdit = NOW() WHERE groupName = @2;", $newGroupName, $groupName);
echo query::length();
echo query::last_sql();

Backup your FreeBSD system configuration.

I set up a simple script to create a configuration backups of my FreeBSD box and I thought I would share it. Note that this script will only back up the /etc and /usr/local/etc directories and weighs in at just under 1MB per backup.

First create a backup script as we can't execute our complex command directly in cron.  You may want to customize the exclude options to your licking, the two listed exclusions are the rather large gconf defaults, witch is not needed, and the working files for transmission.
sudo vim /usr/local/sbin/backup-config
bash -c 'tar -czf /root/freebsd-cfg-`date "+%Y-%m-%d"`.tgz --exclude={etc/gconf,usr/local/etc/transmission/home/{resume,torrents,Downloads,blocklists}} /etc/ /usr/local/etc/'

Now make it executable.
chmod +x /usr/local/sbin/backup-config

Now add the job to cron and set it to run weekly as root.
sudo vim /etc/crontab# Backup the entire server configuration once a week.
0 1 * * 0 root backup-config 2>/dev/null

Thursday, April 15, 2010

Patch for VIrtualbox not working on Freebsd after updating graphics/png port.

I have had a very frustrating time over the last few days.  After doing a full portupgrade I found that virtualbox-ose would no longer work properly.  The GUI portion was working and I could run vm's but I was unable to manage any machines from the console with VBoxManage.  Trying to do any operations would just die with the following error.

ERROR: failed to create a session object!
ERROR: code NS_ERROR_FACTORY_NOT_REGISTERED (0x80040154) - Class not
registered (extended info not available)
Most likely, the VirtualBox COM server is not running or failed to start.

The only changes where Virtualbox being bumped from 3.1.4 to 3.1.6 and a new version of the dependent graphics/png package.  After much testing I determined that the problem was with the png update change but I couldn't figure out how to resolve it.  I finally found a patch out and about that fixed this problem so I am posting more details here.  The patch listed in that page is actually not a patch at all but rather a replacement makefile so i created a patch and posted instructions below.

Build the pached version
Patch, build, and install.

cd /usr/ports/emulators/virtualbox-ose
sudo wget http://pynej.dnsalias.com/Shared/virtualbox-ose-3.1.6_2-1.patch
sudo patch -p0 < virtualbox-ose-3.1.6_2-1.patch
sudo portupgrade -f virtualbox-ose

Thursday, April 8, 2010

Updatting c# applicationSettings in a ASP.NET Web Application

I have a few .NET web applications, using MVC, that make use of applicationSettings in their configuration.  These settings are semi-constant but do need to be updated from time to time.  I was trying to make an edit screen in the web application for developers so they could edit the applicationSettings without having to get on the server and manually edit the Web.config file.  As expected the applicationSettings are read only when accesses directly in the application and can not be updated.  Also it's not possible to configure the settings as userSettings when running as a web application.   Though we could do this by manualy reading and writing the file I was looking for a simpler way to do it.

After some tinkering I found a fairly simple way of doing this.  Basically we can use a custom ConfigurationManager instance to read the Web.config independently of the application and update this instance of the configuration.  Then we just call the save method and the edited data is saved out.  Here is the code for a simple update call.

Note that this code is tailored for MVC and is looping tough the FormCollection values. You could also explicitly read the post variables as parameters of the post action, or use this outside of MVC entirely. Just keep in mind that however you do it you can't loop by clientSection.Settings as the requirement to remove/re-add each updated value prevents this.


using System.Configuration;

[AcceptVerbs(HttpVerbs.Post), Authorize(Roles = "Admin")]
public ActionResult SaveSettings(FormCollection collection)
{
/* This section of code uses a custom configuration manager to edit the Web.config application settings.
 * These settings are normally read only but web apps don't support user scoped settings.
 * This set of variables is used for system features, not runtime tracking so it it only updated when an 
 *  administrator logs in to reconfigure the system.
 * 
 * Author: Jeremy Pyne 
 * Licence: CC:BY/NC/SA  http://creativecommons.org/licenses/by-nc-sa/3.0/
 */

// Load the Web.config file for editing.  A custom mapping to the file is needed as the default to to match the application's exe filename witch we don't have.
System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap() {ExeConfigFilename = HttpContext.Server.MapPath("..\\Web.config") }, ConfigurationUserLevel.None);

// Find the applicationSettings group.
ConfigurationSectionGroup group = config.SectionGroups["applicationSettings"];
if (group == null)
    throw new AjaxException("Could not find application settings.");

// Find this applications section. Note: APP needs to be replaced with the namespace of your project.
ClientSettingsSection clientSection = group.Sections["APP.Properties.Settings"] as ClientSettingsSection;
if (clientSection == null)
        throw new AjaxException("Could not find Hines settings.");

// Loop through each value we are trying to update.
foreach (string key in collection.AllKeys)
{
    // Look for a setting in the config that has the same name as the current variable.
    SettingElement settingElement = clientSection.Settings.Get(key);

    // Only update values that are present in the config file.
    if (settingElement != null)
    {
        string value = collection[key];

        // Is this is an xml value then we need to do some conversion instead.  This currently only supports the StringCollection class.
        if (settingElement.SerializeAs == SettingsSerializeAs.Xml)
        {
            // Convert the form post (bob,apple,sam) to a StringCollection object.
            System.Collections.Specialized.StringCollection sc = new System.Collections.Specialized.StringCollection();
            sc.AddRange(value.Split(new char[] { ',' }));

            // Make an XML Serilization of the new StringCollection
            System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(System.Collections.Specialized.StringCollection));
            System.IO.StringWriter writer = new System.IO.StringWriter();
            ser.Serialize(writer, sc);

            // Get the xml code and trim the xml definition line from the top.
            value = writer.ToString().Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>", "");
        }

        // This is a custom override for MVC checkboxes.  They post as 'false' when unchecked and 'true,false' when selected.
        if(value == "true,false")
            value = "True";
        if(value == "false")
            value = "False";

        // Replace the settings with a updated settings.  It is necessary to do it this way instead of 
        //  updating it in place so that the configuration manager recognize that the setting has changed.
        // Also we can't just look through clientSection.Settings and update that way because then we wouldn't
        //  to do this exact thing.
        clientSection.Settings.Remove(settingElement);
        settingElement.Value.ValueXml.InnerXml = value;
        clientSection.Settings.Add(settingElement);
    }
}

// Save any changes to the configuration file.  Don't set forceSaveAll or other parts of the Web.config will get overwritten and break.
config.Save(ConfigurationSaveMode.Full);
}

Tuesday, April 6, 2010

Multiple Google Calenders on the iPad

Ok, I was a bit frustrated after setting up the Google Sync to find that I could only sync one calender even though the iPhone supports multiple.  I'm sure this will be fixed in short order by Google but until then here is how you can fix it on your desktop using Firefox.  You may also be able to do this in Safari as shown here but I haven't tested that.

  • First set up Google Sync as an Exchange account.
  • Then on you desktop install the User Agent Switcher extensions.
  • Restart Firefox and then go to Tools->Default User Agent->iPhone 3.0 to change your agent.
  • Now navigate to http://m.google.com/sync and select the iPad entry.
  • The new screen with list your calenders but the JavaScript code still prevents you from selecting multiple.  To get around this just go into the browser preferences and un-check Enable JavaScript under the Content tab. 
  • Now you can select all the calenders you want and save the changes. 

To revert the changes to your browser first recheck the Enable JavaScript option and then change the User Agent back to Default User Agent.

You can also just disable scripts temporarily on google.com with the NoScript plugin if you use it instead switching JavaScript on and off.

Monday, April 5, 2010

My thoughts on the iPad.

So I went to the apple store one Saturday to take a look at the new iPad's.  I hadn't pre-ordered one and wasn't sure if I would end up getting one rig away or wait for a newer model/price drop.  After playing with one for a bit and asking some questions I ended up buying the 16GB model with no accessories. What follows are my impressions of the device, mu thoughts on some of the criticisms, and some things I hope to see in the future.

First off let me say hat I run OSX at home and have an first generation iPhone as well.  I like apple products for their reliability and usability.  I don't have a laptop so I was looking for a portable device that I could use at home and on the go for web surfing and personal uses. I get by with my phone for that now but it is rather slow when it comes to web browsing And the smaller screen limits some of it's usefulness. This is where I am coming from and what I was hoping to find with the iPad.  I found the iPad to be very responsive and the larger display to be stunning.  The Apple applications are top notch as one would expect though the third party apps are somewhat limited of of yet.  It is obvious that some developers have grasped the new user interface that Apple has created and some have not.

As for some of the criticisms of the device, first off. It is not a laptop, it's not a net book, stop with the "you could get a laptop for cheaper".  Net book is cheaper yes, but a sub $500 net book is going to have a tiny screen, limited system resources, and run Windows. This type of device is in my mind a lot more hassle and a lot less useful then the iPad and iPhone OS. Furthermore if I were to get a full laptop it would be attest $1200 for a nice MacBook. Again I tend to avoid Windows and a $500-800 Windows laptop isn't going to have the same lifetime as my tablet. As for the lack of a camera, you wouldn't want to use this to take pictures and video conferencing would be nifty but not realistic. The iPad is much more useful as a tool during communications then a provider of it. I'm sure it is a feature they will add but Its just not a must have.  As far as background processing goes, put it to bed already. So the iPhone OS doesn't allow background processing. I'd rather have a stable and secure device then find out half way through the day my batter is dead because I left so e stupid app running in the background. The push notification system provide a lot of the user interaction with minimal power consumption.  The only service that I have found that would really benefit from background processing is instant messaging.

As far as my hopes for the future there are a few things in the works that I expect in the 4.0 release and some third party apps I hope to see. Background processing for one is currently in testing for the 4.0 release. I would also like to see build in support for printing to network printers instead of needing third party apps.  Hulu will be a nice eater once they release their app as will some better google integration. As for things I hope to see. I would really like an app to view and even manage the shared iPhoto/Aperture libraries on my Mac. This would be nice so I Could play slideshows of all my photos without needing to sync over multiple gigabytes of photos.  The ability to manage the metadata of these libraries would also be a killer feature. The same thing would be nice for video content, that is the ability to browse the network and play video content back that did t come from iTunes, many even with xvid/mkv support. This is more of a pipe dream but I can hope.  Finally I hope to see a wireless sync/media streaming feature from Apple like the AppleTV supports and better controls over automatic downloads and updates of applications and subscriptions.

All in all I like the iPad and am glad I bought it, though there are some minor problems and a lot of opportunities that can all be addressed with software updates and the rid part applications. As an aside I wrote this entire review and posted it from my iPad with minimal effort.