drupal

The sequel to my Ubercart i18n adventures

It has been a while since I last wrote about Ubercart, but I'm still working on some multi-lingual stores for clients. I have opted for disabling the stock Catalog module and using Views instead since Views is so much more flexible and easier to theme. I have a very simple setup; some terms in a vocabulary that is localized per-term, and then a custom View that takes a term name as an argument and returns nodes belonging to that term and displays them in a nicely themed grid.

Recently, I ran into an irritating issue where the View would return results from the wrong language if two languages had the same term name. After hours of investigating (and learning all about how to implement View handlers and plugins), it seems that the stock taxonomy term argument validator for Views cannot differentiate between terms of the same name in different languages. So if multiple languages contain the term "Stewart Adam" for example, the view will just returns nodes for whichever term (and therefore language) comes first in the database query. To be fair, the i18n module adds the "language" column to the term_data table so it's not really View's fault... Nonetheless, I was surprised that the i18n module had not already corrected this issue.

I've just reported Drupal issue #832100, Taxonomy term argument validator should not validate terms defined in other languages that includes a fix to the problem by limiting query for term names to terms within the active language. It's not the greatest way to go about solving it since it essentially just copies the original validator and makes two tiny modifications in the SQL query, but it's better then modifying the View module directly.

Rating: 

v1 blog offline

My "v1" Joomla 1.0.15 blog is almost a year old now and support for 1.x has been phased out, so for security reasons I think it's a good idea to finally take it offline. I've been able to import the old content here and I've also setup URL remaps, so you shouldn't notice a thing. Although if you are having trouble with a specific URL, please leave a comment and I'll fix the hole in the regex remapping rules.

Rating: 

Some new features

My trial enabling node comments went pretty well, I did have to handle the inevitable comment spam but there was nothing outrageous - only about one bogus comment a day. With comments being published only after approval they're extremely easy to spot as well, since typically the spammer will post the same comment twice or more once they realize "hey, it hasn't appeared on the page yet!"

So because of comments are functioning well I've just enabled some new features for my blog - namely, you can now leave your name & homepage when you leave a comment, although due to homepage spamming I've disabled all homepage links in the site theme until I find a better solution. As well, you'll notice the new rating widget (thanks to VotingAPI and Fivestar) on every node - feel free to leave any feedback or ratings on any of the blog or tutorial posts. Thanks!

Rating: 

Installing PHP 5.2.10 on OS X 10.6 Snow Leopard

