Blogs

Boot Camp 3.0 + Windows XP Service Pack 3 = installation error

Seeing as I completely reformatted my MacBook Pro's hard disk, I also had to reinstall Windows via Boot Camp 3.0 today. Everything went well, however when the time came to install Service Pack 3 (my copy of XP is an SP2 OEM disc), I received an odd error I had never seen before:

An error occured while copying file osloader.ntd.  Cannot copy file to destination directory.  Click Retry to retry the operation or click Cancel.

A Google revealed that this error is caused by an Apple's new HFS+ drivers for Windows, as detailed here. Simply following the instructions and renaming the driver fixes the problem. After installing SP3, I restored the HFS+ driver to it's original state and all is well.

Rating: 

OS X 10.6 (Snow Leopard)

I just installed Snow Leopard and I must say that I am very impressed. Apple has once again done an excellent job and rounded off the rough edges on Leopard, which was already a pleasure to use.

Although Apple recommends doing an upgrade install over a clean install, I opted for a clean install because I've modified various parts of my OS X Leopard installation over time (installed Python 2.6 over 2.5, installed KDE for OS X, a few MySQL installations/upgrades, etc) and I wanted to try a fresh start of OS X. Formatting the hard disk took and the installation was about half an hour from start to finish, and I was able to use my new system as soon as the Snow Leopard installer rebooted the machine for me.

One of the first things I noticed was that everything was much more snappy compared to Leopard. Everything I do seems more responsive while using Snow Leopard; all of the small lags or delays (while restoring a minimized window, for example) are gone. On the other hand, more intensive tasks like opening a large application or restarting the machine are noticeably faster. Phoronix released an article today comparing the performance of Snow Leopard (OS X 10.6) vs Leopard (OS X 10.5), and at first glance it seems like OpenGL performance has dropped a bit, but otherwise there are enhancements all around.

I was a bit worried as I was upgrading because I had read this document describing that certain (popular) software titles were incompatible and would not open on Snow Leopard. As it turns out, I had no problems at all with any of my software. All of the software I was using with OS X 10.5 works perfectly with OS X 10.6, including Thunderbird 3.0b3, MacFUSE+NTFS-3G, Starcraft and OpenOffice.org. It should be noted that for Starcraft and other PPC-based applications to work, you need to install Rosetta from the Optional Installs of the Snow Leopard installation DVD.

So if you're reading this on a Mac with OS X 10.4 (Tiger) or 10.5 (Leopard), I recommend that you grab a copy of Snow Leopard the next time you are nearby an Apple store or reseller. Snow Leopard is being sold at $35 for a single license or $55 for the family pack (5 licenses for use in one household) and the feature and performance enhancements it brings are well worth it.

For what it's worth, one of the changes I particularly like is how Snow Leopard reports disk size, which as documented here, has changed. All tools in Snow Leopard now report disk size using base 10 measurements, meaning a 200GB hard drive appears in your system as 200GB and not 186.26GB. I'm glad that somebody has finally made a move, because users - myself included - honestly don't care if a GB is 1024 or 1000 MB. That is irrelevant. What does matter is that the measurement doesn't change in one context and another. Otherwise, it isn't much of a standard measurement, right? It would be ridiculous to propose that the same unit "kilometre" represented a different distance depending on if you walked or drove from point A to B, but essentially that is what has happened in the computer industry. Depending on if you are shopping for or writing information to a storage medium, its size changes with the same unit. It's pretty odd to explain that when someone asks "where has the remaining 300MB gone on my 4GB USB key? I only see 3.7GB."

Rating: 

Back to school

It's that time of year.... *sigh*

Rating: 
Tags: 

Translating or internationalizing an Ubercart store: Common problems & solutions

Although Drupal 6 handles node translation with the i18n module very well, Ubercart seems to have a bit of difficulty with it. I believe that most of the problems are due to the nature of translated nodes, which are actually completely different nodes that are glued together with some metadata. This fact, for example, is the reason Ubercart will a product translation as a new product separate from the original.

