The AADL Developer's Blog. Technical info about what new features we're working on, releasing, and playing with.

AADL Developer Blog

Welcome to the AADL Developer Blog! Software Development is a big part of what we do here at AADL, and this section of aadl.org is the place to keep up with our new features, see what our developers are working on, and find out what kind of tools we're playing with.

We also have open-source software that we've developed available for download, and you can find that here. Please feel free to comment on our posts or contactus if you have any other questions, and thanks for your interest!

 

Git Alias for Log and Status

I found that I often was doing a "git log" plus a "git status" when visiting a repository I hadn't touched in a while, just to get a lay of the land. Here's a quick alias I whipped up for git that combines the two, giving you a peek at how recently the repository has commits and also if there are any uncommitted changes lurking around.

Here's an example of the output:

$ git logstat
== LOG ===========
d5e8186 ejk 2014-08-31 Add badge check for all players function
9c3d1e5 ejk 2014-08-04 Correct default end date on game code form if left blank
1f48884 ejk 2014-07-20 Add curly brackets to badge formula description
== STATUS ========
# On branch master
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
#       ._.DS_Store
#       pdf/._adult_game.pdf
#       pdf/._adult_game_1.pdf
#       pdf/._adult_game_2.pdf
#       pdf/._sg2014_adult.pdf
nothing added to commit but untracked files present (use "git add" to track)

Create the alias from a shell prompt:

$ git config --global alias.logstat 'echo "== LOG ===========" && git log --date=short --pretty=format:"%h %an %ad %s" -n 3 && echo "== STATUS ========" && git status'

Or create the alias in your .gitconfig:

[alias]
	logstat = "!echo \"== LOG ===========\" && git log --date=short --pretty=format:\"%h %an %ad %s\" -n 3 && echo \"== STATUS ========\" && git status"

More about git aliases: //git.wiki.kernel.org/index.php/Aliases

Paying with Points: Creating a Custom Payment Type in Ubercart

Our players have kept the Summer Game Shop busy this summer; as of this writing, we have received and processed 1599 orders, with an additional 190 orders waiting to be filled this week, with a total of 14,841,000 Summer Game points spent. We use the Drupal module Ubercart to run our shop. It keeps track of item inventory, allows for customer cart & checkout, and keeps track of orders. Normally, Ubercart expects payment to be in US dollars via check or credit card, but it provides a nice set of API hooks to allow us to pay with points instead of dollars. Here's the code we use to talk to Ubercart:

First thing we need to do is to tell Ubercart that we have a new payment method to allow the user to choose at checkout, using hook_payment_method. Ours has a little wrinkle in that we need to allow payment any of your players if you have multiple players attached to your website account. So we load all of your players and list them all as separate payment methods. For each player, we grab your total points to display in the payment method text.

function uc_summergame_payment_method() {
  global $user;
  $methods = array();

  foreach (summergame_player_load_all($user->uid) as $player) {
    $balance = 0;
    $tokens = 0;
    $uc_game_term = variable_get('uc_summergame_game_term', '');
    $uc_token_term = variable_get('uc_summergame_token_term', '');

    $player_points = summergame_get_player_points($player['pid']);
    foreach ($player_points as $game_term => $game_details) {
      if (strpos($game_term, $uc_game_term) !== FALSE) {
        $balance += $game_details['balance'];
      }
      else if (strpos($game_term, $uc_token_term) !== FALSE) {
        $tokens += $game_details['balance'];
      }
    }
    $playername = ($player['nickname'] ? $player['nickname'] : $player['name']);

    $methods[] = array(
      'id' => 'sgpoints' . $player['pid'],
      'name' => $playername . ' SG Points',
      'title' => "$playername's Summer Game points (current balance: $balance points" .
                 ($tokens ? "+ $tokens Tokens" : '') . ')',
      'desc' => t('Pay with points earned by playing the Summer Game.'),
      'callback' => 'uc_summergame_method_sgpoints',
      'weight' => ++$weight,
      'checkout' => TRUE,
    );
  }
  return $methods;
}

Next we need to tell Ubercart how to validate an order when it's processed. The main thing we need to check is that the player has enough points to pay for the order. The function named in the 'callback' element of the $methods array in the previous section of code will do this validation for us:

function uc_summergame_method_sgpoints($op, &$order) {
  if ($op == 'cart-process' && strpos($order->payment_method, 'sgpoints') === 0) {
    $valid = FALSE;
    $pid = str_replace('sgpoints', '', $order->payment_method);
    if ($player = summergame_player_load(array('pid' => $pid))) {
      $balance = 0;
      $tokens = 0;
      $uc_game_term = variable_get('uc_summergame_game_term', '');
      $uc_token_term = variable_get('uc_summergame_token_term', '');

      $player_points = summergame_get_player_points($player['pid']);
      foreach ($player_points as $game_term => $game_details) {
        if (strpos($game_term, $uc_game_term) !== FALSE) {
          $balance += $game_details['balance'];
        }
        else if (strpos($game_term, $uc_token_term) !== FALSE) {
          $tokens += $game_details['balance'];
        }
      }

      // Get token total (using the "weight" field of the products)
      $order->token_total = 0;
      foreach ($order->products as $product) {
        $order->token_total += $product->weight;
      }

      if ($balance < $order->order_total || $tokens < $order->token_total) {
        drupal_set_message("Your balance of $balance Summer Game points" .
                           ($order->token_total ? " and $tokens Tokens" : '') .
                           " is not enough to complete this purchase.", 'error');
      }
      else {
        $valid = TRUE;
      }
    }
    else {
      drupal_set_message('You need to sign up for the Summer Game to earn points to make this purchase', 'error');
    }

    return $valid;
  }
}

Finally, we need to hook into the various order states to do the actual points transactions, using hook_order. If the order is being submitted, we deduct points on the player's ledger. If the order is canceled or deleted, we need to add a refund to their ledger.

function uc_summergame_order($op, &$order, $arg2) {
  // Get token total (using the "weight" field of the products)
  $order->token_total = 0;
  foreach ($order->products as $product) {
    $order->token_total += $product->weight;
  }

  switch ($op) {
    case 'submit':
      // fires when the order is submitted and adds/subtracts thier points
      if (strpos($order->payment_method, 'sgpoints') === 0) {
        $pid = str_replace('sgpoints', '', $order->payment_method);
        $player = summergame_player_load(array('pid' => $pid));
        $order_link = l('Created Order #' . $order->order_id, 'user/' . $order->uid . '/order/' . $order->order_id);
        summergame_player_points($player['pid'], -$order->order_total, 'Shop Order', $order_link, "delete:no leaderboard:no");
        if ($order->token_total) {
          summergame_player_points($player['pid'], -$order->token_total, 'Shop Order', $order_link,
                                   "delete:no leaderboard:no", variable_get('uc_summergame_token_term', 'SecretShopTokens'));
        }
      }
      // send label to printer
      if ($label_email = variable_get('uc_summergame_label_email', '')) {
        uc_summergame_print_premium_label($order, $label_email);
      }
      break;
    case 'update':
      // if the order is canceled we need to refund thier points
      if ($arg2 == 'canceled') {
        if (strpos($order->payment_method, 'sgpoints') === 0) {
          $pid = str_replace('sgpoints', '', $order->payment_method);
          $player = summergame_player_load(array('pid' => $pid));
          $order_link = l('Refund for Order #' . $order->order_id, 'user/' . $order->uid . '/order/' . $order->order_id);
          summergame_player_points($player['pid'], $order->order_total, 'Shop Refund', $order_link, "delete:no leaderboard:no");
          if ($order->token_total) {
            summergame_player_points($player['pid'], $order->token_total, 'Shop Refund', $order_link,
                                     "delete:no leaderboard:no", variable_get('uc_summergame_token_term', 'SecretShopTokens'));
          }
        }
      }
      if ($arg2 == 'completed') {
        // Send a message to the player
        uc_summergame_send_pickup_notice($order);
      }
      break;
    case 'delete':
      if (strpos($order->payment_method, 'sgpoints') === 0) {
        // Refund the points
        $pid = str_replace('sgpoints', '', $order->payment_method);
        $player = summergame_player_load(array('pid' => $pid));
        $order_link = l('Refund for Order #' . $order->order_id, 'user/' . $order->uid . '/order/' . $order->order_id);
        summergame_player_points($player['pid'], $order->order_total, 'Shop Refund', $order_link, "delete:no leaderboard:no");
        if ($order->token_total) {
          summergame_player_points($player['pid'], $order->token_total, 'Shop Refund', $order_link,
                                   "delete:no leaderboard:no", variable_get('uc_summergame_token_term', 'SecretShopTokens'));
        }
      }
      break;
    case 'can_delete';
      return FALSE;
      break;
  }
}

Keep those orders coming!

Ubercart: http://www.ubercart.org
Ubercart API: http://www.ubercart.org/docs/api
Summer Game: http://play.aadl.org/

Summer Game Tech: QR Code Generator

The Summer Game is a really busy time for us at the library; many of us are involved with producing special events, writing up Game Codes & Badges, and processing and filling your orders for prizes. The Summer Game season each summer gives us an opportunity to develop improvements to the game from year to year. One of the new features for our fourth season of the Summer Game is QR Codes on the Game Code posters around the branches and displayed at events. The QR Code has a link embedded that goes directly to the code submission form with the code in the text field, ready to submit and receive points.

