From the earliest days of the Internet, many firms have tried to build community sites for medical professionals. Large sums of money were expended on technologies, and expectations around these feature-rich sites became very high.

So when a longstanding client, Jobson Healthcare Information (JHI) in New York, wanted to build a community website for America’s 200,000 pharmacists, we at ISL Consulting took it on as a welcome challenge. Given JHI's strong position in the market – they publish the most popular professional magazine for US pharmacists – we knew there would be no shortage of domain expertise or marketing prowess. The question was whether Drupal would permit us to build an affordable yet world-class website with everything from e-commerce to personalized pages, an elaborate friend activity notification system and other community features medical professionals have come to expect from professional sites.

Our experience with PharmQD been exceptionally positive. The larger Drupal platform enabled us to deliver a contemporary user experience on top of a complex database structure. Close to one hundred pharmacy professionals register on the site each day, countless jobs have been posted and the hoped for networks are rapidly forming. The combination of contributed modules and flexibility permitted us to build a site that might have cost millions of dollars a few years ago for a fraction of the cost without sacrificing any major pieces of functionality.

User Types

One of the most important features of the site was the need to distinguish between major user groups – pharmacists, pharmacy technicians, pharmacy students and the general user. Each has different required and optional registration fields. Unfortunately, Drupal does not come with the ability to assign roles and fields for each user group out of the box. This is usually handled after registration, automatically or manually by an administrator who assigns roles . Our goal was to automate this process more completely and collect the desired information up front.

We therefore made the initial registration screen a “dummy” screen. It collects transient information used for registration but does not register the user upon submission. Users enter their preferred Username (checked live for duplicates against the database by the User database check module) and they select their role on this dummy screen. When the next screen offers them the relevant registration fields, the Auto Assign Role module has assigned them the proper role on the site.

Nonetheless, many form alterations to the basic Content Profile module were made, like removing certain fields for certain user types, so we had to create a custom module to revise the registration process in these fundamental ways.

The regular Drupal permissions system then handles the fact, for example, that General Users can only rate the 50,000 listed pharmacies on the site, but not participate in Forums.

Continuing Education (CE)

A further complexity to the profile system comes for those professional users who opt to take Certified Education (CE) courses. JHI has large legacy database of CE test takers used by other sites. They wanted to ensure that CE credits across these sites were integrated in their PharmQD user profiles. So when users access CE, web services calls recognize users via name and email against the legacy CE database, pulling their total CE credits and courses into the PharmQD user profile.

The web services also sends back a Student ID that connects this user to their system and pulls the integrated CE test history among the sites from that point forward. Although CE course information is handled through a content type on PharmQD, the actual test delivery and scoring is done by the central JMI application. Web services calls also update profile information between sites.

Although we are interacting with a Microsoft server and database, and there were some challenges in getting everything to synchronize, the integration is seamless to the user.


Pharmacists change jobs frequently. There is a shortage of pharmacists despite the fact that the first jobs graduates get usually pay in the six figures. We use Ubercart to manage the purchase and posting of jobs, and the Node Expiry module to hide them unless renewed after 30 days.

The User Quota module helps administrators maintain the number of jobs a user is entitled to post; this comes in handy for relationships with posters handled off-line. We used the Job Posting module mainly because it had a built in system for applicants to emails their resume to the job poster.

We used Views to display the job postings. Together with taxonomy (possibly Drupal’s finest little feature), this allows users to easily sort jobs by Title, City, State and Job Type, a feature much appreciated by users in feedback. We also modified the Drupal search form to let users search by zip code.

In addition to allowing registered site members to post and manage job listings, we also created a system for automatically posting jobs from third-party XML feeds of job listings. We worked closely with a job listings provider that was supplying several hundred new pharmacy job postings to ensure that the fields of each job posting in their XML feed was properly mapped to the fields in our job posting content type. At the time of implementation, the Feed API module was chosen for this purpose (it has since been replaced by the Feeds module, a fine improvement and used successfully by us elsewhere).

With the addition of some custom code for plugging into the Feed API framework, we were able to map job postings from a custom formatted XML feed and post them as any other job listings on the site. Thanks to the separation of the parsing of feed items from the processing of feed items that is built into the Feed API module, we were able to implement our system in such a way that all of the custom code we wrote for processing feed items can be re-used to provide the same integration of another third-party feed of job listings with a different format, should the need arise.


Pharmacists enjoy making their opinions felt. Although the Blog feature is one of Drupal’s most well-known, most Drupal sites we have built have editorial blogs and do not open the function to all registered users. We were delighted therefore to use Drupal’s full blog functionality this time. Every professional user can post a blog. Early indications are that this is a feature that a group of dedicated users are keen to exploit, so that we finally can use some of the out-of-the-box Drupal blocks like Recent Blog Posts, Active Bloggers, and Recent Comments. The one module we did add was Bloginfo to allow bloggers to give their blogs titles and a description; it seems that this should be part of the core blog module.

Profiles and Privacy