As I tried to setup a multi-lingual store (English/French) store with Ubercart, I encountered many problems that seemed to be all over Google, but had no resolution posted or the solutions to the problems were fragmented across different sites and support threads. So after a lot of searching, reading PHP backtraces and hacking the Ubercart core modules, I was finally able to have a good Ubercart catalog set up in two languages! I've compiled a list of problems and their solutions below if you are also having problems. Feel free to use any part or all of the code samples on your site.

Note: The code in this tutorial has been confirmed to work with Ubercart 2.2. It should also work with future versions, but may require some minor tweaks.

Problem: "add to cart" and other form buttons need to be translated

Ubercart handles form button texts like "add to cart" via multi-lingual variables. Multi-lingual variables differ from strings that use Drupal's t() function in that they will not appear in the site's Translate Interface utility. Instead, multi-lingual store a value for each of the site's languages and load the appropriate string depending on which language the user is currently browsing in.

In the case of "add to cart", this simply means that one must browse to Administer > Store Administration > Configuration > Product Settings and click on the Edit button. Switch the site language to the desired language and enter new values in the "Add to cart button text" configuration box. Once the values have been saved, you will be able to switch to a different language and enter new values for that language.

Problem: Ubercart does not synchronize product data across translated nodes (product information needs to be updated for each translation)

As I mentioned in the introduction, this is due to the fact that each translation is in fact a node to itself. Since Drupal assigns each node with content type "product" the standard Ubercart attribute set, each product node must be updated individually. Fortunately, there is a quick and easy was to solve this by creating a custom module:

  1. Enable the i18nsync ("Synchronize Translations") module in Administer > Site building > Modules
  2. Create a custom module which will hook into i18nsync:
    1. Create the folder sites/all/modules/custommod
    2. Paste the following into sites/all/modules/custommod/custommod.info:
      name = Custom module
      description = A custom module which helps with the translation of an Ubercart store
      core = 6.x
    3. Paste into sites/all/modules/custommod/custommod.module:
      <?php
      /**
        * Implementation of hook_i18nsync_fields_alter().
        */
      function custommod_i18nsync_fields_alter($fields, $type) {
        if(
      in_array($type, uc_product_types())) {
         
      $fields['uc_products']['#title'] = 'Products';
         
      // These values were found by doing a print_r($node) in node-product.tpl.php
         
      $fields['uc_products']['#options'] = array (
           
      'model' => 'SKU',
           
      'list_price' => 'List Price',
           
      'cost' => 'Cost',
           
      'sell_price' => 'Sell Price',
           
      'weight' => 'Weight',
           
      'weight_units' => 'Weight Units',
           
      'dim_length' => 'Length',
           
      'dim_width' => 'Width',
           
      'dim_height' => 'Height',
           
      'length_units' => 'Length Units',
           
      'pkg_qty' => 'Quantity',
           
      'default_qty' => 'Default quantity to add to cart'
         
      );
        }
      }
      ?>

    (Thanks to these two commenters for posting code updates)

  3. Go to Administer > Site building > Modules and enable "Custom module"
  4. Go to Administer > Content management > Content types and edit the "Product" content type
    1. Under Workflow Settings > Synchronize translations, select all fields you would like to synchronize. However, do not select the Taxonomy terms option (see below for why)
    2. Repeat this step for any other product classes/content types whose product data you want synchronized

That's all! If you edit a node in one language, you should see the updated product data in all other languages as well. The only caveat is that product attributes and product options will still need to be added and updated on each translation.

Thanks to user gupa on Ubercart support forums for posting this fix (see reference 1)

Problem: Taxonomy terms are untranslated in Catalog block

