Pages

Friday, August 30, 2013

How Drupal "Views Auto-Refresh" really works

Views is a great module and as you'll probably know it is the most downloaded module in Drupal's history. The possibilities which Views provides you with are almost endless but there is one limitation. The generated output of a view is by default static. What if you want to have a dynamic activity stream as you know it from Twitter or Facebook?
Allow Drupal's unofficial slogan to answer that question: There is a module for that! The module in question is "Views Hacks" and contains another module called "Views Auto-Refresh". There is a blog post about how to implement this module in order to get it to work as you want to, but it seems like this post doesn't cover all of the aspects that are implemented in the "dev"-version. A follow up blog post offers a little more insight, but still not everything I needed.
On a side note: In this tutorial we will use the "dev"-version of "Views Hacks", because the alpha was buggy at the time, and the implementation of the JavaScript part is not in the Drupal way anymore.

Preparation

Views autorefreshb
Let's get started with the implementation. I assume that you have downloaded, installed and activated Views, Views UI, Views Hacks and Views Auto-Refresh. Further I assume that you have created a view for which you want to use the auto-refresh feature.
I will demonstrate the implementation of Views Auto-Refresh by showing screenshots of the actual project.

Implementation

Views autorefresh
Make the following configurations:
  • First of all, you need to duplicate your actual view as a view page. Give it a semantic name like "autorefresh".
  • Give the page a unique path as displayed in the screenshot above.
  • Also give an easy-to-remember machine name.
  • Make sure that the view is using AJAX.
  • Add the Content: Post date (with operator) or any other timestamp as a contextual filter (we used the "Content: Updated date"). Views Auto-Refresh will provide the timestamp needed.
Now head over to your view and add a "Global: Text area" with the text format "PHP Code" and add following code:
<?php print theme('views_autorefresh', array('interval' => '30000', 'incremental'=> array( 'view_base_path' => 'frontpage/autorefresh', 'view_display_id' => 'autorefresh', 'view_name' => 'articles', 'sourceSelector' => '.view-content', 'targetSelector' => '.view-content', 'firstClass' => 'views-row-first', 'lastClass' => 'views-row-last', 'oddClass' => 'views-row-odd', 'evenClass' => 'views-row-even', ))); ?>
Views autorefresh
 
As you can see, there is a base path and a display ID.
The base path equals your defined page path, and the display ID equals the Machine name of the auto-refresh View.
"interval" defines how often the auto-refresh View is being called while "view_name" is the machine name of the actual view.
The additional settings are the selectors and classes which will be addressed by the JavaScript of Views Auto-Refresh. I don't want to dig too deep since they should be self describing.
Now you might think that everything is done. But no, wait, we have to add the same code to the header of your original view. So do the same thing there as I have described it above. Please make sure that this view also uses AJAX, else it wouldn't work.

Further possibilities

In our project we use the jQuery library Isotope which sorts all the posts dynamically on loading or resizing. But you have to trigger the re-layout of the page if the Views Auto-Refresh has delivered some new posts. This is really simple and straight forward. You just have to add a Drupal behavior in your JavaScript like this:
/** * Add functionality to trigger reloadItems after an autorefresh */ Drupal.behaviors.triggerIsotopeAfterAutorefresh = { attach: function(context, settings){ $('.view-id-articles').bind('autorefresh.incremental', function() { //getting the content/context $isotope = $('.view-id-articles .view-content'); //reload all items by original order $isotope.isotope( 'reloadItems' ).isotope({ sortBy: 'original-order' }); }); } }
As you can see, you can just bind the event 'autorefresh.incremental' to execute your own code. 'autorefresh.incremental' is fired every time the Views Auto-Refresh module loads the designated view.

Conclusion



After a few trial and error attempts, I finally figured out how Views Auto-Refresh really worked. There is a lot more to this module that isn't documented. So it is much more powerful than I can describe in only one blog post. So go on and give it a go.

Update 9.5.2013: As pointed out by Phil Dodd in his comment the Views Auto Refresh module has been moved to its own home athttp://drupal.org/project/views_autorefresh.

Saturday, August 10, 2013

Example: Block to show "Latest News" related to current page

This recipe shows how to apply the Contextual View idea to create a block that shows news headlines related to the current page being displayed. It assumes you have two node types, which I'll call "primary" (this is the node type that contains the base information) and "news" (this is a node type that defines a story, announcement, or whatever). In our case, the "news" stories are often directly related to one of the "primary" nodes on the site, and we wanted to show all the "latest news" related to each primary node in a block on that node's page.
Here's the recipe:
  1. Add a CCK "nodereference" field to your "news" type node.
  2. Use a "Select List" type, with a title like "This news relates to";
  3. Select the "primary" node type as the "Content Types that can be referenced" (and/or set up a View to restrict which nodes to list further, by user id, taxonomy, or whatever).
  4. (You might even choose to set this to be visible in the "Display Fields" settings, as this will provide a link back to the "primary" node it relates to.)
  5. In Views, create a Block view with a title like "latest news", set to display 5 nodes (say, with a more link). Use a List view.
  6. Select the Node:Title Field (as links), so you get links to the "headlines".
  7. Filter based on Node Type is "news" (and Published, of course)
  8. Add the Contextual View Block magic, to tell the view to pull in the news stories that have a reference to the current page! Add the argument: "Node Reference:This news relates to" argument, with "Use Empty Text" as a default. Add the following Argument Code:
          if (arg(0) == 'node' && is_numeric(arg(1))) {
              $args[0] = arg(1);
          }
          return $args;
  1. Sort in reverse chronological order (so latest news goes on top);
  2. Go to admin/blocks and configure the block to show up wherever you like. Set the block to only display on pages that will display your primary type CCK nodes.
Done. Wherever you have enabled this block, it should show only news stories that are related to (have a reference to) the current page!

Friday, May 3, 2013

Implementing a donation system with Drupal Commerce


The Drupal Commerce module is a great solution for anyone who wants to have a shop implemented with Drupal. It doesn't matter if you configure the shop from scratch or if you use an installation profile, you can have a shop in a very short time. Most of the time is just configuration, almost no coding required.
By default, you can create your products, set the prices for them, categorize them, create rules for tax handling, use different payment gateways (also with the help of some contributed modules) and many many other things. Unfortunately there is one thing you cannot do just with configuration: a donation system. The main problem in the donation system is that the amount of each donation can be different. If your donation system has only fixed amounts, then there is no problem, all you have to do is to create one product for each donation amount. But when the amount is variable, then implementing this using only configuration may not be so trivial (if not possible at all).
I will explain next how we deal with this case, when we have to build such platforms.

The concept

If we go a bit deeper and check how the Drupal commerce system is built and how the price of the order is calculated, we can see that each line item from the order has a unit price field, that usually is populated with the price of the product to which it is associated. So the user clicks on the button to add the product to the cart, and this triggers the creation of a line item in the order that will have the unit price of that product. But in our case, the price of the product is variable, because it is about a custom amount that the user wants to donate, so we cannot really use that price to populate the line item. We will have to handle this by ourselves, to implement some logic that will populate the unit price of the line item with the amount of the donation. The solution involves site configuration, as well as some coding.
The main idea is to have a new field on the line items (a price field) that will be populated with the value the user wants to donate, and then using a rule that will be triggered on the event “Calculating the sell price of a product” to put the value from our custom field into the price field of the line item. The reason why we do not just put the value directly in the line item price field, when we create the line item, is that the price of the line item can actually be updated later. For examplecommerce_cart_order_refresh() would update the price of the line items. In this case, our code that populates the unit price will not be executed, and we will not be able to maintain the correct price of the line item. That's why we use that rule that fires on the “Calculating the sell price of a product” and we store the amount that is being donated in a separate field that will not be altered by any other code.

The setup

And this is how it works:
add a price field on the line item type (admin/commerce/config/line-items/product/fields), with the name: field_variable_price
create a product with the SKU “donation” that has the price set to 0
add a rule that updates the price of the line item

 
For the coding part, an example can be found in a sandbox project. An important remark for the coding part is that we actually have to create the order and add the donation product to the order by ourselves, because we have to populate the field_variable_price with the amount that the user want to donate.
The code also has a page: /donate and a configuration form /admin/commerce/config/donation_amounts where some predefined amounts can be configured. For more details about the coding part, you can just check out the sandbox project.
While finalizing this blog post I discovered that, since starting to implement this feature on a recent project, a new module called Commerce Donate surfaced on Drupal.org which might be interesting for you too.

How To: Loading ads in a Drupal AJAX call asynchronously


One of our most recent releases, we were confronted with a challenge: loading ads during a Drupal AJAX (Asynchronous JavaScript and XML) call. This is how we solved it.

The issue

By definition synchronously loaded JavaScript can only add objects like files, content or ads to a page's structure until it is entirely rendered. An additional complexity that can occur while working with third-party advertising providers is that you can not control the performance of their infrastructure. Which means either you accept this risk or try finding a way to mitigate it. We went for the latter.
At this point it is important to stress that it is common practice for third party ads to use the document.write method which only works for the synchronous loading approach. To learn more about the synchronous and asynchronous loading of remote tags I suggest you read the article "synchronous vs. asynchronous tags - what’s the big deal?" on the krux blog. In our case however it became apparent that our solution had to include the asynchronous approach.
While researching possible solutions I found the following tools:
The issue with the the majority of them were, that active development or support wasn't visible, except for PostScribe. This solution offers the following features:
"Krux PostScribe enables the type of tag execution required by most ad formats in an easy-to-use-and-deploy format. It leverages innerHTML to ensure reliable, fast ad delivery."
"Unlike other innerHTML-based solutions (e.g., writeCapture, ControlJS, and OpenTag), Krux PostScribe seamlessly enables the “immediate writes” upon which most ad formats depend."
Which in plain English enables the quick rendering of the page to the user and allowing the ads to respond in their own time without reducing the user's experience.

How to use PostScribe

Download PostScribe directly from its github repository.
Include these files in your project:
/htmlParser/htmlParser.js /postscribe.js
Here is a small example:
<div id="myAd"></div> <script type="text/javascript"> $(function() { postscribe('#myAd', '<script src="remote.js"><\script>'); }); </script>

Our use case

As mentioned in the introduction this solution stems from LikeMag which, besides utilizing PostScribe, is a responsive website and uses the Isotope library to arrange items over the screen automatically depending on the viewport.
Since there are no free lunches on the web, LikeMag has to monetize too and one of their models is built around ads. So in order to deliver the perfect balance between content and ads for every viewport, we had to find a way to render the perfect amount after the identification. And that is moment the where PostScribe comes into play.
Instead of injecting the ads right into the Drupal view and slowing down the loading of the rest of the page, we just print the standard items. In a next step the system iterates over the items and adds the appropriate amount of ad containers to the content with jQuery. (These containers can be addressed with PostScribe, because PostScribe works best when the DOM is ready.)
LikeMag.com Ad
"But where is the AJAX you mentioned in the title?", I hear you cry. So let's talk about it right now. When we scroll down on LikeMag, which besides being responsive is a smart infinite scroll site too, new items will be loaded through an AJAX call. So here we can use PostScribe as we did before. The only thing that we have to check, is that we do not add ads to already "adified" content. In our case we can check if the view-item was already processed by Isotope.
Code from View, partially modified by isotope and postscribe
See the code as an example:
Drupal.behaviors.ViewsLoadMore = { attach: function(context, settings){ if ($(context).hasClass('view')) { $isotope = $('.view-id-articles .view-content'); $isotope.isotope('insert', $('.view-id-articles .view-content .views-row:not(.isotope-item)'), function(){ isotop_load_more_init(settings); Drupal.behaviors.advertisment.loadads() }); } else if ($(context).find("html").length == 1){ // If it contains html, it is the first behavior call. isotop_load_more_init(settings) } } } 
loadads: function(){ $('.view-articles .views-row.views-row-ad').once('postscribe',function(){ if(typeof(cachebuster) == "undefined"){var cachebuster = Math.floor(Math.random()*10000000000)} if(typeof(dcopt) == "undefined"){var dcopt = "dcopt=ist;"} else {var dcopt = ""} if(typeof(tile) == "undefined"){var tile = 1} else {tile++} var string = '<scr'+'ipt src="http://ad-emea.doubleclick.net/adj/likemag.ch/;' + dcopt + ';tile=' + tile + ';sz=300x250;ord=' + cachebuster + '?"></scr'+'ipt>'; postscribe($(this), string); }); } 
As you can see we process only the newly loaded items from the Drupal view and exclude the already processed items.
With this solution we are very flexible to deliver the page content as fast as we can while not depending on the speed of an ad-provider.