One of the most critical parts of the site is the MyPharm section, which brings together a dozen content types relevant to each user personally. This includes their Profile and Account information, Forum postings and Comments on these with links to each, a list of Friends (see below), Bookmarked pages, Comments on other content, Blog entries, Job Postings, CE credits, Stories posted, and User Points. Nearly all these sections were constructed using Views, filtered for the user. The basic information contained in each of these sections is also rolled up in a user’s personal box that stays with them in the top left hand corner of nearly every page.

A major issue was privacy, and clarifying to users what information they entered into the site was public and what was genuinely private. To accomplish this, on the Profile page we put a Public View button next to the edit button so each user could see exactly what other users would see about them.

The underlying functionality is handled by the CCK Field Privacy module which allows users to set the privacy levels of each relevant field, neatly depicted with an orange padlock. Site members can determine on a field-per-field basis if they want that information to remain private, to be viewable to only their friends, or to be public.

Social Network Features: Friends, Notifications, and News Stream

Our client, inspired by Facebook, wanted to add several social network features that would allow registered members to contact other members, connect them to their profile as “friends”, and stay informed about the activities and actions that their friends took on the site To accomplish these goals we added a new module that worked best with our needs and we added some custom code to create a notification system (both on the site and through sent emails) that would make sure site members are kept up-to-date about the activities related to the user and his/her friends.

Establishing Friends

We looked at both the User Relationships (UR) module and the FriendList module, and chose the former because it seemed slightly more advanced and configurable with its multiple component modules, plus it seemed to have a greater rate of development and download activity, though both modules are being used widely. In addition, we wanted to ensure that all the privacy settings would be integrated with this new functionality. The UR module seemed more advanced in this area and would maintain the level of user privacy in all areas throughout the site.

The UR module allowed for a high degree of flexibility through the configuration settings. Users can choose how to accept friendship offers and uses nice Ajax pop-ups during the Friend-ing phase. Administrators can easily tailor the emails and onscreen messages that go out for every situation. The ability to use the UI module to create new types of relationships in the future was also attractive.

Notifications and News Stream

In order to accomplish the goal of notifying site members when their friends take certain actions on the site, we implemented two new features--one to send update emails (Email Notifications) to the member’s inbox and the other was to create a real-time News Stream (very similar to Facebook’s NewsWall) that would appear on the member’s MyPharm personal homepage.

For Email Notifications, users can configure the types of site activities for which they will receive emails (seven options exist) and can opt for daily digest emails of notifications in place of instant email notifications. For the personal homepage News Stream, users view the latest activities of their friends (e.g., new postings, comments, changes to viewable profile fields, new group memberships, photo album uploads, etc) as well as notifications relating to a user's own content (e.g., new comments posted to any of their posted content). The system was implemented in such a way that it will be easier in the future to implement notifications for new types of site activity that may require email notifications and new items in the personal home page news stream.

Modules and Development