This one had me puzzled for a long time. At first, I had enabled localized terms for the Catalog vocabulary. After I created some terms, I translated them using the Translate Interface tool as usual, but this did not work. Editing a product would show the localized term in the Catalog list, however viewing the product always resulted in the untranslated term being displayed on the Catalog block. The solution is to navigate to Administer > Content management > Taxonomy and edit the Catalog vocabulary, choosing Per language terms. Use the following procedure to define taxonomy term translations:

  1. Browse to Administer > Content management > Taxonomy
  2. Add a new term with its translations:
    1. Add a new term to the Catalog vocabulary in the default language of your store and choose that language from the drop-down menu
    2. Repeat the above step, but enter the desired translated texts for the term (including a translated term name)
    3. Repeat the above step, but enter the desired translated texts for the term (including a translated term name)
  3. Click on the Translation tab of the taxonomy interface
  4. Click Create new translation and pair up the original term with its translations
  5. Repeat the step above for each term that you created earlier.

This is why you do not want to synchronize taxonomy terms, with this setup each term is translated the same way nodes are - separate terms per node, linked together with some metadata. Synchronizing taxonomy terms would result in your products appearing in the right categories but in the wrong languages!

Problem: Adding items to the cart in one language, then switching to another language does not localize the cart contents

This is an unusual situation, but it's a very annoying problem; adding an item to the cart, switching languages and then click on that same item in the cart will open the product in first language, not the one the customer is browsing your website in. To solve this, hook_cart_item() will be used to override the default cart rendering with our own version which will replace node with translated nodes. Open the custom module file, sites/all/modules/custommod/custommod.module, which was created earlier and add before the closing ?> tag:

<?php
/**
  * Implementation of hook_add_to_cart().
  */
function custommod_add_to_cart($nid, $qty, $data) {
 
/* Due to Drupal's use of multiple nodes for product translations, the same
   * product in a different language is treated as a different product entirely.
   * This is problematic as the same product in different languages can be added
   * to the cart simultaneously. This function works around that problem by
   * always using the tnid/original node. As a result, the cart must be
   * localized as it is displayed.
   */
 
$node = node_load($nid);
 
// Determine if this node is the source node or a translated one
  // Remember: tnid is 0 if there are no translations
 
$is_source = ($node->nid == $node->tnid || $node->tnid == 0) ? 1 : 0;
  if (
$is_source) {
   
// If it is the source, then all is well…
   
$result[] =  array('success' => TRUE);
  } else {
   
/* If we are not the source node, then fail to add this product silently and
     * call uc_cart_add_item() to add the source node's product instead. It will
     * be localized later - see custommod_cart_item()
     */
   
uc_cart_add_item($node->tnid, $qty, $data);
   
$result[] = array('success' => FALSE, 'silent' => TRUE);
  }
 
// Remember: We need an array in an array here
 
return $result;
}

/**
  * Implementation of hook_cart_item().
  */
function custommod_cart_item($op, &$item) {
 
/* hook_cart_display() isn't really a hook, it's mostly for internal use.
   * However, we do need to access later. Setting $item->module forces
   * a module_invoke() call in uc_cart.module to call custommod_cart_display()
   * instead of the default uc_product_cart_display(). We will call
   * uc_product_cart_display() inside our function to ensure things work as
   * usual in future versions.
   */
 
$item->module = "custommod";
 
/* Note that although it is possible to use check for case 'load' in $op and
   * then override the $item->nid and $item->title values, this will cause bugs
   * when attempting to add or remove products in different languages. To
   * resolve these bugs, we are forcing the use of custommod_cart_display() and
   * rewriting the code for the title, img, and anchors to localize the cart.
   */
}
/**
  * Implementation of hook_cart_display().
  */
function custommod_cart_display($item) {
 
/* Call uc_product_cart_display() to get things setup as usual and to ensure
   * this hack still works even if uc_product_cart_display changes at some point
   * in the future.
   */
 
$display_item = uc_product_cart_display($item);
 
// Get the translations, if any.
 
$node = node_load($item->nid);
  global
$language;
 
$translations = translation_node_get_translations($node->tnid);
  if (
$translations[$language->language]) {
   
// Reminder: NEVER override the nid. That is what causes the bugs!
   
$tnode = node_load($translations[$language->language]->nid);
   
$display_item["title"]["#value"] = node_access('view', $tnode) ? l($tnode->title, 'node/'. $tnode->nid) : check_plain($tnode->title);
   
$display_item["image"]["#value"] = uc_product_get_picture($tnode->nid, 'cart');
  }
  return
$display_item;
}
?>

