Creating a user-centric site in Drupal

Creating a user-centric site in Drupal


A little while ago, while talking in the #drupal mailing list, I showed my latest creation to one of the core developers there. His reaction was "Wow, I am always surprised what people use Drupal for". His surprise is somehow justified: I did create a site for a bunch of entertainers in Perth, a company set to use Drupal to take over the world with Entertainers.Biz.

Update: since writing this article, I have updated the system so that the whole booking process happens online. I will update the article accordingly!

After talking about it for a while, I decided that it would be a good idea to write a short case study about how I created the site. So, here we go.

The constraints

The site needed to be hosted in Perth. This was a crucial requirement, dictated by the fact that it was a very localised business, which needed some proximity between the IP of the potential customers and the hosting server. Hosting was therefore much more expensive that it could have been; however, the benefits in terms of search results were potentially quite big.

The site also needed to be fast, without eating too much memory. So, Drupal 6 was obviously a must, but that wasn't it. No "views", and in general no "big" modules that could potentially increase Drupal's footprint quite drastically. Basically, Drupal needed to be as bare as possible.

Another requirement was to make a site that was profile-centric. This was very important: the site itself wasn't much at all; the users' profiles, however, were "everything" in case the "user" had the role "stripper" enabled. This was made even more interesting by the need to have blogs -- and have them within the profile space (as a tab, just to be clear).

One last requirement was that the site would have two classes of users: the normal ones, and strippers. Strippers would have a much more enhanced profile, with photos, blog, and so on; common users, on the other hand, would only need to have the standard Drupal profile. Most importantly, strippers needed to be able to change their profiles' information and photos whenever they liked. (Amazingly, all of the strippers were totally up-to-date with technology; one of them didn't have a computer, but used his iPhone for everything).

Here is how I did all this.

Hosting in Perth

This one was easy. I searched the Drupal forums, and I found a post that recommended Ilisys. They are also carbon neutral! This goes to show the power of the Drupal forums: I found immensely valuable information in them.

Advanced profiles

Strippers needed much more advanced profiles. If a user was a stripper, s/he would need some extra tabs that would display extra information such as photos, a short interview, and a "stripper summary". The module that was the closest to what I needed was Content Profile. However, it only had about 40% of the features I needed it to have. Also, I had difficulty communicating effectively with the author's maintainer, Fago, while creating FriendList. Finally, the issue queue seemed a little too crowded for my own likings. I needed development to be fast. A crucial feature I needed, which was about taking over the "normal" profile page, was requested (with patch) in March 2008, but the module's author didn't think that it was appropriate to have it, and it's still pending today (November 2008).

I wrote my own module to achieve the results, and it worked fantastically well. As it turned out, writing a custom module in this situation proved to be crucial since quite a few features were rather specialised. If you see Bronson Foxx's profile you will see the extra tabs "Comments" and "Interview". There are two node types which are marked as "profile" ones. If you are the owner and have edit rights, or if you are admin, you get an "edit" link which will allow you to change the contents of the node. There is a 1:1 correspondence between a node of type "interview" owned by Bronson Foxx and the tab "Interview" for the profile Bronson Foxx. This means that there will be zero or one node for each tab -- and never more than one.

Permissions were a rather specialised feature of this module. In particular, tabs will only show up for other users if the owner has rights to edit them. Here, "edit them" means that the user needs to have the right to "edit own $type content"). This is obvious: if a user doesn't have writing access to that profile information, then there is no way for him or her to create it, and therefore show it to other people. Also, the module allows you to decide if a tab should show up if the associated node doesn't exist.

This module is not released to the general public. This is because it's too close to something that already exists in Drupal, and I have no desire to enter another "duplication" war (after the big uproar that my Friendlist module created). If somebody wants to take it on, I will be more than happy to email it to him or her. Until then, it will be a private module.

Please let me know if you are a developer interested in creating a new Drupal project, or if you are interested in merging my module's features with Fago's "Content profile".

Caged blogs

This is the most interesting feature. This was a must: blogging needed to live within the user profile's space. This means that I needed:

  • A tab in the users' profile, which contained the list of blog entries with a pager
  • The ability to display one blog entry within the user's profile

Doing this is pretty complicated, because you basically need to work against Drupal's way of doing things. For Drupal, a blog entry is a node displayed as /node/12345. To do the trick, I created a module called "caged_blog". With this hook_menu() function:

function blog_caged_menu() {

  $items['user/%user/blog'] = array(
    'title' => 'My blog',
    'page callback' => 'blog_caged_blog_page_user',
    'page arguments' => array(1),
    'access callback' => 'blog_caged_blog_access',
    'access arguments' => array(1),
    'type' => MENU_LOCAL_TASK,
  );

  $items['user/%user/blog/%node'] = array(
    'title' => 'Blog',
    'page callback' => 'blog_caged_blog_page',
    'page arguments' => array(1,3),
    'access callback' => 'blog_page_user_access',
    'access arguments' => array(1),
    'type' => MENU_LOCAL_TASK,
    'weight' => '15',
  );

  return $items;
}