Over 50 Modules were used on this site. Besides those discussed above they include the Administration module (which we can't live without) and CCK (over a dozen sub-modules), Watchlist, JQuery menu, Content Permissions, Flag, Image cache, Input Filters, Location (7 sub-modules), Account Reminder, AddThis, Advanced Help, Assigned Role, Block Theme, Bloginfo, Block Cache Alter (for performance), Contact Forms, Custom Pagers, FAQ, Global Redirect, HTTP Request Fails Reset, Menu Trails, Modr8, Node Expire, Override Node Publishing Options, Page Title, Pagination, Pathauto, Secure Pages, Tell a friend, Token, User Prune, User Quota, Printer, Email and PDF versions, Rules, Site Tweaks (this Read More link tweak is so basic, it should be in core), Spam Control, Taxonomy Image, Ubercart (12 sub-modules), User Interface, User Relationships, User Points, Views, Voting (Fivestar and Vote Up/Down) and Seven-up (saved us the enormous trouble/impossible task of making the site work perfectly on Explorer 6).

The abundance of configurable modules and Views has led to a slow but steady change in how sites are developed at ISL. For the first time, Project Managers were able to undertake significant work with Views to configure the site and what it displays, often adjusting things on the fly during conversations with clients. Having the basic content types and most block and page Views set-up prior to programmer involvement helped us to streamline our process, significantly decreased overall development time, and allowed us to adapt functionality more interactively with clients. On a site like this, many details only become apparent after launch and many users working with the applications. Drupal has to some extent relieved the post-launch stress of changes by making many of them so much easier to make than before. Our continuing goal is to put ever more site development tasks into the hands of a wider group of employees, enabling programmers and themers to concentrate on delivering the more custom functionality that clients need.

ISL Consulting

PharmQD was ISL’s seventh major Drupal project (an earlier case study covered a multi-site ecommerce installation.) The project took 6 months to build and was managed by Amy Brotman with help from Amy Shoemaker. The site was designed by Joe Kraynik with Catharine Oshiro. Theming was done by Jeff Turner. Programming was led by Bob Hinrichs and Ankur Rishi with major database tasks handled by Judy Chun.


scottatdrake’s picture

Wow, great writeup and a great site! Thanks!

I'd like to hear about performance issues. What sort of caching are you doing?

Omar Khan’s picture

There are several things we do to improve performance. We use a PHP optimizer, APC. We are using normal Drupal caching. Block caching is turned on. Because, infuriatingly, any modules using node_access disable block caching and ruin your site's performance, we had to make a small change to the block.module, thereby causing one kitten to be killed, but it was all in service to the site. We also use the 'block cache alter' module. This is a very useful module that gives us control over how blocks will cache along several parameters. Finally, for any troublesome views, we sometimes set the cache on an individual basis.

wallbay1’s picture


joemoraca’s picture

thanks for sharing

SoCalCreations’s picture

I showed this site to my staff at our place of work (Alvarado Compounding Pharmacy) and everyone loved the idea of it.

I signed our store up and I am hoping in the future, that they will do a Pharmacy HI-LITE for those of us on the web.

It encourages me even more to get done faster our new website on the Drupal Platform, which I am about 50% done with and hope to launch by next month.

GREAT WORK ISL Consulting!

_gramur’s picture

Love the implementation of the site. Having 200,000 pharmacists access the site may cause a lot of stress on the server my only question is how does it hold up performance wise with large traffic and traffic spikes?

dheeraj.dagliya’s picture

Thanks @islcon for putting up this writeup for the community.

I noticed following in your resources section - find & rate pharmacies - search by Zipcode, which you may want to look at again.
The results displayed seems to have link mentioned as Aren't you using pathauto for it?
Also there is duplication in result display, in link you have address line with title as link and same address line is mentioned just after the link.
Browser Page title too seems to be missing on this page.

But the site is really cool and I like the simplicity of layout.

Kuldip Gohil’s picture

Thanks for sharing, Good Social Network Features.

WildBill’s picture

It would be great if this write-up contained links to the actual modules, to encourage their adoption. Shouldn't be that hard to do...

ajayg’s picture

Great writeup.

Which profile module you use? Drupal core profile or content profile (user as a node)?

jaochoo’s picture

I am interested in the user profiles, too. Could elaborate a little bit more detailled how you built them and brought all the different tabs into the user profile?

Omar Khan’s picture

Content profile, we almost always end up using this on sites.

jaochoo’s picture

Thanks for the info. How did you manage to bring custom tabs into the user profile?

bob.hinrichs’s picture

The tabs in the "my" page are menu items. They are defined in a menu hook in the custom module (as MENU_NORMAL_ITEM), then the theme takes over and renders the menu as tabs. The important thing to note is that these menu items are in the Secondary Links menu, which is loaded into its own variable for theming. Under the 'mypharm' path, it is rendered as the tabs. Otherwise it is rendered as a side menu for the user's convenience. Since the path is important for the rendering of menus and also the context of the page itself, some menu items were devised that are 'wrappers' around functionality that normally has its own pages. For example, the page callback for the tab to edit your profile merely returns the function content_profile_page_edit('profile', $user);. In some cases, we acquire more raw information from the function, so that the wrapper technique allows us to manipulate or augment output to fit the "my" page section of the site.

yarrait’s picture

Very clean layout !! Looks cool and great write up !!
Thanks for sharing this stuff. It would be a great help for me for future developments


manuel.solis’s picture

How did you do it?
I've been looking for something exactly like that!

adrianmak’s picture

If it is possible, it would be great to release some of your custom code like the reformed registration process.
Say, the registration 'dummy' screen , a custom create web form using form api ?
how about the create profile form ? a custom or contributed module ?

I think this is a common user case for such registration process.

bob.hinrichs’s picture

Hmm, I think perhaps this is more a case for a "recipe", as the custom code is very specific to the use. The site uses content_profile extensively, and autoassignrole. Then form_alter is used for functionality the modules can't control. $form_state['storage'] stores the step of the process. If the step is '1', then it is a "dummy" form; this form is the full registration form, but the validation and submit values for the form array have been changed so that it only validates the values shown. Fields and field groups are removed that are not part of the basic information on this first step, providing a simple experience in which the user can select the username (we use username_check) and email address. The form is otherwise the registration form and drupal validates it as such. Autoassignrole module is used to render a role selection. When submitted, it shows the registration form again, but the $form_state['storage']['step'] is set to '2', running a different condition in form_alter, rendering a more complete form, and keeping the values submitted in step 1 in storage. Meanwhile, the anonymous user's role is set to the selection made in step 1. Through cck field permissions, different roles will see different fields in the form for the content_profile node. This of course requires that all roles have a similar profile and this can be accomplished by showing/hiding fields. Submitting step '2' is the true registration submission. Once submitted, the same cck field permissions apply whenever the user edits the profile node. This scheme allows the admin to have a lot of control over the roles, the fields that appear and to whom they appear, without a lot of custom coding.

cmgui’s picture

Very nice site - clean design -- but i hate to say this -- it is slow.

but u r still faster than another showcased drupal site:

but much slower than this drupal site:

Dan Silver’s picture

Thanks for sharring! I am interested in how you handled the user regestration. Very creative approach!