Since my update to Snow Leopard, I was pleasantly surprised to find that Apple has updated PHP to version 5.3 and also included the GD extension. While I no longer have to rebuild the extension manually like on Leopard, these changes to PHP brought around a different problem: Drupal is currently not compatible with PHP 5.3 (#360605).

I've been trying to get my local Drupal installations working, and although the patch from post #84 works pretty well (when applied to a D6 CVS checkout), Ubercart is still nonfunctional. Since I am currently building and testing Ubercart-enabled sites, my only remaining option was to downgrade to PHP 5.2.10. I wanted to have the same extensions and options that Apple's PHP 5.3 build had, so I started by viewing the output of phpinfo() and copying the configure command. To compile PHP, locally installed copies of libpng, libjpeg and pcre are required so let's started with that:

  1. (Like in the Leopard tutorial, I assume you have installed the Xcode & related developer utilities and that all downloads are saved in the "Downloads" folder in your home). Visit the libpng, libjpeg and PCRE homepages and download the latest release available for both. As of writing, the most recent releases are libjpeg 7, libpng 1.2.39 and PCRE 7.9.
  2. Compile libpng and libjpeg statically:
    cd ~/Downloads && tar xfz libpng-1.2.39.tar.gz
    cd libpng-1.2.39
    ./configure --disable-shared --enable-static
    make && make install DESTDIR=`pwd`/localinstall

    cd ~/Downloads && tar xfz jpegsrc.v7.tar.gz
    cd jpeg-7
    ./configure --disable-shared --enable-static
    make && make install DESTDIR=`pwd`/localinstall

    cd ~/Downloads && tar xfj pcre-7.9.tar.bz2
    cd pcre-7.9
    ./configure --disable-shared --enable-static
    make && make install DESTDIR=`pwd`/localinstall
  3. Since PHP will be built with MySQL support, download and install MySQL x86_64 for OS X. As of writing, the latest version is 5.1.38.
  4. Download PHP 5.2.10, available here
  5. Next, PHP needs to be prepared for compilation. As detailed in PHP bug #49267, a small change is required to get PHP to compile on Snow Leopard:
    1. Type in the terminal:
      cd ~/Downloads && tar xfj php-5.2.10.tar.bz2
      cd php-5.2.10
      nano ext/iconv/iconv.c
    2. Skip down to line 185 (Tip: <ctrl+c> shows current line)
    3. Remove the lib on #define iconv libiconv so that the code reads like this:
      #ifdef HAVE_LIBICONV
      #define iconv iconv
      #endif
    4. Hit <ctrl+o> and to save the file
    5. Hit <ctrl+x> to quit nano
  6. Now, PHP is ready for compilation. We will use a configure command relatively similar to the command extracted from phpinfo() earlier:
    ./configure '--prefix=/usr' '--mandir=/usr/share/man' '--infodir=/usr/share/info' '--disable-dependency-tracking' '--sysconfdir=/private/etc' '--with-apxs2=/usr/sbin/apxs' '--enable-cli' '--with-config-file-path=/etc' '--with-libxml-dir=/usr' '--with-openssl=/usr' '--with-kerberos=/usr' '--with-zlib=/usr' '--enable-bcmath' '--with-bz2=/usr' '--enable-calendar' '--with-curl=/usr' '--enable-exif' '--enable-ftp' '--with-gd' '--with-jpeg-dir=/Users/shortname/Downloads/jpeg-7/localinstall/usr/local' '--with-png-dir=/Users/shortname/Downloads/libpng-1.2.39/localinstall/usr/local' '--enable-gd-native-ttf' '--with-ldap=/usr' '--with-ldap-sasl=/usr' '--enable-mbstring' '--enable-mbregex' '--with-mysql=/usr/local/mysql/' '--with-mysqli=/usr/local/mysql/bin/mysql_config' '--with-mysql-sock=/tmp/mysql.sock' '--with-iodbc=/usr' '--enable-shmop' '--with-snmp=/usr' '--enable-soap' '--enable-sockets' '--enable-sysvmsg' '--enable-sysvsem' '--enable-sysvshm' '--with-xmlrpc' '--with-xsl=/usr' '--with-pcre-regex=/Users/shortname/Downloads/pcre-7.9/localinstall/usr/local'

    EXTRA_CFLAGS="-lresolv" make -j2
    Remember to replace shortname in /Users/shortname to your system account's shortname. If you're not sure what that is, type whoami in a terminal to find out.
  7. Finally, backup Snow Leopard's PHP extension so that PHP 5.3 can be restored later, and copy the PHP 5.2.10 extension in its place:
    sudo mv /usr/libexec/apache2/libphp5.so /usr/libexec/apache2/libphp5.so.orig106
    sudo cp libs/libphp5.so /usr/libexec/apache2/libphp5.so
  8. The final step is to restart Apache - this can be done by toggling Web Sharing in System Preferences, or alternatively via the apachectl command:
    sudo apachectl restart
  9. That's all! Now run phpinfo() and verify that PHP 5.2.10 is up & running. While I was trying to get this working, I stumbled accross two compile errors - for the sake of completeness, I've listed them below along with the failure cause:

    1. This error occurs if EXTRA_CFLAGS="-lresolv" is not used while compiling PHP:
      Undefined symbols:
        "_res_9_dn_expand", referenced from:
            _zif_dns_get_mx in dns.o
        "_res_9_search", referenced from:
            _zif_dns_get_mx in dns.o
            _zif_dns_check_record in dns.o
        "_res_9_dn_skipname", referenced from:
            _zif_dns_get_mx in dns.o
            _zif_dns_get_mx in dns.o
      ld: symbol(s) not found
      symbols:
        "_res_9_dn_expand", referenced from:
            _zif_dns_get_mx in dns.o
        "_res_9_search", referenced from:
            _zif_dns_get_mx in dns.o
            _zif_dns_check_record in dns.o
        "_res_9_dn_skipname"collect2: , referenced from:
            ld returned 1 exit status_zif_dns_get_mx
      in dns.o
            _zif_dns_get_mx in dns.o
      ld: symbol(s) not found
      collect2: ld returned 1 exit status
    2. This error occurs if the #define iconv libiconv is not changed to #define iconv iconv
      Undefined symbols:
        "_libiconv", referenced from:
            __php_iconv_strlen in iconv.o
            _php_iconv_string in iconv.o
            _php_iconv_string in iconv.o
            __php_iconv_strpos in iconv.o
            __php_iconv_appendl in iconv.o
            __php_iconv_appendl in iconv.o
            _zif_iconv_substr in iconv.o
            _zif_iconv_mime_encode in iconv.o
            _zif_iconv_mime_encode in iconv.o
            _zif_iconv_mime_encode in iconv.o
            _zif_iconv_mime_encode in iconv.o
            _zif_iconv_mime_encode in iconv.o
            _zif_iconv_mime_encode in iconv.o
            _php_iconv_stream_filter_append_bucket in iconv.o
            _php_iconv_stream_filter_append_bucket in iconv.o
      ld: symbol(s) not found
      collect2: ld returned 1 exit status
Rating: 

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 create 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: 

New blog online!

Apart from having to design a new theme, my blog version 2 is up & running! Hope you enjoy it.

I'll be porting over the content from my previous setup into this one, but in the mean time you can access the old setup here: www.firewing1.com/v1

Update: The old content has been migrated, so the Joomla install at www.firewing1.com/v1 is now offline.

Rating: 

New website online!

As I talked about a few posts ago, I've been trying out Drupal and designing a new section of the Diffingo website with it that is devoted to its open-source software. I polished it a bit and it's now online at diffingo.com/oss. Now that I have a developer blog there, I'll be posting info relevent to fwbackups and my other software there... But the rest will still be here.

Rating: 

Qt, Drupal, iTunes, DRM and a bunch more

I saw this story on slashdot today and wanted to share it - this is exactly why I will never buy anything DRM-protected. If you missed the other article a while back, this is the second music store to shut down in the past few months and leave its users in the dark. I say "in the dark" because both Yahoo! and MSN took down their key servers along with the service, so any user who needs to authorize a new computer to play the music they legally purchased is unable to do so - forcing them to rebuy the songs they've already paid from for somewhere else. This is the perfect example of why I think DRM is a bad, bad technology. Another reason was something I read in a comment on slashdot (sorry, don't have the link this time): When you think about it, the users who pay are getting the short end of the stick. If you pay and recieve DRM content, you're getting a service that's worse than what you get from pirating that same content! For example, music. People who illegally download music are getting high-quality (320kbps) MP3 files without any content restrictions for free, while the users who pay to download music will typically get a 128kbps AAC or MP3 file, with DRM restrictions... Something has to change. Soon.

