Hello,

I found that the calendar only can be edited by the author of the node, can other users to book online also?

Such as users click a date and then can input the notes to book, and the author can approve it later.

thanks,

Comments

fietserwin’s picture

Yes it can. It requires a custom form (typically containing 2 date fields, a submit button and probably a reset button) and a bit of custom javascript to glue the calendar API to your own form. Off you go. I'm currently working on the example below, a copy of my current project. I'll give it a try here.

In turn I ask you to test and review the example, and come up with improvements. I'm also looking which parts can be made generic and thus added to the availability calendars module by default, reducing the need for custom code on the other side.

In my current project, the code below posts to a webform that retrieves the arrival and departure date (and a hidden field for the accommodation nid, not included in the example below) and lets the user fill in other fields like name, address and email before sending an email to the accommodation owner. I leave that up to you.

Notes:
- there is a custom state 'option', replace with your own requested state
- after sending the form, the calendar is NOT updated (not even into the option state). That is left to the owner of the accommodation after approval.

Your form:

function example_reservation_formlet($form, &$form_state) {
  $node = $form_state['build_info']['args'][0];

  $form['arrival'] = array(
    '#type' => 'textfield',
    '#title' => t('Arrival date'),
    '#default_value' => '',
    '#required' => TRUE,
    '#disabled' => TRUE,
    '#attributes' => array('readonly' => 'readonly'),
    '#weight' => 2,
  );
  $form['reset-from'] = array(
    '#type' => 'markup',
    '#markup' => '<input id="edit-reset-from" class="form-reset" type="reset" value="' . t('Clear selected arrival date') . '" />',
    '#weight' => 3,
  );

  $form['departure'] = array(
    '#type' => 'textfield',
    '#title' => t('Departure date'),
    '#default_value' => '',
    '#required' => TRUE,
    '#disabled' => TRUE,
    '#attributes' => array('readonly' => 'readonly'),
    '#weight' => 5,
  );
  $form['reset-both'] = array(
    '#type' => 'markup',
    '#markup' => '<input id="edit-reset-both" class="form-reset" type="reset" value="' . t('Clear selected dates') . '" />',
    '#weight' => 6,
  );

  // Add submit button
  $form['actions'] = array(
    '#type' => 'actions',
    '#weight' => 1,
    'submit' => array(
      '#type' => 'submit',
      '#value' => t('Book this accommodation'),
      '#disabled' => TRUE,
    ),
  );

  $form['#action'] = url('<your confirmation page>');
  $form['#method'] = 'POST';

  // Add the necessary javascript from both this module and the availability calendars module
  availability_calendars_add_js();
  drupal_add_js(drupal_get_path('module', 'example') .'/example.reservation-formlet.js', array('type' => 'file'));
  drupal_add_js(array(
      'example' => array(
        "reservationFormlet" => array(
          'nid' => $node->nid,
          'settings' => availability_calendars_get_js_settings($node->nid, 'available'),
        ),
      ),
    ), array('type' => 'setting'));

  return $form;
}

Include it on your page where the calendar is displayed:

function example_preprocess_node(&$variables, $hook) {
  if (isset($variables['type']) && $variables['type'] === '<your content type>'){
    // Add the arrival date and duration fields
    $form = drupal_get_form('example_reservation_formlet', $variables['node']);
    $form['#weight'] = 11; // Below the calendar, which gets a default weight of 10
    $variables['content']['form'] = $form;
  }
}

The javascript (example.reservation-formlet.js):

