Ubercart building a reputation for Drupal as good e-commerce solution

As Ubercart marches toward a beta release, I'm pleased to see more and more stores popping up in the <a href="http://www.ubercart.org/site">live sites directory</a>. I was just as happy to see a post in my Google Alerts from another development shop finding <a href="http://chasesagum.com/drupal-ubercart-simple-shopping-cart">Drupal + Ubercart to be a good solution</a> for e-commerce development. The guys at <a href="http://www.sundaysenergy.com">Sundays Energy</a> have been doing this openly for a while, and I know several other developers who have pumped out multiple incredible sites (izi are you out there? :P ). The post that just came up today, though, was different. It not only praised our beloved CMS for having a good e-commerce module package in Ubercart, but it <em>also</em> praised Drupal over against the likes of Joomla/Wordpress (and their e-commerce extensions) and other dedicated e-commerce solutions like osCommerce and ZenCart.
<!--break-->
The fellow who posted the article seems to be a pretty well-rounded developer who probably has much more experience than me in general and with other e-commerce packages. He likes the combination of Drupal + Ubercart for simplicity, flexibility, and (for lack of a better word) themability. Don't we all? My hope for Ubercart is that it continues to build a name for Drupal as <em>the</em> open source CMS solution for e-commerce... We're working hard to make it easier for folks to use and more applicable for a variety of uses. I'm happy to be working on such a fun project, and even happier knowing it's building a good reputation for Drupal in another niche market of web development. What's good for Ubercart is good for Drupal and vice versa. :)

<div style="float: left; margin-right: 1.5em;">
<script type="text/javascript">
digg_url = 'http://digg.com/programming/Drupal_Ubercart_Simple_Shopping_Cart';
</script><script src="http://digg.com/tools/diggthis.js" type="text/javascript"></script></div>I found this article through a Google Alert on "Ubercart" as a Digg. You should <a href="http://digg.com/programming/Drupal_Ubercart_Simple_Shopping_Cart">Digg it</a>, too!
<br style="clear: left;" />
On a somewhat related note, we had a new user post the following quote in our forums:
"Ubercart rocks. I was tooling with ecommerce feeling suicidal until I found this."

There you have it... Ubercart saves lives.

Adventures in Drupal 6

As soon as we get the Ubercart 1.0 release out the door, we're going to dive into converting the entire project to Drupal 6. Drupal 5 has served us well for over a year now, but many features offered by the major API changes/additions (menu, forms, schema) are just too sweet to miss out on when we start making our own Ubercart sites. I know we'll be taking a beating for at least a couple weeks updating all our modules, but ... well, no "but" - it's just going to hurt. ;)

In anticipation of the update, I've decided to dive into module development in Drupal 6 early. As my pilot project, I've started building out a budget module my wife and I can use to monitor our spending through a Drupal site. I'm hoping other people and organizations will find online budget tracking handy, and I'm betting they'll be happy to use Drupal 6 to do it. (How sweet will it be to be able to enter receipts remotely? Your budget won't get torn to shreds when you go on vacation.) After several hours reading d.o, chatting in IRC, and trying everything until my eyes hurt, I'm starting to feel comfortable with the changes.
<!--break-->
So, Drupal 5 module developer... what can you expect when you come to Drupal 6? A lot of new, and a bit of confusing. The documentation is still being fleshed out and arranged, so you should be gracious to those posting it up as you try to find that bit of info you need. Overall, it's been pretty helpful, and there are always plenty of examples in Drupal core for just about anything you'll need to do. You can expect to spend a few hours grokking the changes to the menu and forms APIs, even if you were there to see Jeff's ewoks dance in Barcelona. There's just too much going on for folks familiar with the old systems to grasp all the changes in one go. Also, you can expect to spend plenty of time learning to use the new schema API. Here again, I just opened up as many .install files as I could find in core and went to town trying and retrying my own. Just keep reminding yourself that this really is a huge improvement over the old method (or lack thereof) of creating tables!

A few things to look out for that I wasn't expecting and don't know if they're documented:
<ul>
<li>It seems truncating cache_menu no longer does the trick when fooling with your menu hooks to get them right. I've kept the modules page open and disabled/re-enabled the module while working on it. Surely someone can point me to a higher path. 8)</li>
<li>The sequences table is out and auto_increment (and other DB equivalents) are back in. The schema API lets you specify a column's type as 'serial' and takes care of the rest. I got snagged here on a default value in my schema (was copy/pasting...). As far as I know, you should not specify a default value on a serial column. (At least setting it to 0 is going to give you a nice error when it tries to install.)</li>
<li>All form builder functions are being passed an argument... the catch all <b>$form_state</b> array. Either I missed this in the update notes or it wasn't yet in the right place, but this had me baffled for a bit till I realized that the first parameter passed to your function when you use <b>drupal_get_form()</b> is always going to be <b>$form_state</b>. You should account for this if you're counting on a form receiving any arguments, like this one from my <em>budget.module</em>:
<?php
function budget_category_form($form_state, $category = NULL) {
// ...
}
?></li>
</ul>

I also hopped on IRC briefly with my fingers crossed hoping to find someone knowledgeable of the new menu API. I half-heartedly asked if anyone around could help when lo and behold, the Architect himself, chx, waved at me. I leapt for joy (inside, not literally) in my chair and got to chatting. You see, I needed some local tasks to show up only on the edit form of budget nodes, but I was having a hard time restricting them to nodes of that type. I had read in the docs about <a href="http://drupal.org/node/109153">dynamic argument replacement</a> and thought I might be able to do something with that, and chx showed me how. Joy. :D

So, the skinny is... menu item paths now accommodate some wildcard action using the handy %. In my menu items, I was able to put in %node_budget with a coordinated load function that checks if a node having an nid equal to the value of %node_budget exists and is of the budget type. If that function returns FALSE (bad nid or not a budget node), then the menu path is a 404 and won't be displayed in the local tasks area of whatever node you're editing. This may sound vague, but check the code and the screenshot. You'll know if you'll never need this information and can go ahead and forget you ever read this paragraph. For everyone else, as soon as I look at the code making this work, I'll be posting this to the <a href="http://drupal.org/node/102338">menu system docs</a> per chx's request. Thanks again, chx!

<?php
// In budget_menu:
$items['node/%node_budget/edit/budget'] = array(
'title' => t('Edit budget'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'access callback' => 'node_access',
'access arguments' => array('update', 1),
'weight' => 0,
);
$items['node/%node_budget/edit/categories'] = array(
'title' => t('Edit categories'),
'page callback' => 'budget_categories',
'access callback' => 'node_access',
'access arguments' => array('update', 1),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
'file' => 'budget.admin.inc',
);

// The function called when a path that may resolve for either of those items is loaded:
function node_budget_load($arg) {
return (is_numeric($arg) && ($node = node_load($arg)) && $node->type == 'budget') ? $node : FALSE;
}
?>

So, on a page node, those menu items won't show up in the local tasks area at all. However, on a budget node, you'll see them both and get to further setup your budget node. How cool is that!? 8) See the screenshot below for a visual. Also, unless I'm way off, <b>node_load()</b> caches the node so you needn't worry about superfluous database queries.

So... these are the start of my adventures in Drupal 6. Won't you come adventure with me?

Last Ubercart Alpha Version Reached

After a fine wrestling match in the office with CVS, tags, and project releases on d.o, I finally got the initial Alpha 8 release of Ubercart out the door. The last few alpha versions have seen marathons of development and feature additions, and this "minor" release is no different. Thanks in large part to Shawn and Lyle on the Ubercart team, we now have core functionality for file downloads and expirable role promotions (depending on a customer's purchases) and a good base of Workflow-ng integration providing administrators with a UI to create custom order workflows.

The two most requested features from folks I met at Drupalcon were related to the selling of digital goods and user account roles, and Shawn was able to use work that had already been contributed by third party developers (aymerick, CpILL, torgosPizza, and others) as a starting point for some really great module additions to the Ubercore. No time for a complete feature list here, and really no space either. Both modules are packed with excellent functionality, and we're already seeing a very positive response.

Warner Brothers Records has launched their first artist site using Ubercart selling Avenged Sevenfold fan club memberships with the new roles module and Workflow-ng support. Furthermore, the soon to be released redesign of Mike Nelson's RiffTrax will use Ubercart to sell downloads of the trax through a sharp looking custom catalog. The feedback and contributions from both these companies has been very helpful as we've implemented the new features.

So... all that combines together to make this one of the most exciting Ubercart releases to date. What's even better is this will be our last alpha version. We usually hit the minor versions pretty quick with install and update function fixes, but there will be no moving to Alpha 9. From here, we'll head to beta and beyond. Once we hit the 1.0 milestone, we'll set to work frantically updating Ubercart for Drupal 6 and finally get to a point where we can start using this for our own company's sites!

For those interested in demoing some of these new features, you can check out the recently revamped Ubercart Livetest. The site is running the latest code and demos a lot of the key features of Ubercart. (This product, for example, demos the file download system integrated with the product attribute system to sell music either as a CD or mp3 download.) Granted, I'm particularly proud of what goes on "under the hood" in the administrative menus and forms, but I'm also happy with the way the front end is working out. As elv pointed out indirectly through a post at g.d.o/usability, the user experience is very important, and we're working to make the customer user experience better with each new release.

So... feel free to check it out and post any bugs or suggestions in the appropriate forum, and while you're there, you may as well start dreaming of all the ways you can use Ubercart in your future projects. Wink

(Click here to view the news post at Ubercart.org. If this sounds like cool software to you, please vote for Ubercart at Intel's Cool Software site. We're not doing so bad in the rankings. 8))

Checking if a Drupal User has a Role

UPDATE: Over five years later, this post is still one of the most highly trafficked on my blog. Since then I've learned a lot and shared many other tips and modules. This past year I was honored to be a contributor to The Definitive Guide to Drupal 7, which includes many other great tips like this. I highly recommend it if you want to get the latest and greatest information on developing for Drupal.

I'm a little curious why a simple function to check if a user has a role doesn't exist in Drupal yet. Such curiosity that makes me wonder if I've just managed to miss it as long as I've been working with Drupal, so someone feel free to enlighten me. If anyone is looking for the same, please read on...

A fairly common task for me when adding PHP to CCK fields, Views arguments, content templates, etc. is to check whether the current user has been assigned a certain role. Generally in module development you're dealing with permissions and use user_access(), but when you're just customizing a site you often need such role based logic. (Drupal core has some, like the access settings for blocks.)

Use the following simple snippet to check if a user (in this example, it's the logged in user since I'm getting the global $user) has been assigned a certain role on your site:

<?php
 
global $user;

 
// Check to see if $user has the administrator role.
 
if (in_array('administrator', array_values($user->roles))) {
   
// Do something.
 
}
?>

Replace 'administrator' with whatever role it is you're trying to check against. Alternatively, if you keep a helper module for use on your projects, go ahead and drop this function in it:

<?php
/**
 * Check to see if a user has been assigned a certain role.
 *
 * @param $role
 *   The name of the role you're trying to find.
 * @param $user
 *   The user object for the user you're checking; defaults to the current user.
 * @return
 *   TRUE if the user object has the role, FALSE if it does not.
 */
function user_has_role($role, $user = NULL) {
  if (
$user == NULL) {
    global
$user;
  }

  if (
is_array($user->roles) && in_array($role, array_values($user->roles))) {
    return
TRUE;
  }

  return
FALSE;
}
?>

Please let me know if I put a typo up here or you know of an existing or easier way to do this!

Pages