After reloading the Custom module at Administer > Site building > Modules, the cart should behave properly when switching languages. As well, adding a product to the cart in one language, switching languages, then adding it again should not result in two different products being added to the cart. Instead, the quantity of the product will increase by 1.

Credit for this solution goes to user Docc at Ubercart forums, who posted the code sample (see reference 3)

Problem: Ubercart does not localize products during checkout

This one was a bit trickier to solve, since there's no real elegant way to trick ubercart into localizing the products while using the standard checkout pane. As a result, we will have to disable the stock checkout pane and use the replacement provided by the code below instead (the replacement pane is called "Your order").

<?php
/**
  * Implementation of hook_checkout_pane().
  */
function custommod_checkout_pane() {
 
/* Replacement for standard cart contents pane. Although hook_cart_item() can
   * be used to localize the checkout pane, then we get into trouble while
   * trying to localize the cart display (see the comments above). The best way
   * that I can think of to work around this is to disable the stock cart
   * contents pane and enable this one instead.
   */
 
$panes[] = array(
   
'id' => 'custommod_cart',
   
'callback' => 'custommod_checkout_pane_custommod_cart',
   
'title' => t('Your order'),
   
'desc' => t('Display the (localized) contents of a customer\'s shopping cart.'),
   
'weight' => 2,
   
'process' => TRUE,
   
'collapsible' => FALSE,
  );
  return
$panes;
}

/**
  * Callback for our implementation of hook_checkout_pane()
  */
function custommod_checkout_pane_custommod_cart($op, &$arg1, $arg2) {
 
/* The code below is copied from uc_checkout_pane_cart() and is slightly
   * modified to localize the cart contents before displaying it. If you are
   * using this, you need to keep an eye on uc_checkout_pane_cart() to make sure
   * that if there is an important change or bugfix, you make the same change
   * here.
   */
 
switch ($op) {
    case
'view':
     
$contents['cart_review_table'] = array(
       
'#value' => theme('cart_review_table'),
       
'#weight' => variable_get('uc_pane_cart_field_cart_weight', 2),
      );
      return array(
'contents' => $contents, 'next-button' => FALSE);
    case
'review':
     
$items = uc_cart_get_contents();
     
$output = '<table>';
     
$context = array(
       
'revision' => 'themed',
       
'type' => 'cart_item',
       
'subject' => array(),
      );
      global
$language;
      foreach (
$items as $item) {
       
$node = node_load($item->nid);
       
$translations = translation_node_get_translations($node->tnid);
        if (
$translations[$language->language]) {
         
$tnode = node_load($translations[$language->language]->nid);
        } else {
         
$tnode = $node;
        }
       
$desc = check_plain($tnode->title) . uc_product_get_description($item);
       
$price_info = array(
         
'price' => $item->price,
         
'qty' => $item->qty,
        );
       
$context['subject'] = array(
         
'cart' => $items,
         
'cart_item' => $item,
         
'node' => $tnode,
        );
       
$output .= '<tr valign="top"><td>'. $item->qty .'&times;</td><td width="100%">'. $desc
                 
.'</td><td nowrap="nowrap">'. uc_price($price_info, $context) .'</td></tr>';
      }
     
$output .= '</table>';
     
$review[] = $output;
      return
$review;
  }
  return
$result;
}
?>

Thanks to user totsubo at Ubercart forums for helping me test this part of the module!

See also

References:

  1. Information about localized taxonomy in thread Multiple language Ubercart website on the Ubercart support forums
  2. Lots of good information at a thread titled i18n issues i D6/UC2 for Multilingual sites on the Ubercart support forums
  3. Method for product synchronization was based on a small code sample by Docc at Drupal node #456358
Rating: 

Adding support for the GD graphic library on OS X 10.5 Leopard

I play around with a number of Drupal locally and today I finally caved in and decided to add GD support to OS X's webserver. Although I've heard great things about the Entropy PHP packages, I did not want to replace OS X's integrated php installation so my remaining option was to grab a copy of PHP 5.2.8 (the version bundled with Leopard) and compile the module myself. I know there must be quite a few other people who are looking do to the same thing, so I here's a step-by-step guide on how I did it:

(Please note that I assume you have Xcode installed, and that all downloads are saved in the "Downloads" folder in your home. If this is not the case, please move the downloads there first before executing any of the listed commands)

  1. Visit the libpng and libjpeg homepages and download the latest release available for both. As of writing, the most recent releases are libjpeg 7 and libpng 1.2.37 - the commands below will need to be adjusted slightly if you are compiling a more recent version.
  2. Download PHP 5.2.8, available here
  3. Extract PHP and prepare to compile libpng and libjpeg statically:
    cd ~/Downloads
    tar xfj php-5.2.8.tar.bz2
    cd php-5.2.8/ext/gd
    mv ~/Downloads/libpng-1.2.37.tar.gz ~/Downloads/jpegsrc.v7.tar.gz .
  4. Compile libpng without shared libraries:
    tar xfz libpng-1.2.37.tar.gz && cd libpng-1.2.37
    ./configure --disable-shared --enable-static
    make && make install DESTDIR=`pwd`/localinstall
    cd ../
  5. Compile libjpeg without shared libraries:
    tar xfz jpegsrc.v7.tar.gz && cd jpeg-7
    cp /usr/share/libtool/config.* .
    ./configure --disable-shared --enable-static
    make && make install DESTDIR=`pwd`/localinstall
    cd ../
  6. Compile the GD extension and point to our newly compiled libpng and libjpeg static libraries:
    cd ~/Downloads/php-5.2.8/ext/gd
    phpize
    MACOSX_DEPLOYMENT_TARGET=10.5 \
    CFLAGS="-O3 -fno-common -arch i686" \
    LDFLAGS="-O3 -arch i686" \
    CXXFLAGS="-O3 -fno-common -arch i686" \
    ./configure --with-zlib-dir=/usr \
    --with-png-dir=`pwd`/libpng-1.2.37/localinstall/usr/local \
    --with-jpeg-dir=`pwd`/jpeg-7/localinstall/usr/local \
    --with-freetype-dir=/usr/X11R6 \
    --with-xpm-dir=/usr/X11R6
    make
    sudo make install
  7. Copy /private/etc/php.ini.default to /private/etc/php.ini
    • Change the default 'extension_dir' (./) to /usr/lib/php/extensions/no-debug-non-zts-20060613/
    • Add extension=gd.so a bit below extension_dir to enable the gd extension
  8. sudo nano /System/Library/LaunchDaemons/org.apache.httpd.plist
    Change the part between <array> and </array> so that it looks like this:
    <array>
    <string>/usr/bin/arch</string>
    <string>-i386</string>
            <string>/usr/sbin/httpd</string>
            <string>-D</string>
            <string>FOREGROUND</string>
    </array>

    This will ensure that Apache starts in 32 bit mode, which is needed as the gd extension was compiled for i686.
  9. That's all! After a reboot (or toggle Web sharing in the System Preferences), everything should be working. phpinfo() should now confirm that GD library is indeed loaded. If it isn't or if apache refuses to start, try opening Console to check /var/log/apache2/error_log for some useful pointers as to what's wrong.

    Helpful source I used for writing this tutorial: HOWTO: Install Habari on Mac OS X Leopard, From Scratch

Rating: 

Comments

I've always been hesitant to enable comments on my posts due to spambot activity, but I'm hoping Drupal is a bit better than Joomla at fending spammers off... I'm going to enable (moderated) comments for a week and see how it goes. Feel free to leave a comment on any of my articles!

Rating: 

Getting MobileMe to work with any email address

A friend of mine recently bought a MobileMe subscription and I was pretty curious about what it would offer as I've seen the demos from Apple's WWDC keynote. After experimenting with it for a bit while I was setting it up on my friend's home computers, I found that it is actually a very worthwhile purchase for for both home and small business users. The one downside to it was that Apple does not allow the use a non- @me.com email address. Luckily, after about an hour of playing I was able to setup a system which enabled Thunderbird and other email clients to use the MobileMe email features with non- @me.com email address.

The problem is Apple's outgoing mail (SMTP) servers, which reset the From: header in all messages. No matter how you configure your client, all outgoing mail shows From: myuser@me.com. By avoiding the Apple's SMTP servers, you can bypass this problem and send mail as if it was sent from your previous address (for example yourname@yourdomain.com). Here are the steps required to set it up:

  1. Forward all incoming mail to your MobileMe address

    CPanel and many other hosting control panels will support forwarding all mail from one address to another. In CPanel, click on "Mail Forwarders" and forward all mail from your previous address to youruser@mobileme.com. If your hosting provides a different control panel and you can't find the mail forwarders option, try contacting your hosting's technical support team for more information.

  2. Configure your email client (incoming mail)

    In order to receive new messages, you will need to create a new IMAP account. Email clients such as Outlook, Outlook Express, Thunderbird, Entourage and Apple Mail all support IMAP. Apple has a step-by-step guide for setting up each of the programs listed above. If you are using another program, you'll need to enter the following settings manually:

    • Account type: IMAP
    • Incoming server: mail.me.com
    • Incoming username: Your MobileMe account name (the part before "@me.com")
  3. Configure your email client (outgoing mail)

    Instead of entering the outgoing (SMTP) details as listed on the Apple tutorial, enter the information for your hosting that you previously used instead. For example, your outgoing server may be mail.yourdomain.com or smtp.yourdomain.com.

  4. (Optional) Create identities

    If you are forwarding many custom email addresses to your MobileMe account, you may want to take the time to use the Identity feature of your mail client so that you can reply mail as if writing form any of the many custom emails instead of just one (different clients may offer the same feature but under a different name). In Thunderbird, you can manage your Identities by selecting Tools > Account Settings... > Manage Identities

The only trouble you may encounter with this method is that sending mail from the MobileMe Web interface will still show @me.com, as the webmail passes through Apple's SMTP servers.

Note: This procedure works for iPods too! If you configure your iPod's incoming and outgoing mail as shown above, you should also be able to send mail from your custom address as well.

Rating: 

Fedora 11 is out!

You can grab it here. I will post some updated howtos soon.

Rating: 

iTunes 8.2.0

Apple Software Update just popped up with an update for QuickTime and iTunes... The changelog indicates that it has added support for iPhones OS 3.0. I am installing it now, back soon with more info.

Edit: Okay, so apart from bug fixes nothing much seems to have changed for now - iPhones OS 3.0 isn't available immediately.

Rating: 

Troubleshooting the MySQL Server 5.1 service on Windows Vista

Although some users have reported success, most installations I've tried of MySQL 5.1 on Vista have failed, even on fresh Vista installs. The first problem appears at the end of the service instance configuration. All appears to be well, however the server refuses to start with Could not start MySQL service or Could not start the service MySQL. Error: 0.

The trick is to start MySQL from the console so that you are able to see the error message (you can access the command console by typing cmd into the Run dialog):

cd "C:\Program Files\MySQL\MySQL Server 5.1\bin"
mysqld -nt --defaults-file="C:\Program Files\MySQL\MySQL Server 5.1\my.ini" --standalone --console

In my case, MySQL always returned the same error message:

Plugin 'InnoDB' init function returned error.
Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
Unknown/unsupported table type: INNODB
Aborting
Forcing shutdown of 1 plugins

This message is a symptom of the log file size problem (just google InnoDB: Error: log file .\ib_logfile0 is of different size for more information). All you need to do is to clear the following files from the folder C:\ProgramData\MySQL\MySQL Server 5.1\data:

ib_logfile0
ib_logfile1
ibdata1
$YOUR_HOSTNAME$.err

Restart the MySQL server and all should be well. Note that the C:\ProgramData\ folder is hidden, so unless you have enabled hidden folders from the Folder Options dialog, you will need to copy/paste that folder path directly into the address bar in order to access the folder.

Rating: