Tech Blog :: Tech Blog


Aug 22 '10 11:53am

Oracle suit against Google/Android "is the equivalent of dropping a neutron bomb on the open source movement." http://bit.ly/aVm5uw

Aug 16 '10 12:30pm

How Web Design Goes Straight to Hell (design by committee) - sad truth by The Oatmeal http://bit.ly/bYkcRr

Aug 14 '10 9:08pm
Tags

DrupalCamp "Productivity Hacks" Session Video Online

The video from my DrupalCampNYC session, A Developer's Arsenal of Productivity Hacks, is now online thanks to Joel Moore of Bang! House Studios. Enjoy!

Aug 13 '10 6:30pm

Oracle suing Google for java pretty much guarantees that no new platform will be built in java. Good for developers... http://bit.ly/bjY4zA

Aug 9 '10 2:39pm

Experimenting with Skype + Google Voice for cheap global connectivity

I've been trying to figure out the best way to set up a cheap telephone system that reaches me wherever I am at the cheapest price. I've had a cellphone with Verizon Wireless for years which is my primary phone. For the last few months, I've had Google Voice forward to my cellphone, so I get transcribed voicemail, spam blocking, cheap long distance rates, all the other Voice features.

But calls to my cellphone via Voice use up expensive "minutes," so I've been playing with another piece of the puzzle: Skype. I use it often on my Mac anyway: the audio quality is excellent, I have a great USB headset (better than for my phone), it does video and group text chat, and it's encrypted. Replacing my cellphone with Skype (when I'm at a desk) could be very beneficial.

So I bought a bunch of Skype services: for $2.99/month ($8.52 for 3 months) I get a subscription that gives me unlimited Skype calls (to and from regular phones) anywhere in the US. For $12.05 (for 3 months) I get a Skype To Go number, which is a regular phone number for people to reach me on Skype. Then I add that Skype number to Google Voice, so all calls go to both my cellphone and Skype (when I'm connected). Finally I set the caller ID number on my Skype account to my Google Voice number.

Result: everyone can reach me at one number. When I'm at my desk I can pick it up with Skype and it doesn't cost any phone minutes, when I'm mobile I can use my phone. If I ever change my cellphone number it won't change the number people reach me at.

I also installed the Skype app for Android, which potentially adds direct Skype connectivity on my phone as well.

I ran into three problems worth noting:

First, the mobile app is pretty limited: it only works for domestic calls (so I can't use it when I travel abroad in a few weeks as a replacement for a roaming plan, as I was hoping), and it uses Verizon minutes for those domestic calls. It's really only good for making outbound long-distance calls; I guess Verizon doesn't like those anyway and is heavy to offload them.

Second, I ran into a bug with Skype's voicemail system. It comes as part of the subscription, and when I was offline, it would direct all calls (going also to my cellphone) to Skype voicemail (with a generic British woman's voice), not giving me a chance to pick up the call on my phone or go to Google Voice voicemail as it should. I was thinking I'd have to manually toggle the Voice-Skype redirect whenever I was at my desk, but I contacted tech support and it turned out to be a bug (or incomplete configuration panel) in the Skype program. The solution was pretty simple (for the Mac):

  1. Open ~/Library/ApplicationSupport/Skype/YourSkypeUsername/config.xml
  2. Add the line "<NoAnswerTimeout>XX</NoAnswerTimeout>" between <Call> and </Call> in the file, replacing XX with a number of seconds before directing to voicemail. (I set it to 300 seconds, which leaves plenty of time for Voice's voicemail to pick up.)

Apparently the application syncs its configuration to Skype servers when you're connected, because after making that change, then connected and disconnected once from Skype, the problem was solved.

Overall this seems like a pretty good setup, except for the lack of service abroad. I'll report here again if anything new comes up with it.

Update: Actually, the voice mail bug isn't fixed. It's now going to voice mail after 2 rings when I'm offline. I wrote to them again to see what else can be done, will update.

Aug 6 '10 2:15pm

Drupal: Impossible to set custom size for embedded video field in a view?

I have an Embedded Media youtube video field shared across multiple content types on a site, and nodequeues for the administrators to pick from these videos and feature them on various landing pages. The designs call for the videos to be different sizes on different pages - sometimes the standard 425px, sometimes 300px, sometimes widescreen 600px. When I saw the designs, I thought "I'm sure that's easy to do," and gave my approval. I'm implementing each "featured video" using a view that pulls from the respective nodequeue and displays the video as a field.

Well... I've spent hours trying to crack this nut, and my conclusion is that it's nearly impossible to do in a clean way. By clean I mean, I want to specify per view what size the video should appear. I can't set it per content type (using the backend UI) because the videos come from many content types, and if I set it by URL, there could be other videos on those pages that don't need special formatting. The settings for the field in Views only have Full and Preview, not custom sizes.

The logical approach seemed to be a hook_views_pre_render like so:
$view->field['field_video_youtube_embed']->content_field['widget']['video_width'] = 300;

That didn't work (still came out 425px), so I decided to go down an academic rabbit hole to figure out why...

The problem seems to arise with the generic content_handler_field::render() class that the field is rendered through. The object has a perfectly good $this->content_field array, which includes my modified width/height. But the function finds it necessary for some reason to get a fresh field array,
$field = content_fields($this->content_field['field_name'], $values->{$this->aliases['type']});
so the parameters are all reset. Why??

I'll just use a fields preprocessor to re-render the field. Should have done that to begin with; this study was intellectually fascinating (in a sad way) but otherwise a huge waste of time.

Aug 5 '10 4:59pm

I long for the day when we're all on global decimal time and I don't have to debug timezone calculations anymore.

Aug 5 '10 2:24am

"Consumers could also see continually rising bills for Internet service.. as for cable television." Cable 'net is already $60+/m. Jeez.

Aug 5 '10 2:21am

Google & Vz close to deal on giving paying content "web priority" - didn't Google used to support net neutrality?? http://nyti.ms/aDnOeu

Aug 4 '10 11:51pm

Moving to Git, part 1

I've been using Subversion for years and it's high time to switch to Git. So I finally got moving on that last night, starting with various non-site projects like my contributed Drupal modules.

So far:

  • Set up an account at GitHub (I've been hosting my own SVN repo and don't want to bother with that anymore; besides, GitHub offers so much more than code repositories.)
  • Install Git for Mac package.
  • Install SVN2Git.
  • On my server, backup my SVN repo with tar (in case I lose some critical files or file history in the process).

My projects are all in subdirectories of one SVN repo, but in Git they're supposed to be separate. For each project in SVN:

  • Create a repo in Github
  • Create a holder for the new repository locally: mkdir PROJECT
  • Initialize a repository in that directory: git init
  • Import from SVN: svn2git https://path/to/svn/dir --rootistrunk
  • Connect the local repo to the Github repo: git remote add origin git@github.com:USERNAME/PROJECT.git
  • Push/sync it: git push origin master

In some cases I was pulling the SVN directories via svn:externals into other sites. So as a stopgap, I switched the external URL to GitHub's read-only SVN access, and it moved over seamlessly.

SVN2Git imports the entire history of the files, including trunk/tags/branches if applicable (in this case the --rootistrunk flag indicates a flat structure), so I could then delete the files from my SVN repo.

Next steps: * Read Pro Git (free) and other Git resources I've collected. * Learn how to establish a Git-centric development/Drupal workflow. * Start moving whole sites from SVN to Git.

For anyone interested, the code for my Drupal modules and other code I'll release publicly over time will live at http://github.com/newleafdigital. (Unfortunately for now, contributed Drupal modules still have to be copied to CVS, but that's moving to Git eventually as well.)

Aug 4 '10 11:26pm

Finally moving to Git. Set up Github account, importing SVN repos with git2svn, so easy.

Aug 4 '10 8:09pm

Google officially stops supporting Wave, the most amazing tool ever that totally sucked. http://bit.ly/aYZZOG

Aug 4 '10 10:21am

Anyone have experience or suggestions for debugging random flash audio playback issues? (stops playing at random pts, files on web server)

Aug 2 '10 10:54pm

Great video about open transport data and government as a platform rather than a service provider. *Enable* people. http://bit.ly/bMNgXR

Aug 2 '10 10:36pm

Sweet! Documentary about Arduino coming soon! http://bit.ly/c1SYzJ

Aug 2 '10 10:37am
Tags

Drupal: Using CCK Fields's Inconsistent Tables in Custom Queries

Drupal has an inconsistent data structure for CCK fields: if a field is only in one content type, it's stored in a content_type_XX table as a column, but if it's shared across multiple content types, it moves to its own content_field_XX column. Views figures out where fields are located automatically, but in custom SQL queries, this can be a real pain - you can write a query that works one day, then share a field, and the query breaks.

I asked for a solution to this on IRC and was pointed to this post by drewish, Correctly accessing CCK fields in SQL queries. I adapted that method a little to create this helper function:

/**
 * function to get the TABLE or COLUMN for a CCK FIELD
 * method adapted from http://drewish.com/content/2010/06/correctly_accessing_cck_fields_in_sql_queries
 *
 * @param $field_name
 * @param $type 'table' or 'column'
 *
 * to get both table and column, run function twice with each $type
 */
function helper_cck_field_sql($field_name, $type) {
  $field = content_fields($field_name);
  $db_info = content_database_info($field);
 
  if ($type == 'table' && isset($db_info['table'])) {
    return $db_info['table'];
  }
  elseif ($type == 'column' && isset($db_info['columns']['value']['column'])) {
    return $db_info['columns']['value']['column'];
  }
  return NULL;
}
Jul 30 '10 10:31pm

Node.js: JavaScript on the Server

Here is Ryan Dahl, creator of node.js, a super-high-performance server built in C and Google's V8 Javascript engine, presenting a Google TechTalk. This kind of stuff makes me want to get a PhD in computer science.

Here's another article on Node.js, which led me to the video; here are slides of Ryan's Redfin talk; here is Node.JS.

Jul 30 '10 7:57pm
Tags

Customizing Drupal date field with hook_form_alter and #after_build

I spent a long time today trying to figure out how to customize a Date field in Drupal 6. The field is called field_recurring_dates and looked like this:

I wanted the "To" date to only show the time, no date - so the field has Date, Start Time, and End Time. The field is repeating/"unlimited" so the whole unit gets returned via AHAH with the "Add Another Item" button. Renaming that to "Add another day" was easy, but getting the date/time fields to change was elusive. I tried everything else I could think of: #process, theme overrides for every element, preprocess, with no luck. The field is generated by a hook_elements inside the Date module, and I couldn't find the equivalent of a hook_elements_alter ... until I asked on IRC and got the perfect solution from Fox: the Form API's #after_build callback.

So here's the code, finally working, inside a custom module:

function MODULE_form_alter(&$form, $form_state, $form_id) {
  // work on initial node form and AHAH-generated partial form
  if ($form['#id'] == 'node-form' || $form_id == 'content_add_more_js') {
    if (isset($form['field_recurring_dates'])) {
      $form['field_recurring_dates']['#after_build'][] = 'custom_date_combo_after_build';
    }
 
    // rename the 'Add' button
    if (isset($form['field_recurring_dates']['field_recurring_dates_add_more'])) {
      $form['field_recurring_dates']['field_recurring_dates_add_more']['#value'] = 'Add Another Day';
    }
 
  }
}
 
function custom_date_combo_after_build(&$element) {
  // can be MULTIPLE items
  // need to find all the NUMERICALLY-KEYED elements
  foreach($element as $key => &$field) {
    if (is_numeric($key) && is_array($field) && isset($field['value']) && isset($field['value2'])) {
      // remove the Date part of 'To Date's
      unset($field['value2']['date']);
 
      // rename 'From Date' => 'Date'
      $field['value']['#title'] = 'Date';
 
      $field['value']['time']['#title'] = 'From Time';
      $field['value2']['time']['#title'] = 'To Time';
 
      // remove 'To date' block title
      $field['value2']['#title'] = '';
 
      // only need helper text outside whole group
      unset($field['#fieldset_description']);   
    }
  }
  return $element;
}

With a little CSS, it now looks like this:

Perfect!

Update and Correction: The above line,
unset($field['value2']['date']);
didn't work, because the missing 2nd date field messed up the field's validation.

So I checked out the Date Single Day module mentioned in the comments, and it's pretty much identical to this method except for this change:
$field['value2']['date']['#type'] = 'hidden';   // not unset()!

That seems to work well.
As a side note, I tried the #date_format attribute mentioned in the comments and couldn't get that to work at all. Also the first and second dates aren't separated in the form array enough to set a different #date_format on each. So #after_build seems to be the best bet.

Jul 29 '10 3:57pm

sweet - via @pgangal, a new WYSIWYG UI for Google Font API http://ow.ly/2iuhh

Jul 29 '10 3:55pm

President Obama demos the new HealthCare.gov (and using a Mac) http://bit.ly/cUP5ND