Calendar with highlighted dates

In Drupal 7, it's easy to show calendar (ui.datepicker) date selector to user. (it's not hard in Drupal 6, too - but in d6 you need to enable http://drupal.org/project/jquery_ui to get the calendar, I think)

You just write several lines of js code, and you get a calendar.
But things are quickly getting more complicated when you need to allow visitors to choose from dates that have some nodes "attached" to them only. E.g. if you have news view, and you don't have news for 13 Nov 2011, there is no sense in allowing user to click on this date in calendar (I'm talking about the case when clicking on calendar redirects user to views page  /news/2011/11/02 , where 2011/11/02 is a context filter for news nodes)

Here is the screenshot illustrating the desired functionality:

(so, user can click on Nov 3 - but not on Nov 4)

I'm not sure if there is a module to do the stuff I've described in this post, but I couldn't find one, and since I was asked 3 times to implement such date-restricting feature - I hope it can be useful for someone.

Okay, so the overall idea is:

  1. Use jquery.ui.datepicker to show calendar to user
  2. Use several lines of js code to hook into calendar days rendering, and allow/disallow date selection if the date has/doesn't have available nodes.
  3. Implement corresponding PHP method to query database and prepare data for code in step#2.

And here is the code (I recommend to put it to /yourmodule/js/calimprove.js):


(function ($) {

Drupal.behaviors.calrestrictselection = {
 attach: function(context) {     if (!$('body', context)) return; // don't run the processing if it's an ajax call {gfm-js-extract-pre-1}

// just call  mywebsite.com/dateloadevents which returns JSON array of available dates (see php code of this page below)    // and enable calendar for textfield afterwards     $.getJSON(Drupal.settings.basePath + 'dateloadevents', {}, function(data, textStatus, jqXHR) {           eventdays = data;            $('#views-exposed-form-events-page #edit-start-value-datepicker-popup-1').attr('disabled', false);         }); {gfm-js-extract-pre-2}

} } })(jQuery);

PHP code for /dateload_events callback (defined in yourmodule/yourmodule.module):

'Date load',     'type' => MENU_CALLBACK,     'page callback' => 'yourmodule_event_dates',     'access arguments' => array('access content'),   );   return $items; }      function yourmodule_event_dates() {     // retrieve list of dates in %Y-%c-%e format where at least 1 published node   // of type 'event' exists. I use fetchAllKeyed(0,0) method that returns array   // where key=value, so the returned array looks like:   //   array(   //     '2011-11-1' => '2011-11-1',    //     '2011-11-3' => '2011-11-3'   //   );   //   // it's easy to modify the code to return number of items for specific day   // but I didn't need that in my project.   // 'field_date_interval' is the name of my date field attached to node type   // 'event'.    $dates = db_query("SELECT DATE_FORMAT(date.field_date_interval_value, '%Y-%c-%e') as day    FROM {node} n INNER JOIN {field_data_field_date_interval} date   ON n.vid=date.revision_id    WHERE n.type=:ctype AND n.status=1 GROUP BY day   ORDER BY date.field_date_interval_value ", array(':ctype' => 'event'))->fetchAllKeyed(0,0);    // renders php array as json   drupal_json_output($dates); }  ?>

Important notes:

  • I use beforeShowDay() event of ui.datepicker to modify day interaction. here is the event description from jquery.ui website:
The function takes a date as a parameter and must return an array with [0] equal to true/false indicating whether or not this date is selectable, [1] equal to a CSS class name(s) or '' for the default presentation, and [2] an optional popup tooltip for this date. It is called for each day in the datepicker before it is displayed.  Code examples  Supply a callback function to handle the beforeShowDay event as an init option.     $('.selector').datepicker({      beforeShowDay: function(date) { ... }   });

there are other useful methods of ui.datepicker, you can find them here:
http://jqueryui.com/demos/datepicker/

  • In Javascript, the date.getMonth() function returns month numbers starting from 0!  (in PHP, it starts from 1)

You can see working example of the calendar here ("Дата" field in left sidebar)