Oct 29 2019
Oct 29

For DrupalCon Amsterdam, Srijan ran a competition with the prize being an Apple Watch 5. It was a fun idea. Try to get a screenshot of an animated GIF slot machine showing 3 matching logos and tweet it.

Try your luck at @DrupalConEur Catch 3 in a row and win an #AppleWatchSeries5. To participate, get 3 of the same logos in a series, grab a screenshot and share it with us in the comment section below. See you in Amsterdam! #SrijanJackpot #ContestAlert #DrupalCon

I entered the competition.

I managed to score 3 of the no logo logos. That's gotta be worth something, right? #srijanJackpot

The competition had a flaw. The winner was selected based on likes.

After a week I realised that I wasn’t going to win. Others were able to garner more likes than I could. Then my hacker mindset kicked in.

I thought I’d find how much 100 likes would cost. A quick search revealed likes costs pennies a piece. At this point I decided that instead of buying an easy win, I’d buy a ridiculous number of likes. 500 likes only cost 7USD. Having a blog post about gaming the system was a good enough prize for me.

Receipt: 500 likes for 7USD

I was unsure how things would go. I was supposed to get my 500 likes across 10 days. For the first 12 hours I got nothing. I thought I’d lost my money on a scam. Then the trickle of likes started. Every hour I’d get a 2-3 likes, mostly from Eastern Europe. Every so often I’d get a retweet or a bonus like on a follow up comment. All up I got over 600 fake likes. Great value for money.

Today Sirjan awarded me the watch. I waited until after they’ve finished taking photos before coming clean. Pics or it didn’t happen and all that. The insisted that I still won the competition without the bought likes.

The prize being handed over

Think very carefully before launching a competition that involves social media engagement. There’s a whole fake engagement economy.

Share this post

Jan 08 2014
Jan 08

Bootstrap is a great Drupal theme that makes it so your form elements and other Drupal things get output with proper Twitter Bootstrap CSS attributes.  One downside to it is that the popular Webform module has elements that don't get styled by the Bootstrap Drupal theme and then they look like unstyled form fields.

To fix it, go to Bootstrap's theme/process.inc file.  Inside it, add 'webform_email' to the 'types' array in the _bootstrap_process_input() function.  This will style Webform's special email field.  Other fields likely have different types.  The reason it doesn't get styled is because the 'types' array is coded to look for only default types and not the special ones that Webform is using.

If you want to see what the #type is on an element, I recommend installing the Devel module and calling "dpm($element);" inside the theme/alter.inc bootstrap_element_info_alter() function.  This will output all of the elements on your current Webform.

Happy Bootstrapping!

Oct 14 2012
Oct 14

When I was unable to post to Twitter from my blog tonight, I found a very new thread on drupal.org discussing the issue: the Twitter API changed again!

I am only using the Twitter module on Drupal 6 sites right n   ow, I took two of the fixes and combined them in a patch for the 6.x-3.0-beta9 version. Then MinhH submitted new code that only requires a change in one place.

here's my latest patch for the 6.x-3.0-beta9 version

since the Twitter API is constantly changing, and the module is not very stable, always read the thread carefully to make sure you are applying the latest patch! Others have cleaned up the code I added from MinhH, and created a patch for the 6.x-3.x-dev branch which makes sense (however I suspect that 6.x-3.0-beta9 may be ahead of the -dev branch, and it's not a branch provided in the project's Github). Anyway, Open Source rocks, Murray!

and because my Twitter RSS feed URL was also broken, I found this post

https://dev.twitter.com/discussions/844

which provides an update to the URL format:

https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=decibelplaces

Jul 04 2012
Jul 04

As a website is being developed, often times it is useful to have a server set up where clients can view the website in development, for project manager to gauge the progress, or simply just for making sure each commit does not break the site! In order to achieve this, the server must pull codes from the latest release branch, for clients viewing, or development branch, for internal purposes. Having to manually do this each time can be quite a burden! In this tutorial, we will set up a way for the codebase to be deployed to the server automatically on each new commit. Our choice of version control system will be Git. However if Git is not the version control system of your choice, then even though the codes posted here won’t be directly applicable to other version control systems, the ideas behind them should still be useful!
A PHP deployment script, is used to automate the deployment process after code changes are pushed up to a repo. The script handles the pull actions for the hosting servers, allowing them to pull down the changes without manual intervention. The keyword here is automation. Automation provides savings in time, as well as prevents careless mistakes or oversight. An increase in both efficiency and reliability? No wonder a quick Google search turns up so many examples.

Today we are going to walk through the creation of a simple deployment script, with some powerful features that could be customized to fit in with your work flow.

The Basic

Here is a layout of a basic deployment script that achieves automated code deployment, with the option of specifying which branch to pull from by supplying the bn argument. Simply place this script into the public folder of a vhost on the same server as where your websites are hosted and call it with the full path of your targets website as the subdirectories. For example if you placed the script into a vhost named “post-receive.mysrv.com” and your website is hosted in the directory “/var/www/vhosts/mywebsite.mysrv.com/public”, you would call “post-receive.mysrv.com/var/www/vhosts/mywebsite.mysrv.com/public” which will pull any new updates to your website.

If you find that you keep all your sites in the same vhosts directory, with the same name for the public folder, there is no reason to have to type out the full directory paths every time.

Let’s say you have another website hosted at “/var/www/vhosts/myotherwebsite.mysrv.com/public”, we can specify the default parent path as “/var/www/vhosts/” and the default public folder as “/public”. Now we can call the script for the two different websites by simply typing “post-receive.mysrv.com/mywebsite.mysrv.com”, and “post-receive.mysrv.com/myotherwebsite.mysrv.com”.

<?php
// request_path() is defined at the bottom
$path = '/' .

request_path

();

// Edit this string to reflect on the default location of vhost web roots
// do include the trailing slash
// Example: $default_parent_path = '/var/www/vhosts/';
$default_parent_path = '/var/www/vhosts/';

// The name of the public_html directory
// do include the leading slash
// do not include the trailing slash
// Example: $default_public_directory = '/public';
$default_public_directory = '/public';

// Specify which branch by appending a branch name variable 'bn' to the end of the url
// defaults to 'master' if none specified
// Example: http://post-receive.mysrv.com/mywebsite.mysrv.com?bn=develop
$default_pull_branch_name = 'master';
if (empty($_GET['bn'])) {
  $pull_branch_name = $default_pull_branch_name;
}
else {
  $pull_branch_name = $_GET['bn'];
}

// The idea is if only 1 argument is present, treat that as the /var/www/vhosts/<directory_name>
// and if more than 1 argument is given, treat that as the full "cd-able" path
$args = explode('/', $path);

if (count($args) === 1) {
  $working_path = $default_parent_path . $path . $default_public_directory;
}
elseif (count($args) > 1) {
  $working_path =/. $path;
}

// Do the routine only if the path is good.
// Assumes that origin has already been defined as a remote location.
// We reset the head in order to make it possible to switch to a branch that is behind the latest commits.
if (!empty($working_path) && file_exists($working_path)) {
$output = shell_exec("cd $working_path; git fetch origin; git reset —hard; git checkout $pull_branch_name; git pull origin $pull_branch_name);
echo "
<

pre

>$output</

pre

>";
} /**
 * Returns the requested url path of the page being viewed.
 *
 * Example:
 * – http://example.com/node/306 returns "

node

/306".
 *
 * See request_path() in Drupal 7 core api for more details
 */
function request_path() {
  …
}

Here we discuss many different optional features that either adds more functionality or improves convenience. The code snippets in each example builds upon the previous one and reflects all previous feature additions.

Security key

To make sure your script can only be called by you or those you trust, we are going to add a security key. The security key will be supplied by the user through another URL variable we will call `sk`, and will have to match a pre-set string.

To modify the code we simply add the `sk` URL variable and do a check for the variable that it matches with the security key before continuing. This block of code should go at the very beginning of the page.

// Checks the security key to see if it is correct, if not then quits
// Currently set to static key 'mysecuritykey'
// Example: http://post-receive.mysrv.com/mywebsite.mysrv.com?sk=mysecuritykey
if (empty($_GET['sk'])) {
  header('HTTP/1.1 400 Bad Request', true, 400);
  echo '<pre>No security key supplied</pre>';
  exit;
}
if ($_GET['sk']!='mysecuritykey') {
  header('HTTP/1.1 403 Forbidden', true, 403);
  echo '<pre>Wrong security key supplied</pre>';
  exit;
}

Tags


This is arguably one of the most versatile functionality we can add to the script. By adding the ability to pull commits based on certain tags, you can adjust this script to fit your work flow. For example, you may want a production server to only pull commits that are tagged with the latest version number. For more information on tags and how they work in Git here is a nice succinct description straight from the Git official documentation.

Once again, we had to change the shell commands in order to both retrieve tags information and to pull the appropriate commits. You can setup different tag rules by altering the regular expressions and the comparison done between tags. For example, a rule to only pull commits with tags containing the keyword beta. You can also set different rules for different branches by a switch-case structure based on the `bn` URL variable.

// Do the routine only if the path is good
if (!empty($working_path) && file_exists($working_path)) {

  // Fetch and check version numbers from tags
  $preoutput = shell_exec("cd $working_path; git fetch origin; git fetch origin --tags; git tag");
  // Finds an array of major versions by reading a string of numbers that comes after '7.x-'
  preg_match_all('/(?<=(7\.x-))[0-9]+/', $preoutput, $matches_majver);
  // Finds the latest major version by taking the version number with the greatest numerical value
  $majver = max($matches_majver[0]);
  // Finds an array of minor versions by reading a string of numbers that comes after '7.x-{$majver}.'
  // where {$majver} is the latest major version number previously found above
  preg_match_all('/(?<=(7\.x-' . $majver . '.))[0-9]+/', $preoutput, $matches_minver);
  // Finds the latest minor version by taking the version number with the greatest numerical value
  $minver = max($matches_minver[0]);
  // Concaternate version numbers together to form the highest version tag
  $topver = '7.x-' . $majver . '.' . $minver;
  echo "<pre>The latest version detected is version $topver</pre>";

$output = shell_exec("cd $working_path; git fetch origin; git reset --hard; git checkout $pull_branch_name; git fetch origin $pull_branch_name; git merge tags/$topver;");
echo "<pre>$output</pre>";
}

Drush


If you’re using Drupal as your CMS, chances are you’re using Drush. Here we will integrate the script with Drush clear cache commands. The idea is the same as the above features; we start by defining an URL variable `cc` as our drush command variable. The user can execute predetermined drush commands. Clearing cache will clean out all the cached data and force the website to rebuild itself, it is important after code changes in order for those changes to be reflected on the website, especially for the theme layer.

—Update—
As Dustin pointed out in the comments below, there is often a need to perform a database update, and for those that work with features as a tool for site building, running a feature revert is a must on any update. The addition of a few new URL variables will give us the option to do so.

if (!empty($_GET['cc'])) {
  switch ($_GET['cc']) {
    case 'all':
      shell_exec("cd $working_path; drush cc all");
      break;
    case 'cssplusjs':
      shell_exec("cd $working_path; drush cc css+js");
      break;
    case 'cssminusjs':
      shell_exec("cd $working_path; drush cc css-js");
      break;
  }
}
if (!empty($_GET['up'])) {
  shell_exec("cd $working_path; drush updatedb -y");
}
if (!empty($_GET['fr'])) {
  shell_exec("cd $working_path; drush fra -y");
}

Github integration


If your repository is hosted on Github, you can make use of their POST callback service by going to Admin>Service Hooks and adding a post commit webhook with the url of the script, complete with the security key and any other arguments. Github will then call the script whenever a new commit is pushed to the repo. BitBucket similarly offers a post commit webhook.

Twitter integration


The Github POST service sends along a payload object with quite a bit of useful information with the POST call to our deployment script. By including the twitter-php library by David Grudl we can setup a twitter account to tweet updates with information from the POST payload.

You can find the source files as well as the documentation on how to setup your twitter account here on Github.

To include this in our script we simply add the below block of code, with the
require_once dirname(__FILE__) . '/twitter-php/twitter.class.php';
//insert appropriate values for the keys and tokens
$consumerKey = 'consumerkeygoeshere';
$consumerSecret = 'consumersecretgoeshere';
$accessToken = 'accesstokengoeshere';
$accessTokenSecret = 'accesstokensecretgoeshere';// Tweet on success the total number of commits, latest commit number, which repository and branch received new commits, and who pushed the commit.
if (!empty($_POST) && !empty($_POST['payload'])) {
$payload = json_decode($_POST['payload']);
if (!empty($payload)) {
$twitter = new

Twitter

($consumerKey, $consumerSecret, $accessToken, $accessTokenSecret);
$last_commit = end($payload->commits);
$twitter->send('[' . $payload->repository->name . ':' . $pull_branch_name . '] ' . count($payload->commits) . ' commit(s) deployed. Last commit: ' . end($payload->commits)->id . ' by ' . end($payload->commits)->author->name . end($payload->commits)->author->email);
}
}

Here is the script in its entirety

<?php
require_once dirname(__FILE__) . '/twitter-php/twitter.class.php';
$consumerKey = 'consumerkeygoeshere';
$consumerSecret = 'consumersecretgoeshere';
$accessToken = 'accesstokengoeshere';
$accessTokenSecret = 'accesstokensecretgoeshere';
$path = '/' .

request_path

();
$default_parent_path = '/var/www/vhosts/';
$default_public_directory = '/public';

if (empty($_GET['sk'])) {
  header('HTTP/1.1 400 Bad Request', true, 400);
  echo '<pre>No security key supplied</pre>';
  exit;
}
if ($_GET['sk']!='mysecuritykey') {
  header('HTTP/1.1 403 Forbidden', true, 403);
  echo '<pre>Wrong security key supplied</pre>';
  exit;
}

$default_pull_branch_name = 'master';
if (empty($_GET['bn'])) {
  $pull_branch_name = $default_pull_branch_name;
}
else {
  $pull_branch_name = $_GET['bn'];
}

$args = explode('/', $path);

if (count($args) === 1) {
  $working_path = $default_parent_path . $path . $default_public_directory;
}
elseif (count($args) > 1) {
  $working_path =/. $path;
}

if (!empty($working_path) && file_exists($working_path)) {
  $preoutput = shell_exec("cd $working_path; git fetch origin; git fetch origin --tags; git tag");
  preg_match_all('/(?<=(7\.x-))[0-9]+/', $preoutput, $matches_majver);
  $majver = max($matches_majver[0]);
  preg_match_all('/(?<=(7\.x-' . $majver . '.))[0-9]+/', $preoutput, $matches_minver);
  $minver = max($matches_minver[0]);
  $topver = '7.x-' . $majver . '.' . $minver;
  echo "<pre>The latest version detected is version $topver</pre>";

  $output = shell_exec("cd $working_path; git fetch origin; git reset --hard; git checkout $pull_branch_name; git fetch origin $pull_branch_name; git merge tags/$topver;");
  echo "<pre>$output</pre>";
}

if (!empty($_GET['cc'])) {
  switch ($_GET['cc']) {
    case 'all':
      shell_exec("cd $working_path; drush cc all");
      break;
    case 'cssplusjs':
      shell_exec("cd $working_path; drush cc css+js");
      break;
    case 'cssminusjs':
      shell_exec("cd $working_path; drush cc css-js");
      break;
  }
}

if (!empty($_GET['up'])) {
  shell_exec("cd $working_path; drush updatedb -y");
}

if (!empty($_GET['fr'])) {
  shell_exec("cd $working_path; drush fra -y");
}

if (!empty($_POST) && !empty($_POST['payload'])) {
  $payload = json_decode($_POST['payload']);
  if (!empty($payload)) {
    $twitter = new Twitter($consumerKey, $consumerSecret, $accessToken, $accessTokenSecret);
    $last_commit = end($payload->commits);
    $twitter->send('[' . $payload->repository->name . ':' . $pull_branch_name . '] ' . count($payload->commits) . ' commit(s) deployed. Last commit: ' . end($payload->commits)->id . ' by ' . end($payload->commits)->author->name . end($payload->commits)->author->email);
  }
}

 /**
 * Returns the requested url path of the page being viewed.
 *
 * Example:
 * – http://example.com/node/306 returns "node/306".
 *
 * See request_path() in Drupal 7 core api for more details
 */

function request_path() {
  static $path;

  if (isset($path)) {
    return $path;
  }

  if (isset($_GET['q'])) {
    // This is a request with a ?q=foo/bar query string. $_GET['q'] is
    // overwritten in drupal_path_initialize(), but request_path() is called
    // very early in the bootstrap process, so the original value is saved in
    // $path and returned in later calls.
    $path = $_GET['q'];
  }
  elseif (isset($_SERVER['REQUEST_URI'])) {
    // This request is either a clean URL, or 'index.php', or nonsense.
    // Extract the path from REQUEST_URI.
    $request_path = strtok($_SERVER['REQUEST_URI'], '?');
    $base_path_len = strlen(rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/'));
    // Unescape and strip $base_path prefix, leaving q without a leading slash.
    $path = substr(urldecode($request_path), $base_path_len + 1);
    // If the path equals the script filename, either because 'index.php' was
    // explicitly provided in the URL, or because the server added it to
    // $_SERVER['REQUEST_URI'] even when it wasn't provided in the URL (some
    // versions of Microsoft IIS do this), the front page should be served.
    if ($path == basename($_SERVER['PHP_SELF'])) {
      $path = '';
    }
  }
  else {
    // This is the front page.
    $path = '';
  }

  // Under certain conditions Apache's RewriteRule directive prepends the value
  // assigned to $_GET['q'] with a slash. Moreover we can always have a trailing
  // slash in place, hence we need to normalize $_GET['q'].
  $path = trim($path, '/');

return $path;
}

That should be enough to get you started on a deployment script. Let us know in the comments if you have any questions, or any other ideas you might have for an improved script!

Credits goes to Brandon Shi, our senior developer here at ImageX Media, for the ideas behind much of these scripts.

Jun 09 2012
Jun 09

What do the following have in common?

  1. Church
  2. Foster Care
  3. Charter Schools
  4. Drupal

I've been involved with all four - and they all require members of the community to be Of Service. Without those, who are willing to take time to to benefit a greater good, a community collapses.

I learned about service from a very young age. I became a chorister in St. Matthew's Church when I was 6 or 7 years old. This church choir is an amazing place, producing some of the finest choral musicians of my generation. I had the good fortune of rubbing shoulders with the likes of Gerald Finley, Matthew White, and Daniel Taylor. As a chorister there, I spent every Tuesday and Wednesday afternoons practicing with the other boys. Friday evenings were devoted to full practice with the entire men and boys sections. Then the choir would sing two services on Sundays - one in the morning and one in the evening. The excellence would not have emerged, without those willing to sacrifice personal time to a greater good.

As an adult, I continued this legacy of service by engaging the Foster Care System. The Foster Care System tries to protect/support/save the most vulnerable members of our society. The stories you hear are heartbreaking. This ultimately led to the building of Trauma Adoption, a site devoted to supporting Foster Parents and Adoptive Parents of children who have suffered trauma at a young age. Children would remain parentless, without support, aging out at 18 without those willing to sacrifice personal time to a greater good.

Charter Schools are an interesting entity. They are part of the public school system, but are often instantiated by parents who feel the overall public school system has not served the needs of their children adequately. The formation requires a "charter" which outlines accountability that the school must adhere to in exchange for the partial autonomy it receives from the the system. Charter Schools have governance from a Board of Directors who look at high level issues like HR when they are escalated, the budget, and school adherence to policies. I've been on the Parent Teacher Association Board at one school. I was recently elected to the Crown Pointe Academy Board of Directors. These are volunteer Boards. The school would cease to function properly without proper governance without those willing to sacrifice personal time to a greater good.

How does this relate to Drupal? Understanding the idea that Drupal is a community of Developers, Strategiests, Business People, Project Managers, and so much more is the beginning point. Many of these people are giving of their own time in benefit to the overall community. There is an altruistic element to contributing/sharing/helping - but there are also lots of profoundly good personal reasons to:

  • Network
  • Share

social networkAt the end of May 2012 I announced that I had just had my last day with Examiner.com and that I was looking for new work.

The graph to the left is from Klout, a site that looks at your overall activity and the activity around you in social networks and assigns you a score.

Klout writes about itself:

Klout measures influence online
Our friendships and professional connections have moved online, making influence measurable for the first time in history. When you recommend, share, and create content you impact others. Your Klout Score measures that influence on a scale of 1 to 100.

The Standard for Influence
When we're measuring your influence there's no room for error. We have a killer team of scientists and engineers working everyday to ensure continued accuracy and make the Score clear and actionable. We hold ourselves to a high standard and we hope you will too.

The blue arrow points to the moment in time when I made my announcement. Two days later I had finished a blog post and posted that. That stratospheric increase over about 24 hours represented reaction to two messages that I sent out on Twitter, Facebook, and Google+.

Matthew's Tweets

What occurred was pretty phenomenal. Members of the Drupal community grabbed ahold of the messaging and began to distribute it across their networks, effectively amplifying the message exponentially. Within a few hours, I started getting leads to new jobs. I had a first informal interview within a few hours of my first social message.

On June 2nd, I finished up a post that did a brief retrospective of my time at Examiner.com but also talked about my upcoming job search. I used Bit.ly on links that I passed out and included on the post. Bit.ly allows you to track click throughs and the like on URLs that you shorten using the service.

Bitly says this about themselves:

bitly is the easiest and most fun way to save, share and discover links from around the web. We call these links bitmarks, and you can use bitly to remember, curate and share them.

bitly is available via our website, browser extensions, mobile web, and numerous third-party tools integrated with our open public API. bitly also powers more than 10,000 custom short URLs and offers an enterprise analytics platform that helps web publishers and brands grow their social media traffic.

This graph shows analytics for the shortened URL I distributed.

My Bitly ReferralsWhile bitly isn't a perfect tool - others can come to posts from other locations - it does give you a sense of click through traffic and URL sharing with the particular shortened URL you've created.

I found it interesting that the number one referral of this shortened URL was Email Clients, Instant Messaging, AIR Apps, and Direct Sharing of the URL. People saw the URL, presumably with context from an email share or in skype or IRC and so forth and *chose* to click through to the post from there.

Twitter then came in second as a source for folks finding the post. There was recirculation from my site itself - the shortened URL was at the top and bottom of the post encouraging others to share it. Then came in facebook, and then Drupal.org itself.

Looking at Google Analytics, there was an interesting flip in in order of referral traffic - Google saw the following order:

  1. (direct) / (none)
  2. drupal.org / referral
  3. google / organic
  4. t.co / referral 113
  5. dogstar.org / referral
  6. google.com / referral
  7. facebook.com / referral

So, what's the point to all of this and how does it relate to Service and Community? I don't believe that without serving the community and thus creating the relationships/network I've developed over the last five and half years, ANY of this would have been possible. People know that I care about our ecosystem, because I spend the time giving to our ecosystem. Such an outpouring of help from such an amazing community wouldn't have have happened. Because I care, you care. I am humbled and gratified that so many of you wanted to help me out.

My take away?

Service + Networking + Community = Call to Action for a Common Goal
Thanks for making me the goal this time round. I promise to remain of service to the community.
May 01 2012
May 01

Posted May 1, 2012 // 0 comments

 Last summer, while sitting in the East Room of the White House, I discovered that Twitter can help me hear. This experience shed light on the parallels to the principles of Open Source technology: You never know how someone may find an innovative use for existing solutions for social, human, and/or environmental good.

When technologists like Jack Dorsey were first developing Twitter, I don’t think he stood up in front of venture capitalists and said, “This thing! It’s going to help a deaf girl in Washington, D.C., one day hear the President of the United States!”

As some background—I’m deaf and hear with a cochlear implant. It’s an awesome piece of technology that helps me hear and communicate with everyone. Because of this, technology has become a huge part of who I am. I’m constantly finding new and improved uses for technology around us to bridge the accessibility gap.

First-Ever Twitter Town Hall

Last summer, I was invited to the first-ever White House Twitter Town Hall event with President Obama and Jack Dorsey. I only had 48-hours notice, not enough time to request and find an interpreter to ensure that I’m getting access to the dialogue between President Obama and @Jack.

This Town Hall event was a really cool concept, in a little “d” democracy kind of way—anyone canObama talking to Jack Dorsey Tweet a question to President Obama with the #askobama hashtag (basically “keywords” surrounding an event) and he just may answer it. No one in the audience could. Cool, right?

Back to the story. Once Obama and @Jack started talking, they were telling jokes, laughing, the audience was laughing along, except me. Then Obama started talking about the economy and jobs, you know, the usual. Except, I wasn’t understanding anything.

I felt left out.

Pretty soon after the jokes ended and dialogue started, the LCD screens just next to President Obama projected the “tweeted” questions for Obama to answer. There. I could at least have some context to what he’s talking about – whether it’s jobs, the economy, or healthcare. I still wasn’t understanding what he was saying. I wanted to be able to tweet what he was saying to my network – I wanted to be a reporter since I was there.

And that’s when I had my “ah-ha” moment:

Audience Members as Twitter Reporters

You see, I realized that I’m not the only “reporter” in the room—not in a room full of DC’s notable technology leaders and politicians. A lot of mobile phones were out, people’s heads down staring at the screens – I immediately realized, they were live-tweeting what Obama and @Jack were saying. I quickly took an inventory of all the hashtags surrounding this event and ran a search on my iPhone twitter app, and Bingo! There were tweets that were live-tweets as quotes from President Obama as he answered the questions that were projected on the LCD screens.

@WhiteHouse

 Cut through the Noise

This was a great discovery, however, there were thousands of tweets saying essentially the same thing. I wasn’t about to spend the next hour staring at my phone, filtering through all the tweets, when the President of the United States and the Founder of Twitter were sitting twenty feet in front of me. I noticed that a few Twitter accounts had good, live-quotes – not commentary and reactions – that’s what I needed; I needed someone to give me the information in an unbiased manner so that I could form my own opinions. So I figured that the “White House” and “Twitter TownHall” Twitter accounts would be good ones to follow for the event, as opposed to the thousands of tweeters.

Follow Key Accounts

I created a feed of select Twitter accounts that I felt were providing a good report of the dialogue. This became a “smart captioning” tool of sorts. I rely on closed captions on my TV to understand what the speakers are saying, so this Twitter feed became a “live, smart closed captioning” tool of sorts on my iPhone in nearly real-time. It was a lot better than the alternative: not understanding what’s going on at all.

The best part is, through this experience, the organized live-tweets enabled me to become a participant. It was a great feeling to get in the East Room of the White House.

I told a lot of my deaf friends about that experience, and although most were initially Twitter naysayers, they joined because they, too, wanted to be able to follow along with live dialogue in conference and event settings. Twitter as a “smart closed-captioning” tool was better than nothing at all.

So, viral tools like Twitter are very much like those that are Open Source. You never know how someone will use the technology, especially when it comes to improving products and services. As someone focused on accessibility in open source software for Phase2, I am excited to see how people will leverage the tools they’re building in Drupal for social, environmental, and human good.

In a lot of ways, open source software like the Drupal platform that we build here at Phase2, has the same kind of “disrupt and improve” ethos, and it’s why it’s so exciting to be working with it.

If a feature of a website separates audiences, particularly those with disabilities, Catharine McNally views it with a critical eye to find better solutions. While communicating this to web developers, she works collaboratively towards ...

Feb 28 2012
Feb 28

Posted Feb 28, 2012 // 13 comments

Twitter is one of the most ubiquitous social networks out there. As such, it's not uncommon to see a list of related tweets accompanying an article or blog post to provide additional context into the subject matter at hand.

I recently implemented a Twitter widget on a Drupal site that has a significant amount of traffic, and had to weigh my options careful with regard to several concerns:

  • Twitter might be unavailable at any point
  • The service limits the number of requests that can be made in an hour, and this is based on the requestor's external IP address
  • Fetching and processing incoming feed data in a production environment could be taxing

Here is a list of some of the options I considered, and the associated pros and cons I considered while determining my ultimate solution.

Twitter's Javascript Widget

Twitter offers a free Javascript widget to list all the tweets of a particular user or a user's list.

Pros

This option provides the most real-time display of Twitter content possible, since it is fetching data live from Twitter at the moment of the page request. All processing is done client side, so there is virtually no impact on server performance. Using the widget requires very little technical experience to implement, and doesn't take much time to do so. With some basic development skill, it's possible to alter the list or account used based on taxonomy terms or node fields in order to provide additional context to the tweets displayed. Implementation of this widget is CMS-agnostic.

Cons

Twitter limits requests to its anonymous API to 150 per hour. This sounds like plenty, but if 150 people are in the same office and on the same internal network, the API's request limit will be reached after each person visits one page. For the remaining part of the hour, the Twitter widget will be empty (unless it pulls from the client's browser cache). If Twitter is unavailable, then the widget will also be empty. It may require a small amount of effort with your theme to add the widget and provide any customizations. This option requires that users have Javascript enabled, which may be an issue for 508 compliance.

Twitter Pull Module

There is a useful Drupal module called Twitter Pull that does exactly that -- it pulls Twitter feeds for you and displays the desired tweets in a block.

Pros

This fetches content and caches the last good state, which will prevent blank blocks. There will always be content. It is still possible to use account and/or list overrides based on theme settings, taxonomy terms, node fields, etc. When cached, the page actually renders a little faster in the browser, since the content is already present. Takes little time to enable the module and place the block, and requires the least technical know-how of any of the options here.

Cons

This causes Drupal to have to fetch the content periodically (we can set this cache TTL). This adds (according to tests performed in a development environment) an additional 0.3 seconds of load time when the tweets block is not presently cached. This increase in time is due to Drupal having to make HTTP requests to Twitter. This option also has the ill effect of being slower to update content, vs. the near-real time nature of client-side updates. Since all Twitter requests will come from the web server, the rate limit will apply to all aggregate users instead of each client having their own rate limit -- this will only be a problem if there are a significant number of user+list combinations across the site; hence making multiple uncached requests to Twitter. This will be especially worrisome on shared hosting environments, as the API requests will count against the external IP address of the server of all the web sites, and not just against the individual web site. If multiple sites use the same module, the API requests count against all sites together.

Custom CDN Integration

As the name of the option suggests, this will be an entirely custom implementation, and uses a Content Distributed Network (CDN). This option uses Javascript as in the first option, but proxies the request through the web site's CDN so that Javascript responses will be cached.

Pros

Assuming the CDN is stable, this is the most reliable option in terms of always delivering content. Even if the hourly API limit is reached, the CDN will hold on to the last known good state and serve that until the next hour when new content can be fetched again. CDNs are distributed, so, while some users may be affected by the hourly limit, users in a different region (where the CDN request will come from a different IP address) won't be affected.

Cons

This requires much more customization than any other option, to say nothing of the increase in technical skill required. There may be bandwidth costs associated with serving the proxied Twitter content from the CDN. Content from Twitter will be delayed as the CDN cache TTL (though, short as it may be) will prevent real-time requests from Twitter if the CDN already has cached responses.

As one of our superb Team Architects, Tobby Hagler expertise spans the gamut of technical capabilities -- from interface development to application layer engineering and system architecture.

Tobby’s specialties tend to focus on ...

Feb 27 2012
Feb 27

There are only a few weeks left until DrupalCon Denver, and we’re getting pumped up at Evolving Web for the year’s largest Drupal hangout. With an estimated 4,000 participants in mile high Denver, March 19-23th promises to be a week teeming with exciting sessions, networking and skiing! Four of us will be heading down to Denver, and we hope to see you there. We have lots planned for the conference from contests to sessions to code sprints

Update: We've revised our contest, see evolvingweb.ca/drupalirl for the updated version!

DrupalCon Ticket Giveaway

We have one extra ticket for DrupalCon Denver to give away. If you're a Montreal Drupaler who's planning on attending the Drupal 8 Multilingual code sprint in Denver, give us a tweet @evolvingweb and let us know you're interested!

Photo Contest

Following on the heels of our Drupal Crossword in London, comes our Drupal Celebrity Photo Contest! Hunt down your favourite Drupaler for a photo-op, then tweet the picture with hashtag #drupalceleb for a chance to win a camera! More information and contest rules at evolvingweb.ca/drupalceleb. UPDATE: To address concerns raised about privacy, we've modified our photo contest, now called Drupal In Real Life photo challenge. For more info, see the follow-up blog post Drupalcon Contest, Take Two.

Session: Multilingual Site Building with Drupal 7

Curious about what it takes to integrate multilingual support into your website? I'm co-presenting a session on multilingual Drupal with Florian Loretan from Wunderkraut. We'll introduce you to Drupal’s multilingual architecture and help you get off the right foot when planning your next big project. Our session, Don't Get Lost in Translation: Multilingual Site Building with Drupal 7, will take place on Tuesday, March 20th at 3:45pm in Room 203.

Drupal 8 Multilingual Code Sprint

Gabor Hojsty will be leading a Drupal 8 Multilingual Initiative codesprint on the last day of DrupalCon, as well the two days following DrupalCon. There has been a strong push towards greater multilingual support in Drupal 8, and we want to help as much as possible. We'll be at the sprint, so come by and help make this initiative a success. See Gabor’s post on groups.drupal.org for more information.

Silver Sponsors

We’re excited to sponsor DrupalCon Denver at the Silver level this year! Ever since DrupalCon DC, we’ve been showing our support for the conferences by pledging sponsorship at different levels. Join us in Denver at booth 103, where we’ll be hanging out for the week. Come by, say hello and grab some of our awesome swag.

While we’re busy getting ready for DrupalCon, we hope you’ve had a chance to review the schedule and fit in your favourite sessions. Tickets are still available, so get yours now. See you in Denver!

Sep 16 2011
Sep 16

Posting to Facebook and Twitter

Posted on: Friday, September 16th 2011 by Brandon Tate

Facebook and Twitter modules

Here is a little guide on setting up Facebook and Twitter on your Drupal site. In this implementation, the Drupal user can connect their Facebook and/or Twitter account to their Drupal account. After doing so, the user can post referral links to Facebook or Twitter with the click of a button. Below I will show you how I did this.

Facebook:

1. Download all the things you need. Grab the Drupal Facebook module here (http://drupal.org/project/fb), for this implementation I used the 3.0 version. Be careful, different module versions support different PHP libraries, so choose wisely.
2. Download the PHP library for Facebook here (http://github.com/facebook/php-sdk).
3. Extract the PHP library and put the files at sites/all/libraries/facebook-php-sdk
4. Install the Facebook module.
5. Place this tag (xmlns:fb="http://www.facebook.com/2008/fbml") after your HTML tag in the page.tpl for your site.
6. Now, visit http://www.facebook.com/developers/createapp.php and create an app for your Drupal site to connect with.
7. Once that is complete, add the app information such as label, Facebook App ID and the secret key into the admin page (/admin/build/fb/fb_app_create)
8. Set the Facebook App to be your primary connect application within Drupal here (admin/build/fb/fb_connect)
9. Navigate to your user edit page, you will now see a “Facebook” tab beside “view” and “edit”. Clicking that link will take to you to a page that allows the user to associate their Facebook account with their local Drupal account!
10. Now we can focus on posting to the users Facebook wall.

Posting to Facebook:
1. Below is an example of how I posted a referral link to the users Facebook wall.
2. First we must grab the global Facebook object and try to retrieve the users Facebook session from that object.
3. If that session is valid, we can post to the users Facebook page by using "fb_call_method" and passing the Facebook object as well as the flag "stream.publish" with the message we want to post. In this case, I post a referral link.

function facebook_referral_form_submit($form, &$form_state) {
  global $user;
  $fb = $GLOBALS['_fb'];  
  if ($fb != NULL) {
    $fb_session = $fb->getSession();
    $account = $user;
    if ($fb_session != NULL) {
      $fb->api_client->session_key = $fb_session['session_key'];
      $fb->api_client->expires = 0;
      $node = node_load($form_state['values']['nid']);
      $token = drupal_get_token($account->uid);
      $referral_link = _create_referral_link($account->uid, $node->nid);  
      try {
        $success_data = fb_call_method($fb, 'stream.publish', array(
          'session_key' => $fb_session['session_key'],
          'message' => $referral_link,
        ));
      } catch (Exception $e) {
        //catches exception if stream_publish fb extended permission is not available
        //shows dialog box instead of directly publishing to the Wall
        drupal_set_message('An error occured when posting to Facebook.');
      }
    }
  }
}

Twitter:

1. Download the Drupal Twitter module. For this implementation I used version 3.0-beta4. http://drupal.org/project/twitter
2. Twitter requires Oauth to authenticate users so we need to download the Drupal Oauth module here. It is important to note that version 3.0 is not supported by the Drupal Twitter module so we need to download version 2.02 here http://drupal.org/node/476824
3. Install the Drupal Twitter module. Enable Twitter Actions, Twitter Post and Twitter module.
4. Install the Drupal Oauth module.
5. To enable OAuth based access for twitter, you must register your application at the following location https://dev.twitter.com/apps/new
6. Once the application is registered with Twitter, take the Oauth information provided and enter that into the Drupal admin page (/admin/settings/twitter)
7. Navigate to the users edit page. You will now see a “twitter” tab. Clicking that link takes the user to a form that allows the user to associate the Drupal user account with their Twitter account.
8. Now we can focus on posting to the users Twitter account.

Posting to Twitter:
1. Below is an example of how I posted a referral link to the users Twitter account.
2. First we must grab the twitter account using twitter_get_user_accounts()
3. Next we use twitter_account_load to load the specific twitter account (users can have more then one Twitter account associated to their Drupal account)
4. Then I used twitter_set_status to post the referral link to the Twitter page.

function twitter_referral_form_submit($form, &$form_state) {
  global $user;
  module_load_include('inc', 'twitter');
  $account = $user;
  
  $accounts = twitter_get_user_accounts($account->uid);
  if (!$account) {
    $err_message = t("Unable to find your Twitter account. ") .  l(t('Click here to a Twitter account to your profile.'),'user/' . $user->uid . '/edit/twitter');
    drupal_set_message($err_message, 'warning');
    return FALSE;
  }

  $twitter_account = twitter_account_load($accounts[0]->id);  
  
  $node = node_load($form_state['values']['nid']);
  $token = drupal_get_token($account->uid);
  $referral_link = _create_referral_link($account->uid, $node->nid);  
    
  try {
    $result = twitter_set_status($twitter_account, $referral_link);
    drupal_set_message(t('Successfully posted to Twitter'));
  }
  catch (TwitterException $e) {
    drupal_set_message(t('An error occurred when posting to twitter: %code %error',
      array('%code' => $result->code, '%error' => $result->error)), 'warning');
  }
}

That’s the quick and dirty on how I implemented this solution. Hope this helps someone and if you have any comments, feel free to post away.

Aug 05 2011
Aug 05

Twitter Module is your best friend! This module is very simple to use and works pretty much out of the box!

I recently worked on a site that allowed users to connect with their Twitter account and automatically Tweet when they create new content. The only catch is, each content type would have a different message. The included module Twitter Post is where all the magic happens. The main Twitter module handles all the connection variables, all we need to do is create an App from Twitter and plug in the necessary keys.

read more

Oct 08 2010
Oct 08

I was recently asked to add a simple twitter feed to a Drupal 5 site I built a few years ago. You know the sort of thing - a block in the sidebar with the 5 most recent tweets from the organisation's twitter account. Unfortunately, this turned out to be one of those requests that sounds like it's going to be really easy, but is not (especially on Drupal 5). I came up with a solution which does the job quite nicely, but it's a bit of a hack; I wouldn't do it like this on a new site - but then of course I wouldn't be using Drupal 5.

There are several twitter modules for Drupal, as you'd expect. The twitter module does have a Drupal 5 version, but it's a minimal dev release and doesn't seem to offer much of the functionality of the other releases. The other module which sounded promising was twitter pull but this has no D5 release at all. I had a think about doing a backport, but this was supposed to be a quick favour and with the imminent release of D7 (ahem!), working on Drupal 5 code hardly seems like an investment in the future.

Then I checked, and found that twitter provides RSS feeds for users' timelines, and obviously D5 has a perfectly functional RSS aggregator module built-in. The RSS feed looks something like this:

http://twitter.com/statuses/user_timeline/30355784.rss

...so I set this up as a feed (admin/content/aggregator), and this automatically provided a block. This works okay, but the problem is that each item ends up like this:

<a href="http://twitter.com/mcdruid_co_uk/statuses/26748027524">mcdruid_co_uk: Drupal Quiz module: "action to be preformed after a user has completed Quiz: Ban IP address of current user" - seems a bit extreme, shirley?</a>

...so we end up with the entire tweet being a link, pointing at a page displaying just that one tweet. This is not exactly what I was hoping for.

So here's the slightly nasty hack; like all well-written Drupal modules, aggregator runs its output through a theme function, which can be overridden:

<?php
function theme_aggregator_block_item($item, $feed = 0) {
  global $user;
 
  if ($user->uid && module_exists('blog') && user_access('edit own blog')) {
    if ($image = theme('image', 'misc/blog.png', t('blog it'), t('blog it'))) {
      $output .= '<div class="icon">'. l($image, 'node/add/blog', array('title' => t('Comment on this news item in your personal blog.'), 'class' => 'blog-it'), "iid=$item->iid", NULL, FALSE, TRUE) .'</div>';
    }
  }
 
  // Display the external link to the item.
  $output .= '<a href="http://mcdruid.co.uk/content/simple-twitter-feed-on-drupal-5/'. check_url($item->link) .'">'. check_plain($item->title) ."</a>\n";
 
  return $output;
}
?>

We wouldn't necessarily want to mess with the output of feeds other than this twitter one, so we need a way of identifying items from this feed. I noticed that twitter's RSS feed prepends the twitter username to each post, so we can use this to identify them.

I borrowed a function from the twitter module which deals with twitter @usernames and #hashtags. After that, running the tweet through the default input filter strips out potential nasties, and also converts URLs to links (assuming you have this switched on in your default input filter). The item includes a timestamp, so I used that to append a date/time. I didn't want the blog it stuff, so my theme code (in template.php) ended up looking like this:

define('ORGNAME_TWITTER_USERNAME',          'orgname');
 
/**
 * theme aggregator items
 */
function mytheme_aggregator_block_item($item, $feed = 0) {
  // horrible hack, but catch items which are part of the twitter feed
  $twitter_username = variable_get('orgname_twitter_username', ORGNAME_TWITTER_USERNAME);
  $tweet_prefix = "$twitter_username: ";
  if (strpos($item->title, $tweet_prefix) === 0) {
    // chop the username part from the start
    $tweet = substr($item->title, strlen($tweet_prefix));
    // @usernames
    $tweet = _mytheme_twitter_link_filter($tweet);
    // #hashtags
    $tweet = _mytheme_twitter_link_filter($tweet, '#', 'http://search.twitter.com/search?q=%23');
    // add date
    $tweet .= ' <span class="date">(' . format_date($item->timestamp) . ')</span>'; 
    // filter
    $tweet = check_markup($tweet);
    $output = trim($tweet);
  }
  else {
    // default implementation
    $output = '<a href="http://mcdruid.co.uk/content/simple-twitter-feed-on-drupal-5/'. check_url($item->link) .'">'. check_plain($item->title) ."</a>\n";
  }
  return $output;
}
 
/**
 * stolen from twitter module (for D6)
 *
 * This helper function converts Twitter-style @usernames and #hashtags into 
 * actual links.
 */
function _mytheme_twitter_link_filter($text, $prefix = '@', $destination = 'http://twitter.com/') {
  $matches = array(
    '/\>' . $prefix . '([a-z0-9_]{0,15})/i',
    '/^' . $prefix . '([a-z0-9_]{0,15})/i',
    '/(\s+)' . $prefix . '([a-z0-9_]{0,15})/i',
  );
  $replacements = array(
    '><a href="http://mcdruid.co.uk/content/simple-twitter-feed-on-drupal-5/' . $destination . '${1}">' . $prefix . '${1}</a>',
    '<a href="http://mcdruid.co.uk/content/simple-twitter-feed-on-drupal-5/' . $destination . '${1}">' . $prefix . '${1}</a>',
    '${1}<a href="http://mcdruid.co.uk/content/simple-twitter-feed-on-drupal-5/' . $destination . '${2}">' . $prefix . '${2}</a>',
  );
  return preg_replace($matches, $replacements, $text);
}

The result is nicely filtered and formatted tweets, with @usernames, #hashtags and URLs all converted to links.

If you don't like the way the aggregator module provides the block for the feed, you can always take what aggregator outputs and manipulate it with your own module code, e.g.:

  // get the block content from aggregator module
  $block = module_invoke('aggregator', 'block', 'view', 'feed-1');
  $block_content .= $block['content'];

It'll do the job until the site's upgraded to a newer version of Drupal.

Sep 21 2010
Sep 21

This was one of those days where you set out to do something, and something else you've been meaning to do jumps in the way and gets done instead. I put off my blog post about setting up SQLite for testing on MacOS and fixed my Twitter module/OAuth setup instead.

After I posted my last blog entry about devoting Tuesday mornings to Drupal, I realized (for the n-th time) that my "Announce posts to twitter" feature wasn't working, and of course hasn't been working for a while since twitter has moved to oauth for its API authentication. Now, I'm all for that, and open/user-centric id and authentication in general. I even have a few trivial commits to the first incarnation of Drupal oauth module, and a bugfix in for Andy Smith's first OAuth PHP library. Still, broken is broken. General busy-ness and apathy had kept me from diving in there and trying to fix things...

Of course, with a rockin' community like Drupal's , coming back to something later often means that someone else has done some good work in the meantime, and this was no exception. The issue with Drupal's twitter module and OAuth authentication (especially comment #106, by designerbrent) clearly documents the setup required. After a bit of head-scratching (due to my not having paid attention to the required version numbers! You need OAuth module 2.02...), the new twitter and oauth modules (after applying the patch mentioned in the issue) have this all working beautifully again, much nicer than before, really. It would be really nice if the patch in question got committed :)

Feb 20 2010
Feb 20
Printer-friendly versionPDF version

A Capital Region Drupal meetup (the first of its kind I believe) is scheduled for March 6th and I hope to be able to talk about authentication and identity. Just a couple of years ago the issue of authentication and identity on Drupal sites was limited to the functions of the Drupal login system. I remember that the first module I ever utilized to improve the login process was LoginToboggan. LoginToboggan adds such nifty features as logging in with name or email address, placing a login form on pages not accessible to anonymous users and much more. It's a great module and I still use it today. Since then we've also seen a few social networks like Twitter and Facebook increase greatly in popularity. So while millions of people create accounts on those services they may not necessarily want to create an account (and have to remember another password) on your Drupal powered site.

Lucky for us Drupal site builders that the big social networking players realized that in order to get even bigger they would need to reach out to other sites. They have done so via APIs such as Facebook Connect, the Twitter API and Google Friend Connect. All three of these services allow someone to login to a website with their credentials from each of these services. So this allows people to log in to your Drupal site with their identity from Facebook, Google, or Twitter. Pretty cool, eh? All you need are the modules that make the connection between your site and these services.

Modules do exist for each of the three services mentioned above. Facebook Connect, Twitter, and Google Friend Connect all allow you to authenticate users to your site. They require varying degrees of technical expertise to set up and the exact functionality varies too. The good part is that they all seem to excel at verifying the identity of an anonymous visitor to your site, at least temporarily. The bad part is that they really don't fit in with the normal Drupal account creation process. If you're just looking for someone to be able to authenticate temporarily to add a comment they work great. If you want someone to be able to quickly create a full account, with access to all of the features of your site, with Google, Facebook or Twitter credentials then the modules fall short. What seems to work best is a hybrid approach where a user first creates an account on your site and then later connects to their account on the social network. After that they can log in with one of these services (provided you add the right links) and get all the benefits of a member of your site.

Earlier I mentioned varying degrees of functionality. Besides authenticating users these modules can do some publishing back to social networks as well. Facebook Connect will publish a link to a comment to an authenticated user's wall on Facebook. It will not publish to the wall when a new node is created though. The Twitter module will post to the wall when you add a new node but not when you add a new comment. The Google Friend Connect module will publish and update a site's "social bar" when you add a node or comment but it's not clear to me where else this activity shows up. What would be nice, and what I hope to see at some point would be for each of these modules to have similar functionality where users could choose to share a variety of activity on the site they logged in with.

The issue of account creation is also an important one. In order for these modules to be very useful they really need to fit in well with the standard Drupal account creation process. At this time they do not. One possibility would be to be able to assign people authenticating via a social service a certain type of limited role while capturing the minimum amount of information (such as username and email address) to create an account. Then generate an email that invites the person to return to the site and fill out a full profile. 

One other issue worth noting with the modules mentioned involves the placement of login buttons. Both the Twitter and Facebook modules add a button to the page at /user/login and to the login block. The Friend Connect button adds a button just above the comments but nowhere else. You can also use a snippet of code to place the Facebook and Twitter buttons where you want although the code isn't specifically made available. I just grabbed a snippet after viewing the page source html. I have been unable to do the same thing with the Google Friend Connect button. Examples of login placement are shown below.

Facebook and Twitter Login

Facebook and Twitter Login

Google Friend Connect Login

Google Friend Connect Login

As you can see there are various differences and shortcomings with these three modules. In order to really have a positive impact on site membership I think they need to be more flexible (placement of the login buttons for example), more integrated with the Drupal account creation system from the start and have consistent publishing functionality. There are other options available if you want something that is "plug and play."

If you just want to authenticate for comments and offer cross-publishing you can add the Disqus module which utilizes the service of the same name to manage commenting. In order to use this you need to turn off Drupal comments and you also lose the capability to search comments. Also keep in mind that Disqus does not create accounts on your site either. For account creation there is the Gigya Socialize module which integrates with the Gigya service. I have tested the Gigya module a little bit but I'm a bit concerned about running user data through the servers of another party besides my site and the one that the user is authenticating from. If Gigya goes down or has performance issues then it could prevent logging in or registering on my site. The same things goes for the Disqus service. There's also the issue of terms of service. I noticed the following in Gigya's terms of service for the socialize module in section 3, paragraph b.

End User Content. You retain all rights in your End User Content. However, by uploading, posting, submitting, linking to or otherwise transmitting any End User Content on or via the Site or Service, you hereby grant to Gigya a non-exclusive, worldwide, royalty-free, sublicensable, perpetual and irrevocable right and license to use, reproduce, modify, distribute, prepare derivative works of, display, publish, perform, transmit and access your End User Content in connection with the Service and Gigya’s (and its successors) business including, without limitation, for promotion and redistributing part or all of the Service (and derivative works thereof), in any media formats and through any media channels. 

I'm not completely comfortable with those terms. Gigya is well within their rights to set those terms since they are offering up free technology and computing power. As a site builder I would prefer to not expose myself or my community members to the possibility that their content could be published elsewhere. You might feel differently and in that case Gigya's service might work pretty well for you.

I think that it's important to discuss and ultimately create solutions for the issues discussed in this post. In fact I recently posted a bounty on drupal.org for a unified social authentication module. The bounty offer still stands. I'm also open to funding bounties for improvements to the individual modules as I have offered on in the Google Friend Connect issue queue. There are other discussions about this that I am aware of. The discussion called Social networking accounts: unified approach to store in external account data in user content profiles? covers some of the same issues I mention here and even mentions the fact that there is no module for LinkedIn. That's where I first say the Post It Everywhere module which is geared towards making authenticated cross posting easier. This module doesn't handle the sign up though. So there's still fragmentation.

All of this is a lot to digest. And I think it's too much to ask for one solution that does everything. After all, the meaning of everything changes as new social services and new functionality appears. It's an important discussion that I hope will lead to some better functionality soon. I'm going to do my part by talking about it, exchanging ideas and perhaps devoting some funds to help developers bring the features closer to what I would like to see.

Video Links

YouTube Version

Flash Version

Quicktime Version

Jan 13 2010
Jan 13

If you use drupal as a blogging platform you would want to auto tweet your new posts on to twitter. Twitter and Shorten module provides you with the basic framework required to do this and all you will need to get it working is to configure these modules. There is also a small patch that has to be applied to the twitter module (until the patch goes into the CVS) to allow the use of shortened URLs in the tweet.

The steps are as follows.

1) Download and enable the following modules.
- Twitter
- Twitter Actions
- Trigger
- Shorten

2) Download and apply the patch from http://drupal.org/node/678244#comment-2447004

3) Go to Admin » Site Configuration » Actions » Manage Actions
From the 'Make a new advanced action available' drop down pick

Post a Message to Twitter

In the new page that comes up enter twitter Username & Password. For the twitter message use something like "New Article: %title - %tinyurl"

4) Go to Sitebuilding » Triggers » Content

Under "Trigger: After saving a new post" select "Post a message to Twitter"

5) Show a little love for Drupal. Under Admin » Site Configuration » Twitter Setup, enable "Set source to Drupal when updating Twitter status"

If you see that the shortened URLs point to node/nid instead of the url alias you will have to go to your database and increase the weight of the Trigger module to above Pathauto and Twitter Actions

You are done. Go to twitter and bask in the glory of your drupal expertise ;)

Post your comments / questions

Jan 12 2010
Jan 12

This page holds links to documentation about the module “heartbeat” for drupal 6. From here you will find how to install the module, how to use it and how to implement its hooks.
Heartbeat can be used with the rules module. Rules allows other modules to describe default rules events and actions, and that’s what heartbeat does. The rules as well as the messages you build are exportable into defaults. Developers can of course use an api function to log on custom criteria.
After installing, a couple of heartbeat activity messages (and default rules are activated). Add some content, change your profile or reply on some content, messages will be logged immediately. You can use the blocks and pages to view the result. With the heartbeat views submodule, you have a little more control but it is not that convenient all the time. I recommend the built-in blocks and pages.

Before you start using heartbeat

You must be aware that there are two interfaces you will be dealing with, when setting up your heartbeat. You will learn here about how to compose messages and set features to them, but you will have to learn how rules work.
You can always try it out the trial-and-error way. Suites me most of the time :-).

Heartbeat messages

Heartbeat messages are custom made messages that serve as template. When heartbeat activity gets logged, it is always linked to a heartbeat message. Depending on how you configured a message, the result messages go through a merging and grouping mechanism to (re)build the messages that seem related.
E.g. Stalski posted page1, page2 and page3.
For this to work properly as you planned, you have to think a bit about how you will group your variables. You group by user (E.g. !username) to summarize nodes (E.g. !title).

Tip: You can use html in your messages if you want to wrap something with a css class to give a link another colour for instance.

Dumb logger, smart viewer

All heartbeat does is log all we want it to, for each available language in the site. You’ ll see that you can log message activity as long as you configured a heartbeat message to link it to this activity stream (see $message_id). So what i mean with dumb logger is the fact that there are no restrictions on the logging itself. It is the display that determines what needs to be fetched and shown.
There are two ways on how to log something to the database table heartbeat_activity:
1/ use the api function

  1. /**

  2.  * API function to log a message from custom code

  3.  *

  4.  * @param string $message_id

  5.  *   Id of the message that is known in the message

  6.  * @param integer $uid

  7.  *   Actor or user performing the activity

  8.  * @param integer $uid_target [optional]

  9.  *   user id of the target user if present. Target users can be an addresse or a

  10.  *   user relation transaction with the actor $uid

  11.  * @param integer $nid [optional]

  12.  *   Node id for content (for context node)

  13.  * @param integer $nid_target [optional]

  14.  *   Node id for content that is related to other content

  15.  * @param array $variables [optional]

  16.  *   Variables can be used if you used them in the used message. Take care to use

  17.  *   the @-sign for words that are prefix with the question mark sign in the messages

  18.  * @param integer $access

  19.  *   The access to restrict the message

  20.  */

  21. function heartbeat_api_log($message_id, $uid, $uid_target = 0, $nid = 0, $nid_target = 0, $variables = array(), $access = HEARTBEAT_PUBLIC_TO_ALL) {

  22.    

  23. }