The first menu item is managed by the function blog_caged_page_user():

function blog_caged_blog_page_user($account){
  global $user;

  if( ! _blog_post_exists($account) ){

    $msg_1=t("No blog posts! ");
    if($user->uid == $account->uid && user_access('create blog entries')) {
      $msg_2=t('Add a blog entry <a href="!url">Now!</a>', array('!url' => url('node/add/blog') ));
    }
    return $msg_1 . $msg_2;
  }

  require_once('modules/blog/blog.pages.inc');
  return blog_page_user($account);
}

The function is a complete cheat: it simply leaches off blog_page_user() in order to return the right contents -- but within the user's domain.

Pretty much the same thing applies to the second menu entry, which is used to display a specific blog post:

function blog_caged_blog_page($user,$node){

  // Check that it's a user's blog entry we are talking about. This is to
  // prevent this from working for ANY kind of node, which would SUCK
  // as you'd be able to pull any content into the users' page
  if($node->type != 'blog' || $node->uid != $user->uid){
    drupal_not_found();
  }

  // Set the title and return the page!
  drupal_set_title($node->title);
  return node_show($node,NULL);
}

However, this is not enough. While the module does provide a list of blog entries in the users' page, it still allows the site to have the /blog URL which will show all of the blog entries, and the /blog/12345 URL which will show the blog entries owned by the user 12345. This last one is a real problem, because it will pop up every time any module links to a blog entry -- the uncaged version will be shown.

To fix this issue, I did two things. First of all, I zapped the two URLs I mentioned above from Drupal's routing table:

function blog_caged_menu_alter(&$callback){

  // Kill existing URLs, which will never get used. IMPORTANT:
  // make absolute sure you set the right redirections for outbound links!
  $callback['blog/%user_uid_optional']['page callback']='drupal_not_found';
  $callback['blog']['page callback']='drupal_not_found';
}

This will lead to a nice non-functional Drupal site! This is where settings.php came in handy, with its custom_rewrite functions. In terms of outbound, this is what happens to the links:

function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
  [...]

  // Small hack because /blog doesn't work and it might still come up
  // in breadcrumbs
  if (preg_match('|^blog$|', $path, $matches)) {
    $path="";
  }

  // Case #1: check cases if it's node/99 -- URLs will be rewritten
  // according to their type.
  // BLOGS: /node/99 into /user/admin/blog/99 
  if (preg_match('|^node/([0-9]*)$|', $path, $matches)) {
    $n=db_fetch_object(db_query("SELECT n.nid,n.type, u.name FROM {node} n LEFT JOIN {users} u on u.uid = n.uid WHERE n.nid = %d ",$matches[1]));
    if($n){
      $type=$n->type;
      $nid=$n->nid;

      // BLOGS     
      if($type == 'blog'){
        $user=$n->name;
        $path="person/$user/blog/$nid";
      }
    }
  }

  // Case #2: turn blog/99 into users/admin/blog
  if (preg_match('|^blog/([0-9]*)$|', $path, $matches)) {
    $uid = $matches[1];
    $res=settings_lookup_user_name_by_uid($uid);
    if($res){
      $res=urlencode($res);
      $path="person/$res/blog";
    }

  }

The meaning of this is pretty straightforward: any outbound links are rerouted to blog_caged module's hooks, which will obviously work as intended.

In terms of inbound, in order to keep things "clean", I made the blog nodes unreachable when accessed through the URL node/12345.

function custom_url_rewrite_inbound(&$result, $path, $path_language=NULL) {
[...]
 // Since some nodes are special (profiles, blog posts, etc.), I don't
  // want them to be accessed at _all_
  if (preg_match('|^node/([0-9]*)$|', $path, $matches)) {
    $n=db_fetch_object(db_query("SELECT n.type FROM {node} n WHERE n.nid = %d ",$matches[1]));
    if($n){
      $type=$n->type;
      // BLOGS     
      if($type == 'blog'){
        $result="file_not_found";
      }
    }
  }
[...]

As you can see, there is some serious rewriting at work. However, it's fully worth it: as you can see, in the web site the blogs are fully caged.

Conclusion

Well, it's definitely true: Drupal is immensely versatile, and widely used for all sorts of web sites. Creating All Hot Strippers was quite challenging: I had some very strict technical constraints which took some of Drupal's power away. And yet, the site is undeniably functional and somehow unique. More importantly, it can be expanded later should it be necessary.

My involvement in the company is obviously more than just "the web designer". Let's see if you can spot me!

Category: 
Tagging: 

Comments

marita's picture
Submitted by marita on

Hi Tony - I think your profile module might work for a site I am trying to construct where a role of user is editting a list of scripts that they create. I'd like this list of scripts to be accessible in a tab in the profile similar to how you have the personal blogs on the stripper profiles. Let me know if I can take a closer look at your module.

Thanks,

Marita

alex_sa_us's picture
Submitted by alex_sa_us on

Hi Tony, this module of yours can be a great help for my current project. I am working on misic social network site based on Drupal with several roles - artists, promoters, DJs and fans.

Any chance I could take a look at your module?
Appreciate your help mate ;)

Thanks,
Alex
alex@aj-consulting.com

Author information

Tony Mobily's picture

Biography

Tony is the founder and the Editor In Chief of Free Software Magazine

Most forwarded

Interview with Dave Mohyla, of DTIDATA

Dave Mohyla is the president and founder of dtidata.com, a hard drive recovery facility based in Tampa, Florida.

TM: Where are you based? What does your company do?
DTI Data recovery is based in South Pasadena, Florida which is a suburb of Tampa. We have been here for over 10 years. We operate a bio-metrically secured class 100 clean room where we perform hard drive recovery on all types of hard disks, from laptop hard drives to multi drive RAID systems.

Anybody up to writing good directory software?

Since the very beginning, directories (of any kind) have had a very central role in the internet. (I have recently grown fond of Free Web Directory. Even Slashdot can be considered a directory: a collection of great news and invaluable user-generated comments. As far as software is concerned, doing a quick search on Google about software directories will return the free (as in freedom) software directories like Savannah, SourceForge, Freshmeat and so on, followed by shareware and freeware sites such as FileBuzz, PCWin Download Center and All Freeware (great if you're looking for shareware and freeware, but definitely less comprehensive than their free-as-in-freedom counterparts).

Interview with Mark Shuttleworth

Mark Shuttleworth is the founder of Thawte, the first Certification Authority to sell public SSL certificates. After selling Thawte to Verisign, Mark moved on to training as an astronaut in Russia and visiting space. Once he got back he founded Ubuntu, the leading GNU/Linux distribution. He agreed on releasing a quick interview to Free Software Magazine.

Is better education the key to finding better software?

I read David Jonathon's article Anybody Up To Writing Good Directory Software? the other day, which got me thinking about software directories in general. As David mentioned, many of the software directories one finds when doing a quick google search are free as in beer, not as in freedom. But what interests me is the software directories that already exist, providing a combination of both free as in beer software, and open source software. Sites such as Freeware Downloads and Shareware Download don't advertise themselves as providing free as in liberty software, but each of them have a good selection of open source software available... if you know where to look.

Most emailed

Free Open Document label templates

If you’ve ever spent hours at work doing mailings, cursed your printer for printing outside the lines on your labels, or moaned “There has got to be a better way to do this,” here’s the solution you’ve been looking for. Working smarter, not harder! Worldlabel.com, a manufacture of labels offers Open Office / Libre Office labels templates for downloading in ODF format which will save you time, effort, and (if you want) make really cool-looking labels

Creating a user-centric site in Drupal

A little while ago, while talking in the #drupal mailing list, I showed my latest creation to one of the core developers there. His reaction was "Wow, I am always surprised what people use Drupal for". His surprise is somehow justified: I did create a site for a bunch of entertainers in Perth, a company set to use Drupal to take over the world with Entertainers.Biz.

Update: since writing this article, I have updated the system so that the whole booking process happens online. I will update the article accordingly!

So, why, why do people and companies develop free software?

More and more people are discovering free software. Many people only do so after weeks, or even months, of using it. I wonder, for example, how many Firefox users actually know how free Firefox really is—many of them realise that you can get it for free, but find it hard to believe that anybody can modify it and even redistribute it legally.

When the discovery is made, the first instinct is to ask: why do they do it? Programming is hard work. Even though most (if not all) programmers are driven by their higher-than-normal IQs and their amazing passion for solving problems, it’s still hard to understand why so many of them would donate so much of their time to creating something that they can’t really show off to anybody but their colleagues or geek friends.

Sure, anybody can buy laptops, and just program. No need to get a full-on lab or spend thousands of dollars in equipment. But... is that the full story?

Fun articles

Santa Claus - the most successful open source project

It dawned on me the other day, as I was shopping for the dozens of gifts it seems I have to buy every December, that Santa Claus is the most successful open source project in history. (Bridget @ Illiterarty would agree with that). Santa Claus is essentially a marketing development that is embodied by everyone who stuffs a sock, gives a gift, hosts a dinner or wishes Merry Christmas over the holiday season.

Most emailed

Editorial

When I first started thinking about Free Software Magazine, I was feeling enthusiastic about the dream. I had Dave, Gianluca, and Alan willing to help me, I had established members of the free software community willing to help me out, I had writers volunteering their time and energy for free, and I had a generous offer from OpenHosting for servers, all before I'd proved myself. There was a sense of excitement in the air, and I thought maybe, just maybe, I could make this work.

Free Software Magazine uses Apollo project management software and CRM for its everyday activities!