Finding a QR code reader for your device is usually a pretty easy affair (I use Red Laser), but getting one generated can be a hassle, especially if you're trying to integrate it with a script. After looking at some hefty looking libraries to integrate with our sign PDF generation script, I found the QR Code API from goqr.me. You can pass in your QR code parameters via a URL with GET or POST variables, and it will return a QR Code image in your desired format. It was a very simple matter at that point to embed the image in our PDF generation script, no additional libraries required.

For example:

http://api.qrserver.com/v1/create-qr-code/?data=http%3A%2F%2Fplay.aadl.org%2Fsummergame%2Fplayer%2Fgamecode%3Ftext%3DCODEMONKEYLIKECOFFEE

Generates the following image:

The service is currently provided free of charge, with no limit. They do request you contact them if you will use more than 10,000 requests per day.

Enjoy the Summer Game, and keep entering those Game Codes!

QR code API: http://goqr.me/api/
GoQR.me: http://goqr.me/
Red Laser: http://redlaser.com/
AADL Summer Game: http://play.aadl.org

Dev Tools: Modern IE

One of the most challenging aspects of web development is making sure your site works on multiple platforms. Firefox and Chrome are available for most every system where you develop, but tracking down one of the different versions of Internet Explorer to try to reproduce a bug can be an exercise in frustration. Luckily, Microsoft has generously created Virtual Machines containing versions of Internet Explorer from the current IE 11 all the way back to IE 6, and made them freely available. These can run in another piece of freely available software, VirtualBox, which will run on your development system.

modern.IE currently provides the following Virtual Machines:

  • IE 11 on Windows 8.1
  • IE 10 on Windows 8
  • IE 11 on Windows 7
  • IE 10 on Windows 7
  • IE 9 on Windows 7
  • IE 8 on Windows 7
  • IE 7 on Windows Vista
  • IE 8 on Windows XP
  • IE 6 on Windows XP

Getting all these Virtual Machines downloaded and installed properly can be difficult, but luckily there is a terminal script which will do the downloading and initial config for you. Running the bash script from the IEVMS repository on Github will automatically download all or just selected versions of the modern.IE VMs and get them installed in your VirtualBox. Just run the following command in a terminal:

curl -s https://raw.githubusercontent.com/xdissent/ievms/master/ievms.sh | bash

If you've got VirtualBox up and running you can even see your new VMs being created and booted up by the script. Look at the IEVMS page for more information and options.

IEVMS //github.com/xdissent/ievms
modern.IE Virtual Machines http://www.modern.ie/en-us/virtualization-tools
VirtualBox //www.virtualbox.org/

Dev Tools: Regex101

Analyzing text is an essential function of computing. Searching, copying and replacing text happens frequently and can be a source of frustration trying to get your code to find exactly what you want.

In PHP, if you're looking for whether a specific piece of text is included within a larger piece of text, it's quickest to use the strpos() function, but if you don't know the exact text you're looking for, sometimes the only tool for the job is to use regular expressions. Regular expressions allow you to define a pattern that will return matches without knowing the exact text beforehand. But getting that expression pattern just right can take multiple tries and can be increasingly frustrating to make a change in your code, run it, see if it worked, then go back and change it again.

The best tool I have found for designing regular expression patterns is Regex 101. It allows you to create your Regular Expression outside of your code. Paste an example of the test text you will be searching, and start typing in your expression at the top. Regex101 will update live with an explanation of any special characters in your pattern as well as highlighting the matches in your test string. You can even change "flavors" of your Regex between PCRE (Perl-style), Javascript, and Python.

Don't be a chump and write your Regexes in your code. Use Regex101 to be sure and then cut n' paste into your code.

Regex 101: http://regex101.com/
Regular Expressions: http://www.regular-expressions.info/

Developer Tools: JQuery Mobile

The JQuery Mobile framework allows you to easily create HTML elements that are optimized for mobile, touch interface devices. It allows you to give a mobile-friendly user experience in a single generic interface, accessible on iOS, Android or any other mobile device that you may want to display to the user. It supports animated transitions and recognizes swipe gestures within your web app. All you need to do is add a line of Javascript to your HTML page to include the library, and then place identifiers on your page elements to apply the framework's features to your page.

Check out demos here: http://demos.jquerymobile.com/1.4.2/
Download the library: http://jquerymobile.com/download/
Learn more about JQuery Mobile: http://jquerymobile.com/

Talking to CalDAV from PHP