If you agree with me, try to opt for DRM-free music or simpy don't purchase DRM-encumbered music. While a CD will cost you a few more dollars, it has a much higher sound quality and it's not restricted in any way. Hopefully if the music industry sees the common trend of users moving towards DRM-free music, we can get rid of DRM once and for all. I've started purchasing from the iTunes store now that iTunes Plus is getting more popular, and I must admit I'm liking it. I can choose to purchase only select songs from an album, the prices are very reasonable ($0.99/song) and the sound quality is great - 256kbps AAC! What's best of all is that I can play the songs anywhere without the need for authorization - including on Linux or on other non-iPod media players (like Creative MuVos for example) since I can convert the song to any format: ogg, flac, mp3, etc.

Anyways, now that that rant is out of my system, I'll tell you what I've been up to recently. I've been creating a new website for the open-source section of Diffingo.com with the Drupal CMS. I still like Joomla since there's lots of support and community extensions available, but Drupal was recommended by a few friends so I figured I'd give it a shot. And I'm happy I did.

The first thing I noticed was that it was lightning-fast - at least twice or three times faster than Joomla. Not only that, but to my surprise it was much easier to use. I found the interface much cleaner and easier to look at... Everything faded nicely and there seems to be some pretty advanced Javascript stuff built right-in to the administator interface. The WYSIWYG "TinyMCE" editor packaged with Joomla is available for Drupal, so I felt right at home. Drupal also does away with the whole sections and category thing which is really nice. Instead, content items are created as "nodes" and you can cross-categorize nodes if you'd like to.

Blogging is also supported out-of-the-box, which was a big plus since I call this a "blog" but really it's just Joomla content items displayed on a homepage (this is going to be moved to Drupal soon, too). I found themeing Drupal to be a bit more difficult than theming Joomla, however that was made up by Drupal's wonderful access controls. Drupal supports creating user roles, and each role hasa set of permissions. This effetively lets my control who gets to what, when, where and how. I created three simple groups to start with (co-administrators, registered users and anonymous users), but there's a lot of potential in there that I'm hoping I can tap into later.

So, as I blogged about last time I'm also working on a new version of fwbackups. I've changed almost everything I could behind the scenes - from autotools to CMake, from GTK to Qt and from Python to C++. I plan on keeping the interface as similar to as it was before, but it's going to be much simpler and easier to use. It's also going to be much faster under the hood - I'm going to have more info about the next version soon, as soon as I get the new drupal site up ;)

One problem I had when making all these changes was how to set things up properly - it took me a few days, but right now the interface in Qt is done and the very little of the program that's been completed is C++ builds on Mac OS X (10.3.9 and later), Windows (MinGW required) and Linux of course. If you're looking for a way to do this with one of your projects, I'm going to blog about getting the structure for all that setup soon, so check back in a week or two!

Rating: