Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Nov 16 2015
Nov 16

We’ve recently begun moving to amazon web services for hosting, however we still need to authenticate through ITS who handles the central SSO Authentication services for Virginia.edu.  In previous posts we looked at Pubcookie aka Netbadge - however Pubcookie is getting pretty long in the tooth (it’s last release back in 2010) and we are running Ubuntu 14 with Apache 2…. integrating pubcookie was going to be a PITA…. so it was time to look at Shibboleth – an Internet2  SSO standard that works with SAML  and is markedly more modern than pubcookie – allowing federated logins between institutions etc…

A special thanks to Steve Losen who put up with way more banal questions than anyone should have to deal with… that said, he’s the man :)

Anyhow – ITS does a fine job at documenting the basics - http://its.virginia.edu/netbadge/unixdevelopers.html.  Since we’re using ubuntu the only real difference is that we used apt-get

Here’s the entire install from base Ubuntu 14

apt-get install apache2 mysql-server php5 php-pear php5-mysql php5-ldap libapache2-mod-shib2 shibboleth-sp2-schemas drush sendmail ntp

Apache Set up

On the Apache2 side  we enabled some modules and the default ssl site

a2enmod ldap rewrite  shib2 ssl
a2ensite default-ssl.conf

Back on the apache2 side here’s our default SSL Screen Shot 2015-11-16 at 10.18.33 AM

<IfModule mod_ssl.c>
<VirtualHost _default_:443>
ServerAdmin [email protected]
ServerName bioconnector.virginia.edu:443
DocumentRoot /some_web_directory/bioconnector.virginia.edu
<Directory /some_web_directory/dev.bioconnector.virginia.edu>
AllowOverride All

SSLEngine on

SSLCertificateFile /somewheresafe/biocon_hsl.crt
SSLCertificateKeyFile /somewheresafe/biocon_hsl.key

<Location />
AuthType shibboleth
ShibRequestSetting requireSession 0 ##This part meant that creating a session is possible, not required
require shibboleth

the location attributes are important – if you don’t have that either in the Apache conf you’ll need it in an .htaccess in the drupal directory space

Shibboleth Config

The Shibboleth side confused me for a hot minute.

we used  shib-keygen as noted in the documentation to create keys for shibboleth and ultimately the relevant part of our /etc/shibboleth/shibboleth2.xml looked like this

<ApplicationDefaults entityID=”https://www.bioconnector.virginia.edu/shibboleth
REMOTE_USER=”eppn uid persistent-id targeted-id”>

<Sessions lifetime=”28800″ timeout=”3600″ relayState=”ss:mem”
checkAddress=”false” handlerSSL=”true” cookieProps=”https”>
<!–we went with SSL Required – so change handlerSSL to true and cookieProps to https

<SSO entityID=”urn:mace:incommon:virginia.edu”>
<!–this is the production value, we started out with the testing config – ITS provides this in their documentation–>

<MetadataProvider type=”XML” file=”UVAmetadata.xml” />
<!–Once things are working you should be able to find this at https://www.your-virginia-website.edu/Shibboleth/Metadata – it’s a file you download from ITS = RTFM –>
<AttributeExtractor type=”XML” validate=”true” reloadChanges=”false” path=”attribute-map.xml”/>
<!–attribute-map.xml is the only other file you’re going to need to touch–>

<CredentialResolver type=”File” key=”sp-key.pem” certificate=”sp-cert.pem”/>
<!–these are the keys generated with shib-keygen –>
<Handler type=”Session” Location=”/Session” showAttributeValues=”true”/>
<!–During debug we used https://www.bioconnector.virginia.edu/Shibboleth.sso/Session with the  showAttributeValues=”true” setting on to see what was coming across from the UVa  Shibboleth IdP–>

/etc/shibboleth/attribute-map.xml looked like this

<Attribute name=”urn:mace:dir:attribute-def:eduPersonPrincipalName” id=”eppn”>
<AttributeDecoder xsi:type=”ScopedAttributeDecoder”/>

<Attribute name=”urn:mace:dir:attribute-def:eduPersonScopedAffiliation” id=”affiliation”>
<AttributeDecoder xsi:type=”ScopedAttributeDecoder” caseSensitive=”false”/>
<Attribute name=”urn:oid:″ id=”affiliation”>
<AttributeDecoder xsi:type=”ScopedAttributeDecoder” caseSensitive=”false”/>

<Attribute name=”urn:mace:dir:attribute-def:eduPersonAffiliation” id=”unscoped-affiliation”>
<AttributeDecoder xsi:type=”StringAttributeDecoder” caseSensitive=”false”/>
<Attribute name=”urn:oid:″ id=”unscoped-affiliation”>
<AttributeDecoder xsi:type=”StringAttributeDecoder” caseSensitive=”false”/>

<Attribute name=”urn:mace:dir:attribute-def:eduPersonEntitlement” id=”entitlement”/>
<Attribute name=”urn:oid:″ id=”entitlement”/>

<Attribute name=”urn:mace:dir:attribute-def:eduPersonTargetedID” id=”targeted-id”>
<AttributeDecoder xsi:type=”ScopedAttributeDecoder”/>

<Attribute name=”urn:oid:″ id=”persistent-id”>
<AttributeDecoder xsi:type=”NameIDAttributeDecoder” formatter=”$NameQualifier!$SPNameQualifier!$Name” defaultQualifiers=”true”/>

<!– Fourth, the SAML 2.0 NameID Format: –>
<Attribute name=”urn:oasis:names:tc:SAML:2.0:nameid-format:persistent” id=”persistent-id”>
<AttributeDecoder xsi:type=”NameIDAttributeDecoder” formatter=”$NameQualifier!$SPNameQualifier!$Name” defaultQualifiers=”true”/>
<Attribute name=”urn:oid:″ id=”eduPersonPrincipalName”/>
<Attribute name=”urn:oid:0.9.2342.19200300.100.1.1″ id=”uid”/>

Those two pieces marked in red are important – they’re going to be the bits that we pipe in to Drupal

For  debugging we used the following URL https://www.bioconnector.virginia.edu/Shibboleth.sso/Session to see what was coming across – once it was all good we got a response that looks like

Session Expiration (barring inactivity): 479 minute(s)
Client Address:
SSO Protocol: urn:oasis:names:tc:SAML:2.0:protocol
Identity Provider: urn:mace:incommon:virginia.edu
Authentication Time: 2015-11-16T15:35:39.118Z
Authentication Context Class: urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
Authentication Context Decl: (none)

affiliation: [email protected];[email protected];[email protected]
eduPersonPrincipalName: [email protected]
uid: adp6j
unscoped-affiliation: member;staff;employee

The uid and eduPersonPrincipalName variables being the pieces we needed to get Drupal to set up a session for us

Lastly the Drupal bit

The Drupal side of this is pretty straight

We installed Drupal as usual  and grabbed the shib_auth module.

Screen Shot 2015-11-16 at 10.46.10 AM Screen Shot 2015-11-16 at 10.47.51 AM

and on the Advanced Tab

Screen Shot 2015-11-16 at 10.48.58 AM Screen Shot 2015-11-16 at 10.50.07 AM
May 19 2015
May 19

Here’s a tangent:

Let’s say you need to randomly generate a series of practice exam questions. You have a bunch of homework assignments, lab questions and midterms, all of which are numbered in a standard way so that you can sample from them.

Here’s a simple R script to run those samples and generate a practice exam that consists of references to the assignments and their original numbers.

## exam prep script

## build hw data
j <- 1

hw <- data.frame(hw_set = NA, problm = seq(1:17))

for (i in seq(1:12)) {
        hw[j,1] <- paste0("hw",j)
        j <- j+1


hw <- expand(hw)

names(hw) <- c("problm_set", "problm")

## build exam data

j <- 1

exam <- data.frame(exam_num = NA, problm = seq(1:22))

for (i in seq(1:8)) {
        exam[j,1] <- paste0("exam",j)
        j <- j+1


exam <- expand(exam)

names(exam) <- c("problm_set", "problm")

## create practice exam

prctce <- rbind(exam,hw)

prctce_test <- prctce[sample(1:nrow(prctce), size=22),]

row.names(prctce_test) <- 1:nrow(prctce_test)


As the last line indicates, the final step of the script is to output a prctce_test … that will be randomly generated each time the script is run, but may include duplicates over time.

output from r script

Sure. Fine. Whatever.

Probably a way to do this with Drupal … or with Excel … or with a pencil and paper … why use R?

Two reasons: 1) using R to learn R and 2) scripting this simulation let’s you automate things a little bit easier.

In particular, you can use something like BASH to execute the script n number of times.

for n in {1..10}; do Rscript examprep.R > "YOUR_PATH_HERE/practice${n}.txt"; done

That will give you 10 practice test txt files that are all named with a tokenized number, with just one command. And of course that could be written into a shell script that’s automated or processed on a scheduler.

automatically generated practice tests with bash and r script

Sure. Fine. Whatever.

OK. While this is indeed a fairly underwhelming example, the potential here is kind of interesting. Our next step is to investigate using Drupal Rules to initiate a BASH script that in turn executes an algorithm written in R. The plan is to also use Drupal as the UI for entering the data to be processed in the R script.

Will document that here if/when that project comes together.

Apr 07 2015
Apr 07

We don’t have a lot of feedback about how our patrons are using the current equipment booking system. There may be information that users could share with one another (and the library) if given a mechanism to do so. So as part of the new booking system implementation in Drupal, we set a task of including a commenting feature. Each reservable piece of equipment stands alone as a node so all we have to do is turn on commenting, right?


But there are a couple of things that are worth noting about that.

If you’re enabling comments on a content type, it’s probably a good idea to consider who can view (and post comments to) that content. That’s all in the permissions table.

In our scenario, we didn’t want unauthenticated comments and we didn’t want to restrict the individual equipment pages (e.g. the page for iPad copy 2) to any kind of login. The request to reserve equipment from that page would trigger the login.

The snippet from the permissions table below shows how we adjusted the comment access. Note that these will be permissions that will apply anywhere else on we’re using comments on our site … we’re not currently, but if we do in the future we’re fine with this access level.

permissions table for comment settings toggled on for authenticated users

Once authenticated, the comment form defaults to give users a text format selection option. There are advantages to users selecting a WYSIWYG format This too can be handled in the text format configurations or even the permissions table. An easier way is with the Simplify module.

Simplify gives you an interface to hide a bunch of stuff that may be noisy to users adding content — publishing options, path settings, etc.

And for comments it lets you hide text formats.

The finished product:

comment box without text format
Mar 30 2015
Mar 30

The first requirement of a registration system is to have something to reserve.

The second requirement of a registration system is to manage conflicting reservations.

Setting up validation of submitted reservations based on the existing reservation nodes was probably the most complex part of this project. A booking module like MERCI has this functionality baked in — but again, that was too heavy for us so we had to do it on our own. We started off on a fairly thankless path of Views/Rules integration. Basically we were building a view of existing reservations, contextually filtering that view by content id (the item that someone was trying to reserve) and then setting a rule that would delete the content and redirect the user to a “oops that’s not available” page. We ran into issues building the view contextually with rules (for some reason the rule wouldn’t pass the nid …) and even if we would have got that wired up, it would have been clunky.

Scrap that.

On to Field Validation.

The Field Validation module offers client-side form validation (not to be confused with Clientside Validation or Webform Validation based on any number of conditions at the field level. We were trying to validate the submitted reservation on length (no longer than 5 days) and availability (no reservations of the same item during any of the days requested).

The length turned out to be pretty straightforward — we set the “Date range2″ field validator on the reservation date field. The validator lets you choose “global” date format, which means you can input logic like “+ X days” so long as it can be converted by the strtotime() function.

field validation supports global date values, which means anything that the strtotime() PHP function can convert

Field Validation also gives you configurations to bypass the validation criteria by role — this was helpful in our case given that there are special circumstances when “approved” reservations can be made for longer than 5 days. And if something doesn’t validate, you can plug in a custom error message in the validator configuration.

bypass length restriction so staff can accommodate special cases for longer reservations, and field validation also lets you write a custom error message

With the condition set for the length of the reservation, we could tackle the real beast. Determining reservation conflicts required us to use the “powerfull [sic] but dangerous” PHP validator from Field Validation. Squirting custom code into our Drupal instance is something we try to avoid as much as possible — it’s difficult to maintain … and as you’ll see below it can be difficult to understand. To be honest, a big part of the impetus for writing this series of blog posts was to document the 60+ lines of code that we strung together to get our booking system to recognize conflicts.

The script starts by identifying information about the item that the patron is trying to reserve  (item = $arg1, checkout date = $arg2, return date = $arg3) and then builds an array of dates from the start to finish of the requested reservation. Then we use EntityFieldQuery() to find all of the reservations that have dates less than or equal to the end date request. That’s where we use the fieldCondition() with <= to the $arg3_for_fc value. What that gives us is all of the reservations on that item that could possibly conflict. Then we sort by descending and trim the top value out of the list to get the nearest reservation to the date requested. With that record in hand, we can build another array of start and end dates and use array_intersetct() to see if there is any overlap.

I bet that was fun to read.

I’ll leave you with the code and comments:

//find arguments from nid and dates for the requested reservation
$arg1 = $this->entity->field_equipmentt_item[und][0][target_id];

$arg2 = $this->entity->field_reservation_date[und][0][value];
$arg2 = new DateTime($arg2);

$arg3 = $this->entity->field_reservation_date[und][0][value2];
$arg3 = new DateTime($arg3);
$arg3_for_fc = $arg3->format("Ymd");

//build out array of argument dates for comparison with existing reservation
$beginning = $arg2;
$ending = $arg3;
$ending = $ending->modify( '+1 day' );

$argumentinterval = new DateInterval('P1D');
$argumentdaterange = new DatePeriod($beginning, $argumentinterval ,$ending);

$arraydates = array();
foreach($argumentdaterange as $argumentdates){
 $arraydates []= $argumentdates->format("Ymd");  

//execute entityfieldquery to find the most recent reservation that could conflict

$query = new EntityFieldQuery();

$fullquery = $query->entityCondition('entity_type', 'node')
  ->entityCondition('bundle', 'reservation')
  ->propertyCondition('status', NODE_PUBLISHED)
  ->fieldCondition('field_equipmentt_item', 'target_id', $arg1, '=')
  ->fieldCondition('field_reservation_date', 'value', $arg3_for_fc, '<=')
  ->fieldOrderBy('field_reservation_date', 'value', 'desc')

$fetchrecords = $fullquery->execute();

if (isset($fetchrecords['node'])) {
  $reservation_nids = array_keys($fetchrecords['node']);
  $reservations = entity_load('node', $reservation_nids);

//find std object for the nearest reservation from the top
$reservations_test = array_slice($reservations, 0, 1);

//parse and record values for dates
$startdate = $reservations_test[0]->field_reservation_date[und][0][value];
$enddate = $reservations_test[0]->field_reservation_date[und][0][value2];

//iterate through to create interval date array
$begin = new DateTime($startdate);
$end = new DateTime($enddate);
$end = $end->modify( '+1 day' );

$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);

$arraydates2 = array();
foreach($daterange as $date){
 $arraydates2 []= $date->format("Ymd");  

$conflicts = array_intersect($arraydates, $arraydates2);

if($conflicts != NULL){
Mar 23 2015
Mar 23

For our equipment booking system we needed two kinds of content types: reservation and equipment. Patrons would create nodes of the reservation content type. Staff would create nodes of the equipment content type(s). Because our library circulates a lot of different equipment we need a bunch of content types — one for each: Apple TV, Laptop, iPad Air, Digital Video Camera, Projector, etc. The equiment content types all need the same field structure — title, description, image, accessories and equipment category. It’s tedious creating these individually, but once you get one fully fleshed out (and the skeletons of the rest in place) then the Field Sync module will finish the job with the push of a button.

field sync screenshot

The reservation content type would be canonical for each kind of equipment. In other words, we don’t have to create a Reservation_iPad and a Reservation_Laptop and a Reservation_Projector, etc. There’s just one: Reservation. The way we accomplish this is by using an entity reference field that looks for any nodes of equipment content types.

entity reference configurations for reservation content type

When a patron creates a node of the reservation content type, he/she will select the equipment to be reserved in the entity reference field. This entity reference structure allows us to offer a pretty helpful feature for patrons navigating from a specific equipment page to the reservation form. An Entity Reference Prepopulate and Display Suite combination gives us a “Reserve This Item” link on equipment pages (say Apple TV – Copy 1) that sends the patron to a node/add reservation page that has that piece of equipment (in this case Apple TV – Copy 1) selected in the equipment field.

entity reference prepopulate with reservation link

There’s good documentation out there for Entity Reference Prepopulate — check out this video. But it might be worth explaining how we built the URL that prepoluates the entity reference field. With Display Suite you can create a custom code field — we hardcode a URL to the node/add reservation form, with a token for the id of the currently viewed entity appended onto it. When the user clicks the link, the Entity Reference Prepopulate module kicks off, sees the id and builds the form with that entity referenced.

Mar 11 2015
Mar 11

This is the first in what’s going to be a series of posts documenting our equipment booking system project. We’re developers working at a library that circulates equipment (laptops, tablets, cameras, etc.) — and we’re sick of maintaining the custom PHP application that manages the reservation process. So we built the whole thing into our existing Drupal site. I say “built” because it’s done … or at least sitting on the production server waiting for content to be entered. We’re doing the documentation after the fact, so I’ll try to pick and choose what’s worth putting out there. I’m guessing that will boil down to plugging a few modules and spending way too much time writing about the PHP script we used to check for reservation conflicts. We’ll see.

The beginning of the project was deciding whether or not we wanted to use a module to manage the reservation process. Actually the beginning was MERCI— we got a little turned around on this one … picked the module and pitched it before we had everything specd out. Once we dug in, MERCI turned out to be a reasonable module but just a little heavier than what we needed. In particular, the “bucket and “resource” model was too much and it was kind of a pain to manage without being able to get into the field configurations. We also tested out Commerce Stock for its inventory tools. Way heavier than MERCI.  To use Commerce Stock we would have to install Commerce and everything that comes with it. Rather than ripping things out that we weren’t going to use (or adding more to our already overstuffed stack) we decided to build the whole thing with content types, rules and views.

No problem right?

Feb 15 2015
Feb 15

Two new drupal distributions available on githubfrong

** https://github.com/alibama/cvillecouncilus is the distribution behind https://www.cvillecouncil.us - it’s an attempt to run a political campaign through a virtual proxy…

** https://github.com/alibama/rapid-prototyping-ecommerce-drupal – this is the code behind http://rpl.mae.virginia.edu/ it’s an e-commerce solution for 3d printing… A lot of this is implemented in rules and other well-standardized code thanks to Joe Pontani - a talented developer here in Virginia.  Joe integrated several third party tools, and set up the UVa payment gateway through Nelnet.

Both sites are getting updates over the next few months – the Charlottesville Council website also has a drupalgap implementation on it – absolutely awesome toolset…

18F API compliance is another feature I’m pretty stoked about… I got most of that done with the oauth2 server, views datasource, services and a couple of great notification features done with rules + views  i’ll get that feature out asap = it’s really convenient – matching a profile2 taxonomy field onto content taxonomy fields for notifications with new content.

any questions – please drop a line in the comments below

Nov 26 2013
Nov 26

Our library is always open…*almost always.  Things happen (like Thanksgiving, Christmas, New Years) and sometimes we even have extended hours and we stay open even longer. Altogether, we have ~ 14 weeks/year that have “non-standard” hours.

In the past, we had to manually managed our weekly hours by updating a single, static piece of panel content.


You can probably imagine the problems we had trying to maintain this accurately – every week that we had deviated hours the desk supervisor had to submit a request for us to change them. We might be busy with other projects, might not get to it right away, the hours on the home page might be wrong, the desk supervisor might have to submit another request, and then pretty soon they might have to ask us to change it back to the regular hours…

Too much hassle.

We knew we needed dynamic content, and because our homepage was built as panel page, we were ready to pump a view into the hours pane. The question was how to set up the content so that we wouldn’t have to touch it…at all.

We landed on a combination of the office hours and scheduler modules.

Office hours gives us an “Office hours” field type. Since we want to display hours on our home page in weekly chunks, we create a “Weekly Hours” content type and add that field.

weekly hours content type

And we set the default values for this field to the library’s standard opening hours – usually when we have a week with weird hours, it’s only going to affect a couple of days so this helps cut down on data entry.

default office hours field settings

Then the scheduler module kicks in – the content type must be configured to allow nodes to be scheduled for publication. Check a couple boxes and start adding content.

scheduler options of content type

Since the goal here is for us not have to touch the hours (at all) we enlisted the desk supervisor to enter the data – with a couple of tweaks to the permissions table and a 5 minute demo (add node >> toggle hours >> set publish date for sunday evening before given week >> set unpublish date for sunday evening of given week >> click save) he was good to go. In all, he added 10 “Weekly Hours” nodes, including one with the library’s standard opening hours, because that was as far in advance as the library had planned its opening hours.

scheduling data

With the content added, we were able to start manipulating views so that we could display it on the homepage. Since all the nodes (except for the one with standard hours) had an unpublish date, we could configure a view to output only nodes of the type “Weekly Hours” that are published, sorted by the post date descending, displaying one at a time. That way, when a week of non-standard hours is published, it’s displayed in place of the standard hours, which were published previously but are never unpublished.

view for current hours

By creating a block display and setting it to only output the “Office hours” field, we should be all set. If there’s any simple formatting, re-ordering, etc. that you want to do to the hours, you should check in the field settings in the view. The office hours module has substantial integration with views, and gives you a lot of flexibility in how you want to display the hours.

highly flexible field settings for office hours

After that, we view block into a panel and it takes care of itself – here’s the final result on our homepage:


One cool feature that we may implement for mobile is the current hours display. In the view configuration for the office hours field we can set the number of days to show – by setting only the next open day to be displayed, you’ll get a view that can be formatted something like, “The library is open today from: [*]”

current hours today display office hours module
Nov 22 2013
Nov 22

Ever since we launched our site re-design, we’ve had a pretty steady flow internal change requests – add links, change fonts, re-style buttons, adjust layouts, modify permissions, etc.

We were trying to process these requests through a ticketing system that we built on our staff intranet in Drupal 6 – it basically allowed us to toggle tickets between open/closed and send emails when we were finished. With requests piling up (some of which overlapped with one another and/or were vaguely articulated and/or were out of our control anyways) this system wasn’t cutting it. We decided to scrap it and develop a new workflow in Drupal 7 with three questions in mind:

1) How can the requestor  clearly communicate what it is they actually want us to do
2) How can we clearly communicate what it is we’re actually doing
3) And how can we, as the general put it, start “hitting a small nail with an awfully big hammer”

The first two issues were solved easily enough: we enabled comments on the “Web Request” content type. When someone creates a node of that type, we can sniff out what they need with some probing in the comments area. The comments allow for transparent dialogue, so long as you have an effective method for distribution.

Enter the awfully big hammer…

Using a combination of rules, views, display suite and user reference fields, we gave node authors the ability to create email lists specific to the requests they create. With the click of a button they can message as many of their colleagues as they choose, giving them the gift of email at every step of the request.

Here’s how we did it:

First we created a form – we set up a content type for “Web Requests” – we were going to use entityforms but couldn’t since we needed commenting. We added fields for the data we wanted to collect (request title, details, link, and screenshots). We also created administrative fields (for things like UX data, project tagging, etc.) that are only visible to the web team, and therefore don’t appear on the node/add form that requestors use.


The most important field (for the email list) is the user reference field – what we termed “Stakeholders” is a selection of users of a given role formatted as checkboxes, with no restriction on the number of values.


The stakeholders appear on the form listed by name – that works for the users filling out the form, but not for rules, which will need their email addresses. Using display suite, we can configure a custom output for the stakeholders field when it is used as a token. In the “Manage Display” screen, you can specify custom display settings. Since we’ll be using tokens in the rules and views configurations we want to customize that to get the user’s email.


So the token display for the field needs to output [user:mail]


With the content type and fields configured, we’re ready for rules.

We want “Stakeholders” to be notified when a new request is created. So, we add a rule that reacts on saving new content and triggers an email message. By adding a condition that states that the content has the stakeholder field, we can get the token (mentioned above) and use that as one of the recipients for the email. But the email action will only pass through one of the values. That means only one person will be emailed, and that’s not good enough – we need everyone getting these emails. Rules allows us to loop actions, but not without a view that contextually displays content, which in this case is a list of stakeholder email address.

So we create a new view of content – the only field we need is a stakeholders field that has a custom formatter of tokenized text and outputs [user:mail]


The next step is to set a contextual filter that acts on the content node id (nid) – this should generate a list of stakeholder email address per node. Preview it to make sure by passing the view a nid.


In order to call the view in rules, we have to give it a rules display. This also allows you to specify row variables – the important thing in this case is that we use the rendered result and the data type is text.


With the view in place, we can go back to the rule we created and call it as a views loop.


Add the email action to that loop and you’re set.


Each event that triggers an email will have its own rule, but you can use the same view/view loop structure for all of them, except comments. To get the stakeholder addresses to work in an email that’s set off by a comment, you have to create another view. This will be a view of comments that is configured almost identically to the stakeholder view, but with a relationship that joins comments to content so that you can use the nid filter.

Aug 29 2013
Aug 29

Over the past few months our site has undergone a total re-design // upgrade from Drupal 6 => 7.   The priority was to rebuild functionality and migrate content quickly and cleanly – permissions schemes and editing privileges for our content contributors took a backseat. Now that the site has been launched and stabilized, we’ve begun to look at some of the tools we used to try to figure out how and if we can train our librarians to use them.

On our old site, we made pretty heavy use of blocks (particularly for sidebar items) – since we had to rebuild these pieces of content anyways, we tried to put them in more flexible containers.  We started looking at panels – we found Panopoly.  This tool worked really well for us.  We could (and did) use panel pages, custom panel layouts, views as panel panes, mini panels as blocks, blocks as mini panels, etc.  But when we started to turn over content editing responsibilities to our librarians, we discovered that the default settings were way too powerful for what they needed to do.  The interface was overwhelming and privileges were set too high – our content editors had too many options.  We had to scale back.


The first step was to lock down Panelizer privileges on the home page – we were clued into this one when one of the librarians told us that she was seeing a “Change Layout” button on the site’s front page.  That meant she (or any other content editor) could have changed the layout of the home page with two button clicks.  Not good.

We probably could have done this a few different ways – we chose to change the renderer of the home page (built as a panel page) from “In-Place Editor” to “Standard”. Of course this means that we (admins) can’t use the groovy Panelizer interface when we want to edit content on the home page – but that’s cool since we know that those content regions are mini-panels and can be edited elsewhere.


That took care of the home page, but the librarians were still seeing too many options on the other pages (see first screenshot) – we could get rid of the In-Place Editor on all pages, but we’d have to make those configurations on each panel page (or page that had been panelized) and we would lose the slick, drag-and-drop editing interface. So we hit the permissions table.

We found that the permissions were set way too high. In the screenshots that follow, you’ll see what we left on – note that we have 11 roles in the table. The 3rd column from the left is admin, the 4th is editor (librarians) and the last one on the right is portal manager, which we made for development purposes. When we apply these changes to the production site, we’ll set editor privileges to the same as the portal manager on development. So just pay attention to the one on the far right – these are the only permissions we need for our use case: giving librarians permission to to edit layout and add panel content to basic web pages.

permissionstable2 permissionstable3 permissionstable1 permissionstable7

All other panel, panel pane, panelizer, etc. privileges in the table need to be locked down. Note that some of the permissions we turned on were specific to a content type (our “Web Page” content) and that this will vary depending on your needs.

Restricting these permissions reduces access to the editing interface.  Our librarians will no longer see “gear” buttons – they’ll only see “Customize This Page” and “Change Layout” .


But when they click “Customize This Page” and try to change panel content, they still get bombarded with too many editing options (see the first screenshot) – we can fix that. The Panelizer configuration allows you to adjust settings for allowed content per content authoring method.


Since we’re locking down Panelizer settings for the “Web Page” content type, that’s where we’re headed in the configuration table.


This is where we want to be – lots of boxes to uncheck, lots of buttons to push.

That’s cool – all our librarians need to do is add lists of links, images and text, and (ideally) to be able to reuse the content they create elsewhere.

Here are the settings we used:

allowedcontent3 allowedcontent4 allowedcontent5

The result?  Content editors can now…

…choose layout:


…add, edit, move, delete content to the regions within this layout:


…add links, images, text or reusable content:


Note that if they do want to reuse content, they have to specify that in the editor:

Jul 18 2013
Jul 18

I’m betting on there being a better way to do this.  There just has to be.  But I haven’t figured it out, so here is how we’re using (abusing?) Views PHP and ApacheSolr Views  to deliver results on a per-content type basis.

apachesolr views with per content type results

apachesolr views with per content type results

To start with we create a new view

choose the correct solr index

choose the correct solr index

After the new view is created load a bunch of fields on in – it may be useful to remember that “Bundle” will return a content type by name, the “content” will return all of the fielded content in an string, and “entity_id” has the node value.  One cool thing you can do with the node id is then pass that as an argument to another view using Views Field View - be aware that this is somewhat performance naughty, but it’ll work if you want access to images in your results and don’t feel like creating a custom module to index images.

exclude fields from display

exclude fields from display

After getting your fields loaded I set their values to “Exclude from display” in the field settings

Our view with a few fields loaded

Our view with a few fields loaded

And of course don’t forget to add a Global: PHP field – here’s our view now

In the Global PHP comes this fubar stank piece of code

$type=$row->bundle; // Here we’ll get our content types
$content=$row->content; // This is all of the content per node that’s been indexed
$title=$row->label; // This is the node title
if ($type==”mobile_resource”)
$startI = 1;
$stopI = strpos($content, $stop, $startI);
$text=substr($content, $startI, $stopI – $startI);
$startsAt = strpos($content, “URL:”) + strlen(“URL:”);
$endsAt = strpos($content, “Platform:”, $startsAt);
$result = substr($content, $startsAt, $endsAt – $startsAt);
$trimurl=trim($result, chr(0xC2).chr(0xA0)); // This will save someone time – be sure to trim the nbsp spaces from your content when needed

echo “<a href=”$trimurl”>$title: $trimtext</a>”; // and then print stuff as you see fit

else if ($type == “cme_class”) // and more of the same
$startI = 1;
$stopI = strpos($content, $stop, $startI);
$text=substr($content, $startI, $stopI – $startI);
echo “<a href=”$url”>$trimtext</a>”;

$startsAt = strpos($content, “Location:”) + strlen(“Location:”);
$endsAt = strpos($content, “Date and Time:”, $startsAt);
$location = substr($content, $startsAt, $endsAt – $startsAt);

$startsAt2 = strpos($content, “Time:”) + strlen(“Time:”);
$endsAt2 = strpos($content, “Registration”, $startsAt2);
$time = substr($content, $startsAt2, $endsAt2 – $startsAt2);

print(“<h5>Time: $time</h5>”);
print(“<h5>Place: $location</h5>”);
else if ($type == “employee_bio”) //etc
$startsAt = 1;
$endsAt = strpos($content, “My Education”, $startsAt);
$who = substr($content, $startsAt, $endsAt – $startsAt);
echo “<a href=”$url”>$who</a>”;
echo “<a href=”$url”>$title</a>”;

And really… I’m pretty embarrassed by this.   It’s raunchy and rank.  It works.  The facets facet and so on… I just can’t believe that this is the way to do things.  I’ve looked at Solr Panels and feel like there must be a way to do better – maybe having multiple views pre-faceted by bundle at the view level and the made in to content panes?   Anyone with some sense of how to improve this is encouraged to chime in…. Bueller?  Bueller….

Jun 24 2013
Jun 24

@Font-your-face in Drupal 7 provides thousands of fonts to the stylist and end-users with a clean administrative panel.

for the video inclined there is a ~2 minute walk through here https://www.youtube.com/watch?v=DndK-izVnE4

Getting there – after installing click on the “Appearance” menu item and look for Font your face on the top righ

Font your face

Once you’ve gotten in to the font section you will need to enable some fonts… plenty of buttons to click…

enable some fonts...

enable some fonts…

Now that your fonts have been enabled it is time to assign them to parts of your site… the CSS selector tool allows you to provide default fonts for HTML variables like H1, H2, node body regions, and of course … the “Other” css selector. Almost any part of the site can be “fonted” from this dashboard.

Applying fonts to HTML and CSS styles

Applying fonts to HTML and CSS styles

If you are using Sweaver for front end CSS styling you can enable the Font Face plugin in the settings, and all of your new fonts become available in the front end for selective front end styling.

Front end styling in sweaver

Front end styling in sweaver

Apr 02 2013
Apr 02

One of the great tools in Drupal is flags – it’s the big red button.  If R.A. the Rugged Man and Vinnie Paz put together a Drupal site it’d have things to punch on it, and those things would be flags.  Drupal 7 has added several tools that massively extend flags – Flagging Form which may be further extended by the Dialog module.  And to improve the UI a bit we used the Improved Multi Select widget – thanks to Rooby for a recent shout out – tracked your profile and was stoked to start using this :)

For those who want a brief overview of the tools https://www.youtube.com/watch?v=xQ_6eL9_TZ0

A flagging form with term reference and improved multi select in dialog box

A flagging form with term reference and improved multi select in dialog box

And this is again one of those cases where seeing drupal connect various modules together for net gain is pretty sweet.

The Flag set up is pretty straight forward – for our use case we here’s our flag edit page.  Then once that’s done we added a simple term reference field in to the fields section of the form (this is, of course, where flagging form came in handy)

flag setup with dialog box for the form

flag setup with dialog box for the form

Adding the fields is straight forward enough that if you know anything about fields you should be able to do your thing. After that the only step left in this recipe is creating the view that lets you flag content. This too is fairly standard

flag links in views

flag links in views

One thing that still doesn’t work is our taxonomy depth filters. Because the terms are attached to the flag and not directly to the content it’s possible to use contextual filters to return content that is appropriately tagged, however the “With Depth” filters do not work as expected. If anyone has any thoughts on this I would be most grateful.

Mar 15 2013
Mar 15

Rather remarkably we’ve managed to avoid the top xxx module list for Drupal 7… however to recap the presentation yesterday at ACCSVa.org here it goes….

A Drupal Roadmap with Rich Gregory – Look in to some critical dev tools like Drush and other things to get you going.

1.  Display Suite (putting Views at the top almost redundant….) – thanks to Display Suite and it’s buddy Field Group Drupal 7?s core content creation kit is a flexible dashboard delivery tool.  With a few clicks you can now turn a lengthy and unintuitive form into a dashboard – i’m seeing hope for a wordpress like content adding area.

and after

and after DS + FG

Forms before Display Suite and Field Group

Forms before Display Suite and Field Group

2. Views – it should go without saying, and now that it’s going to be a part of Drupal 8 core I’m going to leave it at that… you need views to do anything worth doing.  We’ve got a half dozen or more tutorials on views here, so dig in.

3. Context – this is your logic layout tool – pick conditions and reaction.  There are numerous modules to extend context as well – in the presentation I mentioned http://drupal.org/project/context_reaction_theme however this only has a D6 option.   You’ll probably need to use http://drupal.org/project/context_addassets to do essentially the same thing.  Also note that Mobile Tools allows you to use contexts to do dramatic theming changes based on the mobile device.

First up choose the conditions for your layout

First up choose the conditions for your layout

The choose your reactions

The choose your reactions

4.Rules: Rules allows your site to become a dynamic workflow management intranet style workhorse. The amount of flexibility here, much like Views, extends beyond the scope of a simple “short stack” review, however in essence you’re taking events that happen within the site, or custom crontab events, setting conditions and triggering actions. Coupled with modules like Views Rules the possibilities are amazing.

5. Entity reference - extending CCK (part of drupal 7 core) the up-and-coming successor to References. Allow content to reference other content, and as mentioned this allows View Relationships to create a SQL JOIN on your content – get more information about your Content Author, and many more options…this post here is particularly fun with references referencing references…

6. Honorable mention: Feeds – this is the bulk content migration tool of choice for folks like myself.  It’s intuitive and lets you harvest content from various sources and ingest it in to your content types, user tables, etc.. we have a few tutorials on feeds that may help you with some specifics – it’s a powerful tool, and coupled with tools like feeds tamper there are a lot of options.

7. Honorable mention: Flag.  Give your users buttons and they’ll push them.  Flags allow your users to have simple on/off buttons – categorize content, flag spam, etc…  they of course work with views, rules, and the rest of the gang :)

So there’s my short stack for Drupal 7 – I’m sure entities and entity forms probably belong on there, however for most basic sites I think this is a good start… heck probably need to talk wysiwyg editors too…. so many modules!  Thanks again to ACCSVA.org for the conference, Rich Gregory for the great tunes and the lift, and  Blue Cat Networks – the hat is bangin.

Mar 04 2013
Mar 04

Our use case seemed pretty straightforward.  When users register on the site they select terms from a taxonomy list based on a field in their profile2 profile.  As content is added to the site the same taxonomy is used to categorize content.  We wanted a view that would let users see any content that matched any of their taxonomy term selections.

I’m on the drupal4lib mailing list and want to thank all those who responded, specifically Kelly Lucas aka krlucas who is quoted below

Start with a base View that shows Users (not Content).
1) Set a Contextual Filter for User ID, provide a default value and set it the
logged-in User’s ID
2) Create a relationship from the user to the user’s profile
3) Create a relationship from the field containing the select terms on the
user/user profile to the taxonomy terms
4) Create a relationship from the taxonomy terms to the nodes that have that
taxonomy term.

You should now be able to add the relevant node fields to the View.

walkthrough of a view

walkthrough of a view

using the query settings "distinct" to limit the values instead of one per term

using the query settings "distinct" to limit the values instead of one per term

whatcha get from all these relationships

whatcha get from all these relationships

What didn’t work: So while this seemed incredibly straightforward getting this many-to-many relationship to work caused me endless grief.  I tried views global filter thinking that might be a good solution – according to their documentation any fields pulled from the profile are used to override the values.  Using the multiselect widget this simply failed all over the place – values didn’t save at all, weirdness reigned, and general misery ensued.*

Also in the “fail” category – making a view based on the content table and the taxonomy table.  Either of these might have worked with a few more relationships, however at this point I think we’re stocked up.

Thanks to all involved who helped on this – Cary Gordon, Brazos**, and of course Kelly Lucas

*fwiw we read the entire issue queue and documentation, however this statement really hurt Rik’s feelings.  Would someone please hug him down under?

**Brazos added another interesting option of using page manager to override the user page and pass variables along that way – I’m not experienced in this line of development to implement, but it seems interesting.

Feb 28 2013
Feb 28

EVA, short for entity views attachment, and Display Suite – like Wu Tang vs The Beatles – is a great combo for getting diverse information on a page.  If you just want to see what it looks like and how it works http://www.bioconnector.virginia.edu/galaxy.  For those of you who like the video style learning here you are: http://www.youtube.com/watch?v=E9uq2ZOZNVw

Use case: We have a content type “Experts” that we want to link to the content type “tools” that they are adept with.  Tools  chosen during registration will then show their profile on the page and on their profiles there will be a link to their tools.

Screenshot of the layout with the attached view

Screenshot of the layout with the attached view

What we built: If you’ve used views attach in D6  the EVA concept should be pretty straightforward and the views attach tutorial here covers it pretty well.  As the name suggests EVA takes the broad level entity API as the starting point.  This gives us a lot of flexibility in terms of where we can use the EVA module.  By using an entity reference on the “Expert” content type we are able to create a select list for the end user.

Some of the basic aspects of EVA

Some of the basic aspects of EVA

Could you do this with a node reference… sure – but entity references are so mo betta!

entity reference in the content structure

entity reference in the content structure

Ok so you’ve got the entity reference in place, and the EVA view built… now you want to drop that view stylishly on the the page…  and because the good folks at EVA did things so well all you need to do is install Display Suite and your work is almost done.  The nice thing now is that all you need is to pick a layout and viola… you got pretty music ;)   I realize this is all pretty 101ish stuff, and that is the good news.  There’s really not much more you need.

A few other handy modules that made things pretty: Sweaver.  Some folks can’t stand this module…  I happen to love it for the basics of getting spacing and font sizes correct. Gallery Formatter with the Lightbox2 plugin handled the gallery in the content type – also positioned thanks to Display Suite.

Display suite 101

Display suite 101

What the editing user sees: and the last thing to note is what the user sees when they’re adding experts in to the site:

All the end user needs to know

All the end user needs to know

Dec 05 2012
Dec 05

Feeds SQL does its job so well it’s rather mindblowing.   As the name suggests it allows Feeds to use a SQL query as its data source.  The mapping and all the rest of it works as one might expect, and that’s what is blowing my mind right now.  After installing the module

SQL Fetcher

SQL Fetcher

Firstly set your fetcher to the sql fetcher…  I only have one database in my settings file for now, but that’s sure to change.

Secondly choose the SQL Parser… I guess this means that you might be able to upload a file in to the fetcher and then parse it… for this example we’re going SQL Fetch and SQL Parse though.

Choose the SQL Parser

Choose the SQL Parser

And now the magic begins…  in the settings you can test your sql fetch by putting in a select statement.  For this example I made a dummy table with some random stuff in it

SQL Parsing

SQL Parsing

and now comes the wow!

It's data!

It's data!

And more wow!

Your columns are ready to be mapped!

Your columns are ready to be mapped!

So pretty – and just like that all those whacked databases you were trying to decide how to merge in to Drupal are available with feeds…. yum yum yummy. Mad props to vkareh for the dev on this piece… absolutely awesome.

Nov 05 2012
Nov 05

String Overrides is a pretty sweet module for getting the text on your site more exactly like the client wants.  In general it works with any t function – what I didn’t realize at first is that this means it’ll override variables embedded in the t function as well.

So I was looking for some text that was on the site and initially was doing a search for a pretty long string, not realizing that within the string were several variables that were to be replaced… so by shortening the search to a few words within the string – specifically grep with the flags -H (for the filename) and -r (for recursive search within the modules directory) I got the following

grep -H -r "entities with this value set" .*
./field_analytics/field_analytics.reports.inc:    $headers = array(t('Value'), t('Number of %bundle entities with this value set', $replacements), t('Percentage of total %bundle entities with this value set', $replacements));
../contrib/field_analytics/field_analytics.reports.inc:    $headers = array(t('Value'), t('Number of %bundle entities with this value set', $replacements), t('Percentage of total %bundle entities with this value set', $replacements));

So then it was obvious – the %bundle was actually pulling in a variable and thus doing the search for what seemed like the string was failing, however doing a string override with “Number of %bundle entities with this value set” worked beautifully

String overrides working with HTML and variables in t functions

String overrides working with HTML and variables in t functions

Anyhow, this may be old news to some, but it totally saved my weekend… Aloha

Sep 24 2012
Sep 24

What with this being a library and all it became necessary to get a document management solution rolling.  Initially looked at  File Depot and may go back to that again, however it wasn’t working cleanly out-of-the-box… the desktop integration though may bring it back…  Also I really like PlUpload for its HTML5ness and potential to work on iOS devices, however it doesn’t mesh with CKEditor – our WYSIWYG of choice, and that’s a major requirement.

So without further ado…

webfm tools

webfm tools

Set up default directories and such

Set up default directories and such

Configure the wysiwyg editor

Configure the wysiwyg editor

Open up the webfm module

Open up the webfm module

The webfm module in action

The webfm module in action

Sep 13 2012
Sep 13

CCK Signup is a fun module that is very drupal in the sense that it really just extends a bunch of modules you may already have.  Here’s a brief tutorial on using Rules to handle some basic notifications.  Say you want to know when someone has dropped a class.


Your initial rule conditions

Your initial rule conditions

  1. get a new rule set up and set the initial condition to be “After deleting content”
  2. Set the next condition to be “content has type”
  3. Select the signup type of content
  4. Lastly choose to send an email to an arbitrary email address – in this case let’s say it’s you, the webmaster
  5. Send the email.  Thanks to the Tokens module you can reference content from the fields in the deleted content Using tokens in the emails

    Using tokens in the emails

    Send an email to an arbitrary email

    Send an email to an arbitrary email

    select the type of content

    select the type of content

    Content has type as condition

    Content has type as condition

Aug 27 2012
Aug 27

We needed to allow our users to be able to create image captions for images in a way that was easy and made sense to everyone. We wanted our end users to be able to simply add a class of “caption” to an image using the wysiwyg editor and for Drupal to take care of the rest. Thanks to the Drupal discussion here for this simple idea, http://drupal.org/node/264932.Here is one solution that was quick to implement:
Module Download: image_caption_module.gz

Our Step 1:

We created a simple module that added a new input filter, called Image caption filter. Here is the code:

 tags of the class 'caption', 'image-right' or 'standalone-image'
  with a alt attibute set.  It then places the  tag in a

with the width and class attributes set. A

tag of class "caption" is also added with the caption text obtained from the alt. It removes the class attribute "caption" from the

tag. */ /** * Display help and module information * @param path which path of the site we're displaying help * @param arg array that holds the current path as would be returned from arg() function * @return help text for the path */ function image_caption_filter_help($path, $arg) { $output = ''; switch ($path) { case "admin/help#image_caption_filter": $output = ' ' . t("Adds captions to images") . ' '; break; } return $output; } //function image_caption_filter_help //filter hook implementation function image_caption_filter_filter($op, $delta = 0, $format = -1, $text = ''){ switch ($op) { case 'list': return array(0 => t('Image caption filter')); case 'description': return t('Adds captions to images via a filter'); case 'prepare': // Nothing to prepare return $text; case "process": //Look for

tags and run the doImgTitles function on the

tag $text = preg_replace_callback('|()|s', 'doImgTitles', $text); return $text; default: return $text; } } //function image_caption_filter_filter //helper function to do the actual manipulation function doImgTitles($matches) { $imgText = $matches[0]; //Get the alt out of the

tag preg_match ('/alt=\"(.+?)\"/i', $imgText, &$matches); $alt = $matches[1]; //Get the width out of the

tag preg_match ('/width=\"(.+?)\"/i', $imgText, &$matches); $width = $matches[1]; //Get class out of the

tag preg_match ('/class=\"(.+?)\"/i', $imgText, &$matches); $class = $matches[1]; //Only insert the caption and modify the

tag if it is has an alt attribute and has the class "caption" if (in_array($class, array('caption')) && ($alt)) { $imgText = preg_replace ('/class=\"(.+?)\"/i', '', $imgText); $returnText = "

" . $imgText . "

" . $alt . "

"; return $returnText; } return $imgText; } //function doImgTitles

Our Step #2:

After enabling the module, we simply added that filter to our existing named filters that we were using for our content types that allowed HTML content. Our two types were “Raw HTML” and “Full HTML”. Do this by:

  1. Go to your filter settings at /admin/settings/filters/
  2. Choose “configure” for your named filter.
  3. Under the Filters section select “Image caption filter”


Now your images can be given the class of “caption” by your wysiwyg editor or other and they will be converted from this:

This should be the alt text and the caption text

…To this…

This should be the alt text and the caption text

This should be the alt text and the caption text

This method can be applied to any html element, class, and desired html output. Just change the values in the module. Here is a link to the module if you want it: image_caption_module.gz

Aug 08 2012
Aug 08

Feeds is a lovely utility that has blossomed greatly in Drupal 7. As with Drupal 6 both Feeds and Feeds Tamper are Features compatible and now even more versatile with add-ons like Feeds SQL.  There are a several caveats to getting Feeds happy when working with CSV formats.  I’m coming from a Mac environment, often mapping excel spreadsheets over in to Drupal.  These are my ongoing notes, am doing a lot in Feeds and will keep this post as a list of my remarkably consistent failures.  (NB: if you’re in D6 Node Import just seems way more forgiving on all these points, just not nearly as “Drupal Hep”)

Remember to get your csv UTF-8 compatible – I like Text Wrangler for this – especially when coming from Micro$oft Excel your content may have some odd-ball encoding. So if saving as Windows, Unix, or MS-Dos CSV fails on you just open up your CSV file in there and resave

CSV fix in textwrangler

CSV fix in textwrangler

saving csv from excel for feeds

saving csv from excel for feeds








Caveats:  Another common quandary with Feeds is using it with fields outside of core CCK or Entities. While it’ll map to CCK Select Other it has a tough time, especially with multiple values.  Even with the values put in the drop-down selection area it always mapped to “Other” and then stuck in the value.

Unlike in Drupal 6 this field will throw errors if indexed, doesn’t seem to work with the Facet API, or be good for much of nothin’. Perhaps Select Or will work better, however it seems to have view limitations too.  So while none of this reflects on Feeds it’s worth noting because you may end up needing some UI tidbits… it may map to them, but it’s

Aug 02 2012
Aug 02

I love excel’s vlookup tool. it’s great for replacing stuff in one spreadsheet using key values and whatnot

UPDATE content_type_er_resources AS t
LEFT JOIN collections_resources AS e
ON e.id = t.field_er_resource_uid_value
SET t.field_er_resource_notes_value = e.res_notes

this is essentially the same thing in sql and it saved a bunch of time.

Jul 16 2012
Jul 16

There are many forum links with numerous hacks detailing how to create a single exposed filter that combines search across multiple fields.   In Views 3 and Drupal 6 or 7 it is as simple as installing the Views Filter Populate
Setting up groups for views populate filter

Setting up groups for views populate filter

And that’s about it – just configure the new global filter to include your desired fields

Configure views filters populate

Configure views filters populate - include the fields you want searched

There isn’t much to know about this module, although I certainly haven’t tested it against dates or numbers, and I rather doubt that works. For text it works like a charm though – http://www.youtube.com/watch?v=AwZj8o2v4uM for the folks who like videos here’s your 1:45 of fun

One filter, two fields... yum

One filter, two fields... yum

Thanks + add any questions or comments below
Jun 21 2012
Jun 21

Increasingly our web pages are being bookmarked on iOS devices.  This is a good thing, however we had traditionally been using long titles on our pages and these were clumsy for end users when trying to store bookmarks.  Like 90,000 other websites we have chosen Page Title to solve this issue.  It works well with Tokens and Views and is probably one of those modules you should just install.

Here’s your 1:30 video http://www.youtube.com/watch?v=DMvXVJ2mbGQ

and a couple of screen shots for the really curious types.  It’s a simple module to install and use. Made by several of Drupal giants robertDouglass, JohnAlbin, and nicholasThompson.

Turn on page titles in the content type area

Turn on page titles in the content type area

Enjoy refreshing page titles independent of your content title

Enjoy refreshing page titles independent of your content title

May 17 2012
May 17

Sometimes Drupal opens up a big can of whoop-up process on ya, and Rules Scheduler is one of those gems.  Rule Scheduler allows you to use rule triggers to schedule rule sets.

Use Case:Ever want to send an email the day before a scheduled event?  We use CCK Sign-up and wanted to send an email to users one week before an event, as well as the day of.  We also needed to send teachers views of the roster of students

Here’s the 2 minute mojo on it http://www.youtube.com/watch?v=55HwOzFHoB8 and it just highlights the mistakes I was making.

The recipe came out of Drupal documentaiton, and these two tutorials got me 90% of the way:

working with scheduled rules

working with scheduled rules

And I thought I was home free, however I ran in to a pretty stupid problem that took me a minute to understand = all the identifiers need to have tokens in them with unique values or else they overwrite themselves in the database.

Solution? Easy = add some tokens – [node:nid] worked for me

The nicely scheduled rules

The nicely scheduled rules

Another fun feature in this project was revisiting one of my favorite modules: Rules Views Integration.  The cool thing about scheduling a rendered view in a rule set is that the rule gets rendered at the last minute and so you can schedule (in our case) a class roster to go out and it’ll have the most up to date information possible at the time of sending.

NB: I also ran in to a problem where cron wasn’t sending my emails initially.  I have no idea why it was stuck, however Ultimate Cron has been a good friend of late and unlocking and running the tab got it back on track = I haven’t seen it stall in a few weeks now.

Ultimate Cron unlock and run run run

Ultimate Cron unlock and run run run


Something else I haven’t gotten into yet, however, is the use of the Rules Scheduler Views.   Something tells me there is some magic to be had in there, used correctly.  At this point however I’m just using the off-the-shelf defaults.   I know of no Views Bulk Operations for scheduled rules, so perhaps the default info is all there is to know.  In any case, it’s an elegant solution to working with drupal’s cron functions.

May 11 2012
May 11

Use Case: We want patrons to find our forms easily.  If they need help finding something they should be able to go to a search bar, type in “consultation request”  and get to our consultation request form.

The Challenge:  First off Solr doesn’t index the node/add/your-content-type-here pages by default – I’m certain there’s some workaround, however it’s not obvious (dear readers are welcome to retort).  Secondly none of the form results should be indexed by solr in the first place – we certainly don’t need our patron’s requests showing up anywhere ever.

The Hack: Form Block allows you to make content types available as blocks.  Pretty straightforward, click a few buttons, go to the context editor, and woo-ha = forms in a block = we took the pages that were already returning for the queries we were interested in and just added the form to those pages.

Here’s the VERY brief overview – 1:30 seconds or so worth http://www.youtube.com/watch?v=gzvq-t1m03A that goes over turning a content type into a block as well as adding the block to a context using the context editor that comes with the admin toolbar

Form Block settings in content type

Form Block settings in content type

Now is also a good time to mention that if you are using context’s the Admin toolbar is almost required – it really extends the UI giving you a drag and drop interface for your blocks within every context active on a given page.

Adding node form in the context editor

Adding node form in the context editor

Form Nodes in context Editor

Form Nodes in context Editor

A form in a block!

A form in a block!

May 10 2012
May 10

Use case: Our patrons don’t really think of “saving” their class signups, nor do they often care to preview the request form that they recently filled in.

Quick solution: Using Jammer (admin/settings/jammer) we are able to easily remove the preview (delete, or submit) button on selected content types.  This is also a place to remove path, menu, author and several other fields.

Jammer - remove preview

Jammer - remove preview

Saving makes sense for admins, however our patrons really don’t think of saving their work – they’re submitting requests. Making a happier UI is part of the work we do to keep our patrons around, so we use more vernacular terms to mo betta stuff.  The obvious limitation here is that it overrides EVERY string rendered

Simple string overrides simplify succinctly

Simple string overrides simplify succinctly

in drupal (not in the content, of course)

If anyone knows of a module that’ll let us customize buttons on a per content basis please chime in – not interested in writing tpl.php’s for every one of these though – it’s just not that big a deal yet. Speaking of UI thanks Vid @ the University of Oregon (rss added to the left here) for their work with Taxonomy Super Select Styling http://pages.uoregon.edu/vid/2011/01/21/put-taxonomy-super-select-check-boxes-into-1-2-or-3-columns/

Apr 24 2012
Apr 24

JSON is a data exchange tool that has some distinct advantages over XML as Dare Obasanjo notes.  Using Feeds JSON Path Parser to ingest JSON and Views Datasource to output JSON we are hoping to open up our data to web services.  This tutorial is just about Views Datasource, hopefully we’ll get on the eating side of things with feeds soon!

For the two minute magic here you go http://www.youtube.com/watch?v=yc9Uc-2WczI

Usually when we write up documentation it’s to explain something we understand reasonably well (or at least think we do) on this post we’re grateful to Doug Chestnut over at Alderman for recommending JSON, twistor and infojunkie at drupal.org for banging up some code.

This is just a brief review of a simple views plugin with a lot of potential. In general it is becoming more and more apparent that having web services is rapidly eclipsing the value of web sites.  Web services means being able to extend your integral core data to other developers without the shrouds of silly design and such that stem from institutional committee design.  Put the data in the hands of designers and let them run with it.

Settings for JSON

Settings for JSON

All said and done what this means for developers who may have a use for our electronic journals and books data is that by changing a URL they may have direct access to our work as JSON objects – something that SOLR may ingest directly as of 3.1 – for an example on our development server http://drupal.hsl.virginia.edu/e-resource-journals-json/Biochemistry now returns the JSON output of the regular taxonomy view http://drupal.hsl.virginia.edu/e-resource-journals-taxonomy/Biochemistry

JSON Style in views

JSON Style in views


Of course now that we have all these lovely services who is it we’re servicing?  That’s a question we’re still working on the answer to, however given that UVa’s main library is outputting JSON and running SOLR we’re hoping we may be able to streamline and consolidate some of our work for our patrons  (fyi for the VIRGO JSON the string change is slightly different http://search.lib.virginia.edu/catalog?q=chestnut now becomes http://search.lib.virginia.edu/catalog.json?q=chestnut- still a simple way of handling data objects.

Additional resources:

http://jsonviewer.stack.hu/ a handy tool for JSON viewing

Apr 17 2012
Apr 17

Our online exhibits need an attractive home page. To make the view we decided upon Views Fluid Grid for the display (using imagecache of course to fix the width of the images). We use taxonomies extensively in our site to help create contexts among other things so using a term view to list all of our exhibits is a natural extension of the library philosophy. Since not all of our exhibits are hosted on the same site we added a term field to provide a space for a dedicated home page on each exhibit. We also used taxonomy image to make the page more attractive.

Views Fluid Grid Output

Views Fluid Grid Output

It’s a bit long winded at ~3 minutes, but hopefully thorough enough for anyone following along http://www.youtube.com/watch?v=j0fnqBOAj7k

As a result all our archivists must do to add a new exhibit to the home page is add a new taxonomy term to the archival taxonomy and the image and term description are posted to the fluid grid page.

Taxonomy term with a single term field

Taxonomy term with a single term field

The settings within the view itself are pretty straightforward -

Views Fluid Grid settings

Views Fluid Grid settings

and the main thing to remember is that you’ll want to go to imagecache and create a scaled and cropped fixed width preset- this keeps all your pictures the same size and works well with variable sized images.

ImageCache Presets scaled and cropped

ImageCache Presets scaled and cropped

and you’ll also want to change the link output to reference your term field.  Any questions or comments welcome, take care and have a great day.

Replace the link with your term field

Replace the link with your term field

Apr 16 2012
Apr 16

The more I use the activity module the more I like it.  It is a simple module that does one thing well = it keeps track of what is happening on the site, and it leverages other modules like core Triggers, Views, and Tokens to create a useful intuitive activity page.  In our use case we wanted to have the ability to track certain roles working with certain nodes – and having the output styled in views is just icing on the cake.


To start with you’ll need to go to /admin/build/activity/create and add activity templates.  Because of how this module was designed I believe it would be pretty simple to extend this module to monitor almost anything.. we just needed to know when certain roles updated or created certain node types, so that was pretty simple.

Setting up an activity template

Setting up an activity template

The use of tokens in the message display section makes for readable and useful messaging

Add tokens for readability

Add tokens for readability

After creating your template(s) you now need to go over to views and add an “Activity” type view – here again – it’s just so cool that you’re extending what is already there and working well… it just makes for an easier time + one less thing to learn!

Adding the Activity in Views

Adding the Activity in Views

With the View going the rules are pretty much the same as for all views – it’s worth noting that if you want to filter by User Role you’ll need to add the “Activity: User” relationship – this adds all of the typical user fields to your filters as well as your fields.  Likewise if you need more information on the nodes you may add the Activity: Node relationship – it’s not always that important since many of the information may be stored in the tokens.  It’s nice to know that you may access those fields if you need to though – just add the relationship and fields + filters as desired.  Nice work on this module folks = well thought out and well implemented.  Thanks!

Setting up the activity views

Setting up the activity views

The final outcome = another view

The final outcome = another view

Apr 12 2012
Apr 12

We wanted to pass information to a view from a node and have the results pulled together inline.  Using node reference CCK fields to pass our arguments along w are able to make the edit and review process simpler for our team.  For the Quick video here is your link http://www.youtube.com/watch?v=TdrugF9pAfU

Adding the token for a referenced node

Adding the token for a referenced node

We also set the build mode to “Edit page” as well as “Full Node” for convenience in editing.

Views Attach build modes

Views Attach build modes

now we can add all the fields we need from the referenced node and have them appear in your full node and node edit pages.

We have chosen to use a token from the node – a node reference field – as our argument that we pass along to the view.

The view loaded into the content type

The view loaded into the content type

The views is loaded into the content type which makes it pretty simple to organize.

The final output has our tabled view inserted into our content = yay ;)

Attached table view in our content

Attached table view in our content

Mar 31 2012
Mar 31

Views Or allows you to define filter blocks with multiple alternatives in drupal views.

for the video inclined here’s the two minute walk through – http://www.youtube.com/watch?v=pWE78xQK3Z0

A bit sleepy this morning and forgot coffee.  It shows, I know.

Use case: we have some old data – some of it comes in with the number 0 meaning blank, some of the rows contain the word NULL and some of the rows are empty. All three variations mean the same thing. We want to create a single view with all three options open – if it’s the number 0 OR the word NULL or simply empty.

Views Or adds to your filters options

Views Or adds to your filters options

Installation and usage: Install as usual – obviously you’ll need views installed…. To use you will need to add all three of the new fields into your filters block. First will be the “Begin alternatives” then the “Next alternative between your filters, (repeated for as many alternatives as you need) and lastly you’ll need to “end alternatives.”

Views or fields in the filters area

Views or fields in the filters area

All the pretty fields need to be reorganized

All the pretty fields need to be reorganized

As a lazy lad I like to just add them all at once and then move the options around afterwards, do whatever your gods tell you to…

The final views or arrangement

The final views or arrangement

Programmatically what this has achieved is

AND ((UPPER(node_data_field_er_acq_uid.field_er_acq_uid_value) = UPPER(‘NULL’)) OR (node_data_field_er_acq_uid.field_er_acq_uid_value IS NULL))

In any case it’s a pretty simple module – installs well and saved us from cruddy data. Merci a bunch to all

Mar 28 2012
Mar 28

Views field view allows you to pass an argument to a view and return results.

For the video inclined here’s the 2 minute feed http://www.youtube.com/watch?v=dUoL_ARPLcI

Use Case:  we’ve got a lot of variables being tallied in different views.  Views in general seems an awkward tool for tabulating values.  Tools like views calc and group by work ok for getting a sum or count here and there, but try mixing several together in one view and the errors start to fly.  May just be I don’t have my head around them, in any case they are not intuitive.   For a given organic group node I need to know how many posts there are total (not a problem, that field comes with organic group views integration), and how many of these posts have been completed in a certain way.

Views field view added to fields in view

Views field view added to fields in view

Choose a view and add fields from the view as argument tokens

Choose a view and add fields from the view as argument tokens

Creating this separate view is simple, but now I want to link the main view to the small view tally.  Views Field View let’s you pick a view and pass it an argument to filter the results per row.  In this case we load a node id into our main view and pass the argument over to the Views Field View – creating a new field with the correct info on a per row basis = Nice!

The only thing that doesn’t work is that it does not actually load this view in the order the rows are listed.  Because of this it does not work well with Views PHP.  Although the rows are listed in this global php field the actual data is not available because for some reason the field is not loaded in the order one might imagine it to be.   It’d be really handy to be able to use these variables in calculations, however it’s just not obvious at all how to do that.

Views PHP and VIews Field View don't work together

Views PHP and VIews Field View don't work together


Mar 23 2012
Mar 23
Custom apachesolr results

Custom apachesolr results

We’ve been getting some questions about how we customize our apachesolr results by content type.  The answer:  very simply.  We use views php.  Views php allows us several new field type in views including a place to put custom PHP. It also has the advantage of allowing you to pull in variables already loaded into your view.  Thus while it is possible to write queries to the database in the php area, it is not necessary.  Moreover if you have need to tables not typically available in views you may use the data module to gain access.

For the video inclined here is a link to our 2 minutes on views php http://www.youtube.com/watch?v=cAehdUVrVDs

While there are several fields available in the Views PHP field I tend to use the output field area.  To hide the actual complete results

Using available variables in views php

Using available variables in views php

this makes it so that you can add a few lines of logic and have the results better reflect the information.  For our librarians we output their profile picture linked to their email address, for our web results we provide link descriptions etc.

Excluding results from display allows the results to be available to views php without cluttering the patrons view

Excluding results from display allows the results to be available to views php without cluttering the patrons view

Just remember to exclude your fields from display so that the user isn’t presented with  hundreds of fields and you’re good to go.

Mar 19 2012
Mar 19

Note to all:  Feeds doesn’t seem to connect with user profiles http://drupal.org/node/1060230 = stay tuned for the fix in part 2 (hint use user import also)

Organic groups is a great way to organize groups and cascade permissions – in this example we tested against 45,000 users in 3 different roles going in to 500 groups.

While Node import did the heavy lifting for the group node creation feeds came ahead for user imports.  Node Import just has a few more settings for group node creation than does feeds.

This project came about as a conversion from an unmaintainable drupal 6 site that we converted into off-the-shelf drupal.
Modules used:

Feeds Tamper to assign multiple groups from a signle field   group node id's and role assignments in a spreadsheet

group node id's and role assignments in a spreadsheet

Feeds Tamper to assign multiple groups from a signle field

feeds tamper for role id

feeds tamper for role id

Something to remember with feeds – it can get a bit wonky over file line endings and encoding – more so than node import from what I’ve seen. These are the settings that have worked for me coming from mac excel -> open in text wrangler and resave with windows cr/lf and utf-8 encoding

Feeds Settings

Feeds Settings

The settings are pretty straightforward.

A few things to remember before you upload:

  • if you plan on making node aliases do that before you upload – views bulk operations is slower than molasses updating url aliases.  Same goes for auto node titles if you’re using them.
  • In general every node reference or user reference you have is going to slow something up
text wrangler settings for feed import

text wrangler settings for feed import

Feed mappings

Feed mappings - remember to have a unique id in there

  • organic groups access permissions settings Permissions settings

Modules tested: although a lot of modules were tested not all really made the cut. Some because they didn’t really seem useful in our case. Others (organic subgroups) because I really couldn’t get a response on IRC or the forums about how to ingest them en masse – if anyone has done this please feel free to chime in) organic groups access permissions settings

  • User Import – it only allows you to map your users to a single group.  Thankfully feeds tamper allowed us to map multiple groups per user and group post
  • User Import for Organic Groups – these two seemed promising, however UI4OG only allows the users to be mapped to one group… no good for our use case
  • OG Subgroups – ok – wanted to see this in action, never really got it working at least not through the import interface- might make a good use case for using the migration modules
  • Node Access by User Reference - I really like a lot of Node one’s ideas, and Johan Falk in particular comes up with some very clever ways of re-arranging drupal mods – so I thought I might give this intuitive technique a go – the caveats mentioned in his post indeed became insurmountable – with the need to have 16 user references in each group post my virtual machine came grinding to a halt.