(function($) {
Drupal.example = Drupal.example || {};

/**
 * @class Drupal.example.ReservationFormletHandler provides the glueing
 * code that connects the reservation formlet on the availability and
 * prices tab with the @see AvailabilityCalendars API object.
 */
Drupal.example.ReservationFormletHandler = function(calendar) {
  var from = null;
  var to = null;

  /**
   * Adds a date to the command.
   * - If it is the first date, it will be the from date.
   * - If it is the 2nd date, it will be the to date, swapping the from and to dates if needed.
   * - If it is a 3rd date, the from date will be set earlier if it is before the current from
   *   date, otherwise the to date will be changed.
   */
  function addDate(date) {
    if (from === null) {
      from = date;
    }
    else {
      if (to !== null) {
        calendar.restoreRangeState(from, to);
        if (date < from) {
          from = date;
        }
        else {
          to = date;
        }
      }
      else {
        if (date >= from) {
          to = date;
        }
        else {
          to = from;
          from = date;
        }
      }
    }
    show();
  };

  /**
   * @returns Boolean Whether the current form values are valid.
   */
  function isValid() {
    return to !== null && from !== null;
  };

  /**
   * Shows the current values, help texts, and enables the submit button.
   */
  function show() {
    if (from === null) {
      $('.form-reset').css('display', 'none');
      $('#edit-arrival')
        .attr('disabled', 'disabled')
        .val(Drupal.t('click on an available date in the calendar'));
      $('#edit-departure')
        .val('')
        .attr('disabled', 'disabled');
    }
    else {
      $('#edit-arrival')
        .val(from.toFormattedString('dd-mm-yyyy'))
        .removeAttr('disabled');
      if (to === null) {
        $('#edit-reset-from').css('display', 'inline-block');
        $('#edit-reset-both').css('display', 'none');
        $('#edit-departure')
          .val(Drupal.t('click on your departure date'))
          .attr('disabled', 'disabled');
        calendar.addExtraState(from, 'calselected');
      }
      else {
        $('#edit-departure')
          .val(to.toFormattedString('dd-mm-yyyy'))
          .removeAttr('disabled');
        $('#edit-reset-from').css('display', 'none');
        $('#edit-reset-both').css('display', 'inline-block');
        calendar.removeExtraState(from, 'calselected');
        if (isValid()) {
          calendar.changeRangeState(from, to, 'option');
        }
      }
    }
    if (isValid()) {
      $('#edit-submit').removeAttr('disabled');
    }
    else {
      $('#edit-submit').attr('disabled', 'disabled');
    }
  };

  function init() {
    // Attach to the calendar events.
    calendar.getCalendar().bind('calendarclick', function(event, date, nid) {
      addDate(date);
    });
    $('#edit-reset-from').click(function(event) {
      if (from !== null && to === null) {
        calendar.removeExtraState(from, 'calselected');
      }
      from = null;
      show();
      return false;
    });
    $('#edit-reset-both').click(function(event) {
      if (from !== null && to !== null) {
        calendar.restoreRangeState(from, to);
      }
      from = to = null;
      show();
      return false;
    });
  }

  init();
  show();

  // Nothing to return (no public interface)
  return true;
};

/**
 * Instantiates a @see Drupal.example.ReservationFormletHandler object on
 * load. It is assumed that only one calendar will be shown on a page. So
 * only 1 edit object will be created, thus this can be done in this file
 * self.
 */
Drupal.behaviors.exampleReservationFormlet = {
  attach: function(context, settings) {
    Drupal.example.reservationFormlet = new Drupal.example.ReservationFormletHandler(
      Drupal.availabilityCalendars.get(
        settings.example.reservationFormlet.nid,
        settings.example.reservationFormlet.settings
      )
    );
  }
};
})(jQuery);

And some css (add your colors, text-decoration, more borders, margins and paddings, etc. yourself):

/* In page booking formlet */
#example-reservation-formlet label {
  width: 140px;
}

#example-reservation-formlet input[type=text] {
  border-width: 0;
  background-color: transparent;
  width: 70px;
}

#example-reservation-formlet input[type=text][disabled] {
  width: 340px;
}

#example-reservation-formlet .form-item-arrival,
#example-reservation-formlet .form-item-departure {
  display: inline;
}

#example-reservation-formlet .form-reset {
  display: inline-block;
  width: 180px;
  cursor: pointer;
}

#example-reservation-formlet .form-submit {
  float: right;
  width: 200px;
}

#example-reservation-formlet .form-submit:active, #example-reservation-formlet .form-submit:hover {
  /* change colors */
}

#example-reservation-formlet .form-submit[disabled] {
  color: inherit;
  cursor: auto;
}
rogical’s picture

Status: Active » Needs review

really appreciate!

I will test it and feedback as soon as possible.

fietserwin’s picture

Awaiting your comments.

Note: see also these comments: http://drupal.org/node/306467#comment-1026074 and my reaction (introducing the above stuff): http://drupal.org/node/306467#comment-4216740

Note: edited the code by adding weights to the form elements (I removed them in stripping it to the bare necessary, but they are needed as the submit button floats right and thus must be rendered first for the other elements to appear to the left of it.)

rogical’s picture

StatusFileSize
new30.87 KB