Our internal room reservation system runs as multiple bookable calendar resources on our Zimbra email system. In order to reserve rooms for our website events, I have created a custom drupal module that hooks into the drupal node save process and places the event information on the correct calendar. The full module is not ready for the public (yet), but here's a sample of how we use the caldav-client PHP library from the Davical open source project.

In this PHP example, I will include the library, create an object of the CalDAVClient class, connect to a CalDAV Calendar, and get details about the calendar:

include_once('davical/inc/caldav-client-v2.php');
$cal_url = 'https://mail.example.org/dav/username/Calendar';
$cal_user = 'username';
$cal_pass = 'password';
$cdc = new CalDAVClient($cal_url, $cal_user, $cal_pass);

$details = $cdc->GetCalendarDetails();
print_r($details);

If you are able to connect to your calendar correctly, you should see something like this:

CalendarInfo Object
(
    [url] => /dav/username@example.org/Calendar/
    [displayname] => Calendar
    [getctag] => 1-98765
    [calendar-timezone] => BEGIN:VTIMEZONE
TZID:America/New_York
BEGIN:STANDARD
DTSTART:16010101T020000
TZOFFSETTO:-0500
TZOFFSETFROM:-0400
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU
TZNAME:EST
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T020000
TZOFFSETTO:-0400
TZOFFSETFROM:-0500
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU
TZNAME:EDT
END:DAYLIGHT
END:VTIMEZONE

)

The CalDAVClient library includes functions to get specific events from the calendar, place new events on the calendar, and additional functions to interact and modify your calendar from your PHP script. It's an excellent foundation for building more specific calendar interactions in your script.

DAViCal source code: http://repo.or.cz/w/davical.git
DAViCal project: http://www.davical.org/
Zimbra email server: http://www.zimbra.com/

Tool Evaluations: Code Editors

I've recently switched away from a two-computer desktop (Linux workstation with a Mac laptop on the side), to a single Mac laptop with multiple monitors. As part of the transition, I've been looking into the new possibilities for my primary code editor on OS X coming from Ubuntu. I primarily write PHP code, and am frequently working on Drupal modules. Here's a list of some of the apps I've been evaluating:

Komodo Edit (Free)

My longtime favorite, which I've been using as my primary code editor for the last 7 years.

Pros: cross platform, free, autocomplete, code hints, extendable, New Source Tree sidebar module.
Cons: Some display bugs with multiple monitors make the autocomplete and lookup popups appear on the wrong desktop, autocomplete not working

Some of the bugs I'm seeing while using Komodo Edit on my new machine may be configuration errors on my part, but the popups appearing on the wrong monitor are a known issue. I have a feeling I may end up coming back around to Komodo Edit eventually, just because it's so familiar and it does the things I need so well.

Coda ($75 in the App Store)

Coda comes from Panic software, creators of high quality OS X software. Aimed primarily at the Web Development audience, Coda looks great and makes it easy to work on a remote set of files.

Pros: native OS X interface, clear grouping of projects into "sites", custom autocomplete, extendable with plugins
Cons: price, autocomplete is local to current file only

Coda is very easy on the eyes, I just wish it was easier to get site-wide autocomplete with hints. I need to be able to call custom functions from one drupal module to another without having to open the file and copy the function name.

Sublime Text ($70)

Sublime Text is my current goto for code editing, is a strong favorite for OS X developers and has a great feature set.

Pros: Extensive and powerful keyboard shortcuts to get around your code quickly, edit multiple selections simultaneously (e.g. change all instances of a variable name to another name on the fly), large selection of packages for extention
Cons: bit of a steep learning curve for keyboard commands and package installation. Only local autocomplete

I'm still learning how to get around in Sublime Text, but I'm optimistic that I'll be able to get it to do the things that I want it to with a bit of time and experimentation with some of the custom packages. While it does cost $70 to register, there's no set limit on an evaluation copy, so go give it a try.

Komodo Edit: http://komodoide.com/komodo-edit/
Coda: http://panic.com/coda/
Sublime Text: http://www.sublimetext.com/

Partner Project: UMS Rewind

Congratulations to the University Musical Society for the recent launch of their online performance archive, UMS Rewind. We were partners on the project, providing the back end infrastructure for the data entry and editing. The project has grown over the years with many people putting in many hours of hard work. It's great to see it online and accessible to the public.

The archive contains thousands of performances, artists and works from 135 years' worth of concerts, and will be updated every season. Many of the performances link to programs and photographs in our collection of UMS materials.

The site is currently in beta, so let them know what you'd like to see.

UMS Rewind http://umsrewind.org/
UMS http://ums.org/
AADL's UMS materials http://ums.aadl.org/

Syndicate content