2/ use a heartbeat action triggered by the rules event based system.

The screenshot shows a rules rule edit form. Choose an event, digg into the conditions and use the heartbeat user activity logger as action.
Customizing the action is basically nothing more than setting tokens to its variables. Instead of typing a message here, we choose a heartbeat message we defined earlier or that came as default. This will openup a textarea with prefilled variables from the original message. Same thing here, assign tokens to the variables, each line for another variable.
Save this and make sure the rule is active. Rules will do the rest and start logging on occurring events.

It is even possible to add your own tokens to build really nice heartbeat views. Like a gallery with image_fupload. I did this and the result will be maybe included later as image_fupload integration submodule, if there is request for it.

Heartbeat blocks

With this module come a couple of default blocks. The built sql for this depends mostly on the scope of users that performed the activity. Everyone , only your friends or only your own activity.
Here I need a little more work because this is not scalable at all. So let’s say it is on my todo list.
Another block in the heartbeat core is the flag message form. With this form, users can flag each other by chosing one of the existing flag messages to form messages like “user slaps other user in the face”. Again, build as many template flag messages you like.

Administer settings

Settings for heartbeat regulate the way things are logged and viewed.  Even a couple of template stuff.

Submodules

Heartbeat has integration for some other contributes that are important because they describe user activity on a drupal site. A person that becomes friends with another or decides to subscribe to some group or channel can be logged as heartbeat message.

Heartbeat rules

This module is the module that allows you to create heartbeats with UI only.

Shouts

A shout is something like twitter or facebook’s “what are you doing”. It comes with a form block you can place on your site and let people shout. The messages appear in the heartbeat messages.
Important as well is to mention that these shouts live in a separate table as well. One could decide to show the shouts in a separte way and build their views with views2.

Og activity

Organic groups is a module that has a lot of user interaction going on. At the hearbeat activity settings page, some extra settings are provided if the submodule is set to on. Organic group describes extra events and conditions that heartbeat can use and uses.

Friendlist activity

Friendlist implementation into heartbeat activity with a user-to-user interaction. By all means it is user activity. A extra default message and rule comes with this installation.

Features

  • Messages can be grouped together when activities happen more than once in a set timespan.
  • Messages contain variable names you can choose yourself
  • It’s possible to write your own tokens to build sofisticated messages
  • You can use the message_alter hook to filter messages. (Sometimes conflicts can occur when building exactly the same messages on more than one event with the group setting on)

This entry was posted on Tuesday, January 12th, 2010 at 11:36 pm and is filed under Drupal, Heartbeat. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

May 22 2009
May 22

Play Video

Movie link (right-click to download) Problem with this video? Contact me

"Twitter me this, twitter me that," can anyone listen to or read any Internet media news and not hear about Twitter? Not likely these days. It seems that celebrity and competition has driven Twitter to become that latest Internet fad.

Wait as second? A fad? Well, only time will tell if that's the case. Currently (to me at least), it seems like Twitter is yet another channel for social communication between multiple parties. If you think in terms of RSS, with a 140 character limit - (some times it's better if some people talk less*) -, you simply have the same selective personal input with some useful conventions providing some uniquely new levels of information access.

So, when it comes to cutting edge, would you expect any less from Drupal? Of course not. While there are a few twitter modules on Drupal.org, I chose to focus on the Twitter module created by James Walker (walkah) of Lullabot fame.

I've covered as much as I can, to give you a strong sense of what you can do with this module and how to integrate into your own Drupal sites.

--

Want to know when new videos are released on this site? Just follow GotDrupal.com on Twitter.

(from asterisk above) * although they just say more in smaller chunks

Apr 09 2009
Apr 09

Open Source Micro-Blogging CMS Software & Twitter Integrations

Posted by Jeffrey Scott -TypeHost Web Development | Thursday, April 9th, 2009
, ,

With all of the news about a possible Twitter sale to Google, I am wondering if we will see an increased trend in clients looking for micro-blogging sites and “Twitter Clones” for local and niche communication. Already, I am offering Twitter and Social Network integration (Facebook, mySpace) to clients on blog and ecommerce sites, to easily connect with friends, community, marketing, communication, etc. that takes place on those sites from the main domain. More and more people are including their Facebook status messages and Twitter stream on their homepages, using the API & RSS feeds those sites offer to pull social content across network channels.

WordPress Plugins:

Xavisys, the web development company behind WebDevNews.net, has created a Twitter feed module for WordPress called WordPress Twitter Widget Pro.

Drupal Modules:
For Drupal, I find the best module to be Activity Stream.

This module will republish all of your “tweets” into your Drupal site, as well as building a stream from your Facebook, Digg, Delicious, and other social networking and bookmarking sites. Highly recommended.

Also very promising for Drupal, but still in development, is the Facebook Status module (Facebook-style Statuses).

As expected, this module replicates the Facebook “Wall” and allows users to post status messages on their user profile pages. When I tried the module previously, there were still some bugs that would have prevented its use on a live site, but huge potential here with this module and a 2.0 version is now released.

The Twitter module allows users to:

  1. Associate one of more Twitter accounts with their Drupal user account
  2. Have their tweets displayed in a sidebar block or on their user profile
  3. Post to their own Twitter account or a site-wide Twitter account whenever they create new content

http://drupal.org/project/twitter

The Tapatio Projects seeks to build an installation profile for Drupal that “enable(s) users to leverage a drupal system as a front end to twitter.com (and other sources in the future) allowing them to use drupal as a hub for message aggregation, moderation, and dispatching.”

http://drupal.org/project/tapatio

http://drupal.org/project/tapatio_installation_profile

Stan alone CMS Software:

If you are interested in breaking out from a CMS like Drupal or WordPress and building a site that is a stand alone micro-blogging platform like Twitter, the Laconica open source software may be of interest.

For an example of a Laconica site, see: http://identi.ca/

The alternative to Laconica (but operating on the same open source micro-blogging standard) is Open Microblogger

By design, the open-source micro-blogging sites share an API to allow easy cross posting and searching of messages between sites. They also have more advanced built in SMS capabilities, something you will have to add extra modules for in Drupal & WordPress.

Whether your client is requesting Twitter integration into their home page or business site, or you have a request to build a full micro-blogging platform, one of these solutions should make a good base to build upon.

Tweetbacks

Mar 31 2009
Mar 31

I admit I've been lurking in a very slackernly manner in all the discussions in the Sakai community about content authoring, 3akai, UX, K2, Sakai NG and other unpronounceables, so I'm sorry if all this is a day late and a dollar short. Feel free to ignore me if you're part of Sakai and are way too far gone for any more input. After all, these are just thought experiments ;) If you're not part of Sakai, you might learn something about Drupal at least, so it may well be worth your time.

I draw some lessons here from Twitter and Drupal not to suggest that Sakai duplicate them, but rather that we hold those models in mind as we move Sakai forward. Even without these experiments, some of these ideas may be in our thinking about Sakai, so if they are familiar, take it as a vote of confidence. But if not, I'd like us to have at least thought through why we would not take them as inspiration or why we would choose another path.

In the lessons I draw from Twitter and Drupal below, I may come off as a bit of a zealot. Frankly, I have a greater appreciation for Twitter and Drupal as tools that I have for Sakai as a tool—my greatest appreciation for Sakai has always been for its community. But I would like to appreciate Sakai-the-tool as much or more than Twitter and Drupal, and I think I could, given the directions I see Sakai heading now.

But why Twitter and Drupal? When I'm thinking about all this Sakai stuff, my first thought is to reach for existing models. And the models I reach for are the handy ones. Why? Because there must be some reason I keep certain tools handy. There are lots of good tools, but the ones that fit so comfortably in my hand are well-worn for a reason. I also know them well—keen edges and ugly nicks—and so can draw the best lessons from them.

For those of you who live under rocks, Twitter is a microblogging and social networking service that has gotten a bit of press lately. A lot of people are using Twitter, and a lot of people don't get it at all. If you already use Twitter, great. If you don't get it, that's OK. You don't need to "get" Twitter to learn its lesson. There's only one, it's pretty big picture, and you won't have to tweet about doing your laundry or hear about mine just because you read about it.

Drupal is a mature web content management system/application toolkit, currently at version 6.10, with a very vibrant international community and strong commercial ecosystem. About 1,400 people attended the most recent DrupalCon in Washington DC.

Keep It Simple Like Twitter

Keep it simple. Open it up. Let the complexity come from everywhere.

John Ellis has kidded that Twitter has become so popular mostly because it is so easy to make new words that start with "tw": twavatar, tweeple, tweetup, twistory, etc, and if you haven't heard them all, check out the twictionary: http://twictionary.com

I think John's almost right. It's about the simplicity and what people do with it. I think Twitter's rise has a lot to do with their focus on keeping it simple: 140 character posts, direct, addressed, or pure statement, public/private, following/followers, search. Twitter keeps it simple and—just as importantly—offers a public API that lets the world layer an almost unfathomable and—to the Twitter team I'm sure—unimagined variety of interfaces, uses and extensions based on Twitter's simple service.

In other words, Twitter knew what it was doing, but it didn't expect that what it was doing was all that could be done.

Neither can Sakai. Looking ahead: we should focus, keep it simple, do it well, and open it up, so that everything we can't yet imagine can hang off our work.

Draw Good Boundaries Like Drupal

One of the most crucial decisions for any information machine is to decide how specific it wants to be and where it will draw boundaries around what it will do.

Every piece of software I've been involved with seems to follow the same general development pattern: In the beginning, software is built to meet specific needs in a certain context. As more needs are layered on over time, the software becomes either more cluttered or more generalized—or both—and gets rather messy. No one knows anymore exactly what it is for or where its boundaries are, but we end up tethered to it in all its messy complexity.

Take MS Word, which is both highly generalized (name something you CAN'T do with Word) and overly cluttered (you have to manage the TOOLBARS in Word). At this point, Word is really neither a great word processor nor is it powerful enough to be our main content management interface. And yet we use it for both. We are stuck to this big, messy machine and we can't get loose (assuredly, there are also some other, non-technical reasons we are stuck to Word).

Sakai also started out meeting specific needs. And over time, Sakai too has become rather cluttered. Now it seems we are clearly at a turning point where we want to generalize. I think our success will depend on where and how we map Sakai's boundaries as we clean things up. Drupal provides an instructive model in the cartography of information machines.

Caveats: There are other models that might be just as instructive. Drupal is just the piece of software I know best that, in my opinion, matches Sakai's stature and has done a good job defining itself as an information machine. There are also many valid criticisms of Drupal. For example, I would never argue that Drupal's user interface is ideal. The lessons I think Sakai can take from Drupal are at a deeper, architectural level.

First, like Twitter, Drupal has done a good job of drawing boundaries around what should be core and what should be left outside. Drupal core is pretty clean, focused on key, common functions. Meanwhile, in Drupal's contribution space and beyond, it is rather messy. That is exactly as it should be. Good stuff comes out of that mess and some of it makes its way into Drupal core when the time and scope is right. Other stuff outside is fully mature and absolutely essential—for some. And for that reason, it will never become core, which is exactly as it should be. Core should not be defined by maturity, but by generality. There are a lot of ways that the Drupal community works to support both high innovation in the periphery and healthy boundaries at the core. Sakai should think carefully about where we put our borders and how we will support work inside and out.

Second, Drupal has done a good job of generalizing, focusing and simplifying its core functionality, and making core functions easily available to peripheral tools. If you do it right, all you need to build into a Drupal module is the specific functionality you need...everything else is a call to core. This makes it incredibly easy and quick to add functionality to Drupal—which fosters innovation—and also ensures that contributed modules participate fully in the larger Drupal machine. As we collaborate to define and build Sakai's next generation, we should look to mature models like Drupal that have a head start on clarifying and exposing core functionality.

Those are the general lessons I draw from Twitter and Drupal, so if you've had enough, you can stop reading here. Following are some more specific examples I draw from Drupal that relate to some of our recent discussions in Sakai.

Content

Drupal starts with the idea that (almost) everything is a piece of content, starting from the same place on a conceptual and technological level as a "node." In Drupal, you augment the structure and/or functionality of these generic nodes by "decorating" them with additional structured data fields, workflow, etc. This way, all Drupal core has to concern itself with is the common structure (eg, common metadata like author, creation timestamp, etc) and common functionality (eg, access, CRUD, versioning, validation, input, etc) of nodes. More specific structure and/or functionality gets layered onto the basic node either through generic admin interfaces (eg, the Content Construction Kit, or CCK) or more tailored modules that make specially augmented nodes for specific purposes.

Want to create an event record? Create a new "event" content type, add a timestamp field that uses a mini-cal data entry widget. Click, click, done. Add location? Just decorate your event content type with a location field—oh, and then you'll have all the magic of a host of map integration modules at your disposal. Click, click, done. Want your events to link to location profiles? Create a separate "location" content type and add a node relation field to your event content type to link each event to a profile of its location. There, you just built a dynamic event web application in 15 minutes without calling your code monkey.

Another key Drupal module—Views—provides a generic query and output service for content. Need a table listing of all content meeting certain parameters, paginated in groups of 25 with sortable columns? Click, click, done. Want to offer that same content as a feed? Click, click, done. Want some completely different bucket of content, organized and presented in some other way? Again, click, click, done—and almost always with no coding required.

By generalizing core content in this way, Drupal handles it all consistently, but at the same time makes it easy to customize content for even the most complex needs—often with no coding required. Is it so easy faculty could do it? Maybe not without training, but you may not want end-users defining new content types willy nilly anyway. What the Drupal model offers is a way to lower the bar for meeting specialized content needs to a level that they can be met in minutes by someone with only a bit of training: a faculty power user, instructional designers, help desk staff, student assistants—crikey, even I can do it. Also, did I mention that Drupal content type definitions are exportable? Yes, you can define, export and share specialized content types.

Drupal understands that content creation and display is a core function, but it doesn't expect to know what YOUR content needs to be. Sakai would benefit with generalizing its content handling to Drupal's degree. As educators, we may feel that we are well-placed to define what a syllabus, a quiz, a discussion, a lecture podcast, or a portfolio should be. But as technologists, we will serve all the unforeseen ideas for what educational content might be far better by designing a solid, general framework on which our current—and future—ideas can be made manifest.

Taxonomy

From the start, Drupal anticipated that taxonomy—which is just a fancy word for categorization or tagging—was an essential core function. Drupal enables you to define any number of category vocabularies, each having special characteristics (eg, freetagging, hierarchy, etc), each holding any number of terms, and each related to whatever specific content types you desire. Thus any content in Drupal can be categorized in multiple ways, with highly structured categories or freetag folksonomies—or both—for different purposes. Good stuff is baked into core, like returning content matching various categories by simply including boolean queries in the URL.

Following the model I've been stressing of core simplicity enabling unanticipated innovation, Drupal modules make use of the basic, core taxonomy service to do all sorts of unexpected things, like category-based access control, or category-based organic group creation and management. With taxonomy as powerful as Drupal's baked into core, Sakai too could enable all sorts of things that we may not have yet imagined.

Theming

Drupal's core presentation layer is the icing on the cake, which is exactly what presentation should be: icing. There's nothing worse than finding presentation baked into deeper levels of software, where it's static and hard to change. Drupal handles presentation as the final layer, enabling design to be very flexible and easy to modify. Want every user to be able to choose between three entirely different designs? Just load three themes and let the users choose for themselves. Need to offer a mobile version of your site? A dedicated mobile URL and/or some user agent sniffing can automatically deliver a theme optimized for mobile devices—oh, and there's a module that does all that for you (something you'll find again and again in Drupal). Need that new content type you just made to look different than every other piece of content on your site? Drop a custom theme template named for that content type in your theme's directory and your generic theme gets overridden automatically with your custom design—just for that special case.

The power of this theming model came when Drupal core stopped worrying about what the perfect presentation should look like and instead offered a way to deliver ANY presentation predictably with easy-to-build templates. In Sakai, the same power and flexibility would take us past the question of what Sakai SHOULD look like to the simple answer: Sakai looks like whatever you want it to look like.

Layout

Our next-generation Sakai authoring experiments are delivering some juicy tools to place different pieces of content and widgets in different parts of a page. Drupal offers two models that may help us refine the good work we're already doing.

Drupal core has long had the concepts of regions and blocks. Regions are areas of a page defined by a theme. A theme can have any number of regions laid out in any way. Blocks are bite-sized content or functionality: anything from a static welcome message, to a listing of recently updated content, to a user log in form. Blocks can be created individually by users or made available programmatically by Drupal core or contributed modules. Drupal core offers tools to map blocks to different page regions, and customize the visibility of blocks based on almost anything: user role, content type, authentication status, URL patterns, etc.

Drupal's region and block system is very flexible, but it is better suited for site administrators to define global page layouts than it is for individual users to author custom pages with varied content as we have been imagining in Sakai.

Panels is a module outside Drupal core that comes closer to what we've been imagining in Sakai: the ability for an individual user to place content and/or widgets exactly where they want on an individual page. Unfortunately, the authoring experience in panels is not anywhere near the kind of intuitive WYSIWYG experience we have been working toward in Sakai. However, Drupal panels offers all the ingredients we might want in a page authoring experience...we just need to cook and serve them differently.

I take several lessons for page authoring in Sakai from what works well in Drupal's layout tools of regions, blocks and panels. When laying out a page in Sakai, we should be able to choose from:

  • A library of predefined layouts, offering good, commonly-used page structures. Some of these layouts could be merely structural—empty containers waiting for me to fill them—defining something like Drupal's page regions (eg, a typical three-column layout with header and footer). Others could combine some empty regions for me to fill, along with other regions already filled with existing widgets (eg, a big, empty body region with a sidebar that has calendar, discussion and tagcloud widgets already in place). We should be able to export and import these predefined layouts. I should be able to tag layouts and see/browse tags from other users—but one should be able to tag everything in Sakai, so we don't ever need to state this requirement again, right?
  • The opportunity to author a new, custom layout, where I can design whatever structure I want. I should be able to tag, export and share my custom layout.
  • A library of widgets—not unlike Drupal blocks—that deliver either content or functionality. I should be able to search and browse global widgets supplied by the system, my own widgets, and ideally, widgets authored by other users I've subscribed to. I should be able to tag widgets and see/browse tags from other users.
  • The opportunity to author a new widget on the spot, thereby adding it to both the page I'm authoring right now and my widget library for use elsewhere. Making a new widget might be like making a view in Drupal: the ability to make some selection of content and display it in some form (eg, table, list, feed, etc). Making a new widget might be like something else too. Like Drupal, Sakai should offer new tools the opportunity to supply new widgets—and/or the opportunity for users to author new kinds of widgets.
  • The opportunity to author a piece of content on the spot, thereby adding it to both the page I'm authoring right now and whatever other collections that specific kind of content happens to live within (eg, pages, syllabi, tests, forum topics, etc). Like modules in Drupal, new tools in Sakai that define content should end up offering the chance to author in Sakai's standard authoring environment.
  • The opportunity to define under what circumstances a given layout/widget/content piece is visible. As in Drupal, I should be able to define who can see something and when they can see it. Default visibility should be baked into predefined layouts, widgets and content. And if I have the right access, I should be able to override default visibility.

Pluggable Authoring Tools & Filters

Drupal offers flexible frameworks for modules to supply different ways to get content in and spit it back out. While Sakai has been wedded to the oft-maligned FCKeditor for some time (yes, all the WYSIWYG tools have their drawbacks), Drupal offers the ability to plug in almost any authoring tool: plain old text, different WYSIWYG/WYSIWYM editors, various markup formats, etc. At the same time, Drupal offers the ability to define output filters that can clean up and/or enhance stored content when it is rendered. Drupal's filter on output strategy allows the same content (stored raw in the database) to be transformed differently depending on what filters are in use (which can even depend on user role or preference). Want anonymous users to see all your content translated into Pirate talk? Done. Again, Drupal core is not determining how stuff gets in and out, it instead provides a pluggable framework so we can decide for ourselves what we need. So many of Sakai's usability issues revolve around the rigidity of input and output, we would do well to adopt a pluggable model which opens possibilities beyond what we can anticipate.

URLs

Unfriendly to humans and search engine robots alike, Sakai has some of the ugliest URLs ever. It makes sense for a web application to have canonical URLs and it's hard to make them friendly, but there's no reason to show them to the world. Drupal solves the ugly URL problem by offering a core service for URL aliasing, so users themselves can define better URLs for their content. A valuable contributed module—pathauto—allows site administrators to define automatic URL aliasing rules for different canonical URLs, thus saving authors the aliasing task and enabling more structured aliasing patterns. Again, the lesson for Sakai is to fix our problems by offering flexible options rather than trying to bake in the final solution.

Workflow

Drupal core has basic workflow baked in based on triggers and actions, where triggers are set to fire on certain events, in turn generating specific actions. For example, a workflow can be established to email a site owner (an action) every time new content is posted (a trigger). Once again, instead of providing all the ideal workflows we might imagine, Drupal provides a generic tool to build workflows, which we can use to build those we already know we need, as well as those we don't yet know we need. If only Sakai's sometimes idiosyncratic workflows were merely defaults, which I could change or replace as easily as I can in Drupal.

Installation Profiles

Many parts of Drupal can be exported/imported (eg, content types, views) or are modular (eg, modules, themes, filters) to allow for easy sharing and migration. One of the Drupal's most powerful tools is the installation profile: an automatic recipe to build a new Drupal site. Installation profiles set modules, themes and other configuration options when Drupal is first installed so you can distribute a pre-packaged Drupal recipe, optimized for specific purposes. If Sakai had installation profiles like Drupal, I could imagine distributions for different organizational types (K-12, community college, liberal arts college, research university, etc), different usage focuses (collaboration, portfolios, teaching and learning), or different languages, giving new adopters better starting places than a generic Sakai installation. As Sakai generalizes itself, we should also have a way to demonstrate and distribute best practices for specific uses.

Other Stuff

There are many other (smaller) lessons Sakai might take from Drupal. Some that come to mind include:

  • The core form API that lets modules safely offer forms with minimal work and modify others (including core forms) dynamically as needed.
  • The core multisite functionality that allows Drupal to run multiple sites from the same codebase, yet override and/or augment any part of any site as needed.
  • The core comment functionality that lets any piece of content include threaded comments.
  • The core support for automatic feeds.
  • The core menu functionality that allows navigation to be managed as its own kind of content.
  • Core support for OpenID authentication.
  • Sophisticated core caching mechanisms.

Finally, a Shout Out to Portfolios

Oddly enough, the part of Sakai that most reminds me of Drupal's generality is our portfolio toolset. It's not surprising that portfolios—maybe the least well-defined practices in teaching and learning—have led to the most generalized tools in Sakai. And yet, the most obvious complaint about Sakai portfolio tools is that they don't do anything at all. The fact is: Sakai portfolios can do almost anything. What's missing in portfolios is the easy tools to build them and the portable models to demonstrate their power. Sakai would do well to look inside to its portfolio tools—for inspiration to generalize, but also for caveats about what must be in place to make generalized tools usable and practical.

Feb 19 2009
Feb 19

I created a Twitter account that just tweets all Drupal Planet posts - .

Aug 04 2008
Aug 04

One thing that has really blown up in the past two years is micro-blogging and the idea of sending little updates to a mass majority of people at once. These updates are public, and users can "subscribe" to another's updates, therefore encouraging conversations to start, and "lifestreams" to spawn.

Some fairly large websites that provide this type of service are Twitter, Jaiku, Facebook, Spoink, LinkedIn, and Identi.ca/Laconica.

Robert Scoble has made some pretty interesting quotes about the phenomenon, my favourite being "Twitter is the public square. Lots of noise, little signal. Blogs are like a speech. Signal, but little noise". But that's up for debate, and out of context with what I want to talk about in this blog post. What I want to talk about here is integrating micro-blogging with the content management system, Drupal.

Recently, Identi.ca has become quite popular. What makes it different then other services like Twitter is that it is open source, under software named Laconica. Since Laconica is open source, it means that there could be any number of servers running the software at any given time. I could be posting on Identi.ca, while my friend is posting on SportsTwit. So how do I subscribe to my friend's posts when we're on different networks? The answer is provided through the OpenMicroBlogging specification, which describes how two different systems could manage subscriptions across networks. When I log into Identi.ca, I see updates from people across a number of different Laconica installations and networks.

So why would we want to bring this to Drupal to make a distributed Twitter clone? This would allow you to use your own blog as your Twitter/micro-blogging profile. People could still subscribe to your posts, and you could subscribe to theirs. Instead of just writing small 140 character text updates, you could incorporate anything you wanted (video, pictures, audio, etc). Drupal is all about distributed open source awesomeness, and this would bring that awesomeness to the micro-blogging world.

How would we put this into action? The first thing to do is take a closer look at the OpenMicroBlogging specification, as well as the awesome OAuth Drupal module. Sumit Kataria wrote a brilliant post about OAuth and how it works. I posted a groups discussion on it a while ago to get discussion going, and Tim Millwood mentioned that he started FooCity. It doesn't allow subscribing to other networks through the OpenMicroBlogging specification though and that, my friends, is the next step to allowing the ultimate distributed micro-blogging platform come to life: Implement the OpenMicroBlogging specification using Drupal and OAuth.

Jul 15 2008
Jul 15

Our second episode features Nate Angell or @xolotl, a name that's hard to remember completely, but one that we will always remember. There's development talk later as he demonstrates the shiny, new iPhone app called iToony.

But first, we chatted about Drupal for large organizations and the relative livability of various European cities—all accompanied by extremely loud (but pleasant) French songs, one of which may or may not be a rendition of Cole Porter's Night And Day.

The image Nate made of Bram Pitoyo using the iToony app is here.

Apr 29 2008
Apr 29

I was recently playing around with the design of my twitter page and got it to look like Garland, the default design for Drupal. A number of you have asked me about it, so I thought I'd publish the values I'm using. So, if you want your Twitter page to also look like Garland, you can input the following values into your Twitter design.

Background Color: EEF5FA Background Image: garlandtwitter.png Text Color: 494949 Name Color: 494949 Link Color: 027AC6 Sidebar File Color: E1F0FA Sidebar Border Color: E1F0FA

Of course, I'd recommend taking my website logo out of garlandtwitter.png, and putting your own image in there, but that's completely up to you.

Apr 22 2008
Apr 22

For the second year running, Drupal placed in the Webware 100 awards in the Publishing & Photography category.

In this publishing category, the only other contender in the top 10 that comes close to matching the flexibility and functionality of Drupal was Wordpress, which would probably do well to continue to focus on the blog niche and not try to play catch-up with the more mature Drupal (current versions: Drupal 6.2, WP 2.5). Missing altogether were projects that match up better with Drupal, such as Joomla and Plone.

Check out all 10 winners:

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