I've copied your code as a module example, and figured out the form, the screenshot was attached.

But the javascript seems not working, there's no response when I click the calendar date.

BTW, when I using Availability Calendar, I keep thinking what's the better between Availability Calendar and 'Calendar + Date', then I aslo tested 'Calendar + Date'.

The advantage of 'Calendar + Date' is it can show the notes/node in the specific date square, while Availability Calendar's notes area are belong to weeks.

The shortage of 'Calendar + Date' is it's not good to show the available status, while AC is already be.

fietserwin’s picture

* does the javascript get loaded:
- availability-calendars.js
- example.reservation-formlet.js
(show source, ctrl + U in most browsers)

* Do you see any error messages in the browser about javascript errors? (firebug console)

* Does the cursor change into a pointer and does the cell border change color on hover?

rogical’s picture

StatusFileSize
new44.04 KB
new39.29 KB

the javascript were all loaded, and no errors found in the console.

no pointer and cell border color change when hover on the cell, as well as on click.

I'm using Drupal 7.2, and found that when loading the calendar page, it even brokes my login button on login block.

fietserwin’s picture

StatusFileSize
new2.4 KB

What browser are you using? (I'm currently testing older no-standard browsers, read IE8-, and looks like I am getting same problems)

I always develop on modern standard compliant browsers and only at the end of a project, as the customer does not come up with last minute changes anymore, I start testing IE. IE9 seems to work fine though.

Attached, a slightly modified module archive (mainly css to get what I intended: a form of just 2 lines).

rogical’s picture

actually I'm using the latest Firefox and Chromium, but none of them works. As I'm still using WinXP, so not tested with IE9 yet.

I have a online test envirionment, you can see it throungh:
http://www.cms-cloud.com/node/26

http://www.cms-cloud.com/node/add/availability-calendars

No errors throw out, but just doesn't work. I'm also try to debug, no progress so far as I'm not familiar with CA api yet.

fietserwin’s picture

Category: support » bug
Status: Needs review » Needs work

I had a look at your site, thank you for sharing. Please visit the settings page for this module and define which states are considered available. Due to an error in the install, the states that are created by default are all set to not available. Hence there is no day that is made selectable.

Please also note that IE8 (and probably older versions as well) will not work with the current version of Availability Calendars (a property of an object may not be called 'class' in IE8). So I changed the category of this issue to a bug report and will communicate here when I committed a patch. But please keep continue to use this issue for your findings regarding the custom code.

rogical’s picture

It works now!

Now I'm trying to figure out a submit solution, I prefer to use node type than webform.

As webform is a standalone data, not good compatible with views/panels yet, so I try to use node type.

Some modules may be used: references, node reference url, eva ... I'm still processing, maybe will create a new field or other stuffs.

I will feedback here later after get the submit solution done.

BTW, one suggestion here is: in the booking process, user may want to specify the time period, such as 2 hours in a day. I think you must have been used the Outlook Calendar in the Microsoft Windows, it can be booked on hours, minutes. This could be a new feature later.

fietserwin’s picture

one suggestion here is: in the booking process, user may want to specify the time period, such as 2 hours in a day.

Forget about that. This module will not do so. See #470994: search , especially comment #16. Use other modules, like the ones people suggested in #721762: Hourly booking or #306467: Integrate Availability Calendars with booking process (was: ability to reserve).

But I'm interested in your way of integrating the submit data, so keep posting.

fietserwin’s picture

Status: Needs work » Fixed

The fix for the IE8 error has been committed and can be found in the 7.x-2.x-dev version. But, please post your findings about how you integrated this into your booking process, and any other remarks about the above discussed functionality.

rogical’s picture

Thanks, I'm tied up a project recently, will not able to make my solution done soon.

My idea is make a new content type with a start/end date filed, and use panels to override the node/add form, and get the corresponding calendar to the form, when user click the calendar date, it will fill the date field in the content type.

Finally, user can view his own booking and author can view all the booking through views.

The approve process is difficult, ideally when the booking node was posted, and author can just click to approve on viewing the booking node. This is what I say may need to write a custom field.

As make a new field in D7 is not much difficult, so it's available.

fietserwin’s picture

As per #1151382: Use fields in Drupal 7, I'm currently converting the module to a field. I'm not sure if it will, but it might help you to better place and order all those fields/inputs. Stay tuned.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.