Mar 13 2012
Mar 13

Note to all:  this module is majorly messed up – am leaving this warning here until http://drupal.org/node/1494820 is resolved – briefly the template files get deleted and the output becomes munged after ~1,000 pdfs are generated.   Also while some of my early tests were cranking ~100-200 pdfs per minute we’re seeing results now in the 3-5 per minute making this module unusable for large scale projects. here’s a patched version for D6views_pdf-patched

Views PDF allows you to output the results of a view onto a PDF. You can even upload an existing PDF to use a background template – pretty cool.   For the Audio Visually inclined here’s your 3 minutes of fun. http://www.youtube.com/watch?v=PszjYSq7xQQ

Use Case: Generate 40,000 PDF’s with a user’s name on them and some other predefined fields and make these files accessible to the user for download. 

Problem: creating the PDF’s on the fly is slow – so we need to have them all pre-generated

Solution: Used Cottser‘s writeup here http://drupal.org/node/1415380#comment-5701968 and modified it to be a rule set instead of a rule – with argument type node. Under that added one rule that ended up looking like this

Rule set for saving PDF's to server

Rule set for saving PDF's to server

As you can see the rule requires a pdf view – so hopefully you’ve already got one of them here are some shots of mine

PDF View

PDF View

and here are

views pdf argument settings

views pdf argument settings

the argument settings.  Now earlier we mentioned that the pdf needs to be accessible to users, so back over in our rule set we take an action – we load a referenced user (the user is referenced in the node itself through a user reference cck field) so our final rule set ends up looking like this

Final look of Rule Set

Final look of Rule Set

Ok – almost finished.  Now that we have a rule set we need to add this to a views bulk operation view so that we can “do the deed.”

I like Improved Admin because it gives you a nice starting point for working with Views Bulk Operations.. use whatever brings you the greatest joy.

PDF Save Rule Set in Views Bulk Operations

PDF Save Rule Set in Views Bulk Operations

Once your Rule Set is added to your VBO you may use it with any set of nodes you like.  On our VMWare machine we were churning ~200 pdfs per minute, not too shabby.

Mar 12 2012
Mar 12

A few weeks ago after looking at the taxonomy views integrator a reader posted about how helpful sub path alias is combined with url alter and of course pathauto.   Full Disclosure: tested path alias extended, it seems more robust, but must have missed something as it didn’t flow right off the shelf…

Use Case:  when a user logs in I want them to go to a URL based on their user ID and immediately begin editing the URL.  Pathauto will give you token replacements to do stuff like survey/[user:uid] however when you go to edit the node it typically reverts back to node/81948794387/edit making it difficult to reference.  You could use something like drupal_get_normal_path($path); to do this, but that's not going to make it pretty on the url line, and it's just kinda messy.

A rules based page redirect to an edit page - no node lookups needed

For those interested in a video it's here - no reason to get too excited though - it really does just work - tested against 40,000 nodes and they're all happy, you should be too... http://www.youtube.com/watch?v=t6Touamrvk0

Other niceties – your user/username/edit pages are now working, your mother sent you cookies and it’s Monday morning.  Get some.

Mar 06 2012
Mar 06

Last Thursday we had the opportunity to meet up wth Bryan Ollendyke aka btopro to discuss his flagship product ELMS, as well as numerous other modules – Accessible Content for making more standards compliant sites, CDN for speeding up sites, Organic Groups for working with students and classes, Spaces, database tuning, and many more.  You’ll want to skip to ~minute 8 or 9 where we worked out our technical difficulties.  Ask any questions in the blog and I’ll do my best to help answer.  Specifically settings for CDN are covered as well as use cases and other general high level approaches to development.   Note that link goes to a JNLP blackboard elluminate session – you can exand the size of the window pretty easily in there


apologies for the


Feb 24 2012
Feb 24

Create a rule set and add an argument to receive from your view

There are numerous ways to clean up old posts. Node Expire + Rules is one way, however it requires the user to specify an expiration date, another module, and in that sense is neither intuitive nor efficient. Using Rules and Views together via rules + views integration there’s a great way use the data that has already been entered.

For the video folks here’s your 2 minute rundown of what is up:

1) Create the rule set to handle the data. Add a content argument – you’ll be passing the node ID’s over to Rules for the actions to take place

2) Inside the rule set add a rule – no conditions are needed, just add the action to unpublish the nodes

Here's what your final rule should look like

3) The final rule should look similar to the image at right

4) Once you’ve got your rule set up it’s time to create the view that will send information over to the rule.  Assuming you have rules + views integration and Views Periodic Execution installed you’ll now be able to execute your views on a cron tab

5) In views you’ll need to set the periodic execution phase, the rule set, and of course set up your filters as per usual and MOST IMPORTANTLY include the NID (so that rules knows which nodes to act on)

Select your rule set and pass the node id for your argument

6) The Views Periodic Execution mod by Stephen Jones aka darthsteven

Set the periodic execution phase

7) and of course you need to choose the rule set unto which the little nids may argue and achieve action.  Once you pick your rule set the available arguments’ll appear – any fields you have elected to have appear in your view are available to activate the rule.

Create a rules executor view and send it to your rule set


About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web