From 30ebde2a767ba2459318b01b8f2fad561e3cf728 Mon Sep 17 00:00:00 2001 From: Adrian Rollett Date: Mon, 15 Aug 2016 10:23:18 -0600 Subject: [PATCH] Add bat_booking module --- modules/bat_booking/bat_booking.admin.inc | 323 +++++++++ modules/bat_booking/bat_booking.info | 14 + modules/bat_booking/bat_booking.install | 200 ++++++ modules/bat_booking/bat_booking.module | 645 ++++++++++++++++++ .../bat_booking_example.info | 11 + .../bat_booking_example.install | 69 ++ .../bat_booking_example.module | 199 ++++++ .../booking_confirmation_page.tpl.php | 2 + .../views/bat_booking_example.views.inc | 18 + .../bat_booking_example_book_this_field.inc | 13 + .../bat_booking/bat_booking_type.admin.inc | 138 ++++ .../bat_booking/views/bat_booking.views.inc | 30 + .../bat_booking_handler_delete_link_field.inc | 47 ++ .../bat_booking_handler_edit_link_field.inc | 47 ++ .../views/bat_booking_handler_night_field.inc | 23 + 15 files changed, 1779 insertions(+) create mode 100644 modules/bat_booking/bat_booking.admin.inc create mode 100644 modules/bat_booking/bat_booking.info create mode 100644 modules/bat_booking/bat_booking.install create mode 100644 modules/bat_booking/bat_booking.module create mode 100644 modules/bat_booking/bat_booking_example/bat_booking_example.info create mode 100644 modules/bat_booking/bat_booking_example/bat_booking_example.install create mode 100644 modules/bat_booking/bat_booking_example/bat_booking_example.module create mode 100644 modules/bat_booking/bat_booking_example/booking_confirmation_page.tpl.php create mode 100644 modules/bat_booking/bat_booking_example/views/bat_booking_example.views.inc create mode 100644 modules/bat_booking/bat_booking_example/views/bat_booking_example_book_this_field.inc create mode 100644 modules/bat_booking/bat_booking_type.admin.inc create mode 100644 modules/bat_booking/views/bat_booking.views.inc create mode 100644 modules/bat_booking/views/bat_booking_handler_delete_link_field.inc create mode 100644 modules/bat_booking/views/bat_booking_handler_edit_link_field.inc create mode 100644 modules/bat_booking/views/bat_booking_handler_night_field.inc diff --git a/modules/bat_booking/bat_booking.admin.inc b/modules/bat_booking/bat_booking.admin.inc new file mode 100644 index 000000000..25e340469 --- /dev/null +++ b/modules/bat_booking/bat_booking.admin.inc @@ -0,0 +1,323 @@ +path . '/add']['title'] = 'Add a Booking'; + $items[$this->path . '/add']['description'] = 'Create a new booking.'; + $items[$this->path . '/add']['page callback'] = 'bat_booking_add_page'; + $items[$this->path . '/add']['access callback'] = 'bat_booking_add_access'; + unset($items[$this->path . '/add']['title callback']); + + // Add menu items to add each different type of bookings. + foreach (bat_booking_get_types() as $booking_type) { + $items[$this->path . '/add/' . $booking_type->type] = array( + 'title' => 'Add @booking_type_label booking', + 'title arguments' => array('@booking_type_label' => $booking_type->label), + 'page callback' => 'bat_booking_create_form_wrapper', + 'page arguments' => array($booking_type->type), + 'access callback' => 'bat_booking_access', + 'access arguments' => array('create', bat_booking_create(array('type' => $booking_type->type, 'uid' => 0))), + 'file' => 'bat_booking.admin.inc', + 'file path' => drupal_get_path('module', $this->entityInfo['module']), + ); + } + + return $items; + } + + /** + * Creates the markup for the add Booking Entities page within the class + * so it can easily be extended/overridden. + */ + public function addPage() { + $item = menu_get_item(); + $booking_types = bat_booking_get_types(); + + if (count($booking_types) == 1) { + $booking_type = reset($booking_types); + drupal_goto($this->path . '/add/' . $booking_type->type); + } + + $items = array(); + foreach ($booking_types as $booking_type) { + $items[] = array( + 'title' => t('Add @booking_type_label booking', array('@booking_type_label' => $booking_type->label)), + 'href' => $this->path . '/add/' . $booking_type->type, + 'description' => '', + ); + } + + return array( + '#theme' => 'bat_booking_add_list', + '#content' => $items, + ); + } +} + +/** + * + */ +function bat_booking_form($form, &$form_state, $booking, $op = 'edit') { + global $user; + // Add the breadcrumb for the form's location. + bat_booking_set_breadcrumb(); + drupal_set_title(t('Edit @booking_label', array('@booking_label' => $booking->label))); + + $booking->date = format_date($booking->created, 'custom', 'Y-m-d H:i:s O'); + $account = user_load($booking->uid); + $booking->author_name = isset($account->name) ? $account->name : ''; + + return bat_booking_edit_form($form, $form_state, $booking); +} + +/** + * Form callback wrapper: create a Booking. + * + * @param $type + * The Booking type for the booking to be created. + */ +function bat_booking_create_form_wrapper($type) { + global $user; + // Add the breadcrumb for the form's location. + bat_booking_set_breadcrumb(); + + $booking = bat_booking_create(array('type' => $type, 'uid' => $user->uid)); + $booking->created = REQUEST_TIME; + $booking->author_name = $user->name; + $booking->status = 1; + + return drupal_get_form('bat_booking_edit_form', $booking); +} + +/** + * Generates the booking editing form. + */ +function bat_booking_edit_form($form, &$form_state, $booking) { + // Add the field related form elements. + $form_state['bat_booking'] = $booking; + field_attach_form('bat_booking', $booking, $form, $form_state, isset($booking->language)? $booking->language : NULL); + $form['additional_settings'] = array( + '#type' => 'vertical_tabs', + '#weight' => 99, + ); + + // Type author information for administrators. + $form['author'] = array( + '#type' => 'fieldset', + '#access' => user_access('bypass bat_booking entities access'), + '#title' => t('Authoring information'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#group' => 'additional_settings', + '#attributes' => array( + 'class' => array('type-form-author'), + ), + '#attached' => array( + 'js' => array( + array( + 'type' => 'setting', + 'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))), + ), + ), + ), + '#weight' => 90, + ); + + $form['type'] = array( + '#type' => 'value', + '#value' => $booking->type, + ); + + $form['author']['author_name'] = array( + '#type' => 'textfield', + '#title' => t('Authored by'), + '#maxlength' => 60, + '#autocomplete_path' => 'user/autocomplete', + '#default_value' => !empty($booking->author_name) ? $booking->author_name : '', + '#weight' => -1, + '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))), + ); + $form['author']['date'] = array( + '#type' => 'textfield', + '#title' => t('Authored on'), + '#maxlength' => 25, + '#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => !empty($booking->date) ? date_format(date_create($booking->date), 'Y-m-d H:i:s O') : format_date($booking->created, 'custom', 'Y-m-d H:i:s O'), '%timezone' => !empty($booking->date) ? date_format(date_create($booking->date), 'O') : format_date($booking->created, 'custom', 'O'))), + '#default_value' => !empty($booking->date) ? $booking->date : '', + ); + + $form['options'] = array( + '#type' => 'fieldset', + '#access' => user_access('bypass bat_booking entities access'), + '#title' => t('Publishing options'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#group' => 'additional_settings', + '#attributes' => array( + 'class' => array('booking-form-published'), + ), + '#weight' => 95, + ); + $form['options']['status'] = array( + '#type' => 'checkbox', + '#title' => t('Published'), + '#default_value' => $booking->status, + ); + + $form['actions'] = array( + '#type' => 'actions', + '#tree' => FALSE, + ); + // We add the form's #submit array to this button along with the actual submit + // handler to preserve any submit handlers added by a form callback_wrapper. + $submit = array(); + if (!empty($form['#submit'])) { + $submit += $form['#submit']; + } + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save Booking'), + '#submit' => $submit + array('bat_booking_edit_form_submit'), + ); + if (!empty($booking->label) && bat_booking_access('delete', $booking)) { + $form['actions']['delete'] = array( + '#type' => 'submit', + '#value' => t('Delete Booking'), + '#submit' => $submit + array('bat_booking_form_submit_delete'), + '#weight' => 45, + ); + } + + // Depending on whether the form is in a popup or a normal page we need to change + // the behavior of the cancel button + if (isset($form_state['ajax']) && $form_state['ajax'] == TRUE) { + unset($form['actions']['cancel']); + } + else { + $form['actions']['cancel'] = array( + '#markup' => l(t('Cancel'), 'admin/bat/config/booking'), + '#weight' => 50, + ); + } + + $form['#validate'][] = 'bat_booking_edit_form_validate'; + + return $form; +} + +/** + * Form API validation callback for the booking form. + */ +function bat_booking_edit_form_validate(&$form, &$form_state) { + // Notify field widgets to validate their data. + entity_form_field_validate('bat_booking', $form, $form_state); +} + +/** + * Form API submit callback for the booking form. + */ +function bat_booking_edit_form_submit(&$form, &$form_state) { + $booking = entity_ui_controller('bat_booking')->entityFormSubmitBuildEntity($form, $form_state); + + $booking->created = !empty($booking->date) ? strtotime($booking->date) : REQUEST_TIME; + $booking->changed = time(); + + if (isset($booking->author_name)) { + if ($account = user_load_by_name($booking->author_name)) { + $booking->uid = $account->uid; + } + else { + $booking->uid = 0; + } + } + + $booking->save(); + drupal_set_message(t('Booking @label saved', array('@label' => $booking->label))); + + $form_state['redirect'] = 'admin/bat/config/booking'; +} + +/** + * Form API submit callback for the delete button. + */ +function bat_booking_form_submit_delete(&$form, &$form_state) { + if (isset($form_state['ajax'])) { + bat_booking_delete($form_state['bat_booking']); + drupal_set_message(t('The booking has been removed')); + $form_state['booking_deleted'] = TRUE; + } + else { + $destination = array(); + if (isset($_GET['destination'])) { + $destination = drupal_get_destination(); + unset($_GET['destination']); + } + + $form_state['redirect'] = array('admin/bat/config/booking/manage/' . $form_state['bat_booking']->booking_id . '/delete', array('query' => $destination)); + } +} + +/** + * Page to add Booking. + */ +function bat_booking_add_page() { + $controller = entity_ui_controller('bat_booking'); + return $controller->addPage(); +} + +/** + * Displays the list of available booking types for booking creation. + * + * @ingroup themeable + */ +function theme_bat_booking_add_list($variables) { + $content = $variables['content']; + + $output = ''; + if ($content) { + $output = '
'; + foreach ($content as $item) { + $output .= '
' . l($item['title'], $item['href']) . '
'; + $output .= '
' . filter_xss_admin($item['description']) . '
'; + } + $output .= '
'; + } + else { + if (user_access('administer bat_booking_type entities')) { + $output = '

' . t('Bookings cannot be added because you have not created any booking types yet. Go to the booking type creation page to add a new booking type.', array('@create-booking-type' => url('admin/bat/config/booking-types/add'))) . '

'; + } + else { + $output = '

' . t('No booking types have been created yet for you to use.') . '

'; + } + } + + return $output; +} + +/** + * Sets the breadcrumb for administrative BAT pages. + */ +function bat_booking_set_breadcrumb() { + $breadcrumb = array( + l(t('Home'), ''), + l(t('Administration'), 'admin'), + l(t('BAT'), 'admin/bat'), + l(t('Configuration'), 'admin/bat/config'), + l(t('BAT Bookings'), 'admin/bat/config/booking'), + ); + + drupal_set_breadcrumb($breadcrumb); +} diff --git a/modules/bat_booking/bat_booking.info b/modules/bat_booking/bat_booking.info new file mode 100644 index 000000000..ab47e55d7 --- /dev/null +++ b/modules/bat_booking/bat_booking.info @@ -0,0 +1,14 @@ +name = BAT Booking +description = BAT Booking +core = 7.x + +package = BAT + +dependencies[] = bat_event + +files[] = bat_booking.admin.inc +files[] = bat_booking_type.admin.inc +files[] = views/bat_booking.views.inc +files[] = views/bat_booking_handler_delete_link_field.inc +files[] = views/bat_booking_handler_edit_link_field.inc +files[] = views/bat_booking_handler_night_field.inc diff --git a/modules/bat_booking/bat_booking.install b/modules/bat_booking/bat_booking.install new file mode 100644 index 000000000..2fbe7be85 --- /dev/null +++ b/modules/bat_booking/bat_booking.install @@ -0,0 +1,200 @@ + 'The base table for Bookings.', + 'fields' => array( + 'booking_id' => array( + 'description' => 'Primary Key: Identifier for a Booking.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'type' => array( + 'description' => 'The {booking_type}.type of this Booking.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'language' => array( + 'description' => 'The language of the Booking.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'label' => array( + 'description' => 'The label of the Booking - a human-readable identifier.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'created' => array( + 'description' => 'The Unix timestamp when the Booking was created.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'changed' => array( + 'description' => 'The Unix timestamp when the Booking was most recently saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'uid' => array( + 'description' => 'The {users}.uid that created this booking.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0x01, + 'size' => 'tiny', + 'description' => 'The exportable status of the entity.', + ), + 'data' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + 'serialize' => TRUE, + 'description' => 'A serialized array of additional data.', + ), + ), + 'primary key' => array('booking_id'), + 'indexes' => array( + 'type' => array('type'), + ), + ); + + $schema['bat_booking_types'] = array( + 'description' => 'The base table for Types.', + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Primary Key: Identifier for a Type.', + ), + 'type' => array( + 'description' => 'The {type_bundle}.type of this Type.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), + 'label' => array( + 'description' => 'The human-readable name of this booking type.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'default_booking_label_field_name' => array( + 'type' => 'varchar', + 'not null' => FALSE, + 'length' => 32, + 'description' => 'The name of a field to use to retrieve label information.', + ), + 'data' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + 'serialize' => TRUE, + 'description' => 'A serialized array of additional data.', + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0x01, + 'size' => 'tiny', + 'description' => 'The exportable status of the entity.', + ), + 'module' => array( + 'description' => 'The name of the providing module if the entity has been defined in code.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), + ), + 'primary key' => array('id'), + 'unique keys' => array( + 'type' => array('type'), + ), + ); + + return $schema; +} + +/** + * Create "Standard" booking type. + */ +function bat_booking_create_standard_booking_type() { + $booking_type = bat_booking_type_create(array( + 'label' => 'Standard', + 'type' => 'standard', + )); + + $booking_type->save(); +} + +/** + * Add default label name field. + */ +function bat_booking_update_7100() { + $field = array( + 'type' => 'varchar', + 'not null' => FALSE, + 'length' => 32, + 'description' => 'The name of a field to use to retrieve label information.', + ); + + db_add_field('bat_booking_types', 'default_booking_label_field_name', $field); +} + +/** + * Rename 'name' field in 'label'. + */ +function bat_booking_update_7101() { + db_change_field('bat_bookings', 'name', 'label', array( + 'description' => 'The label of the Booking - a human-readable identifier.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + )); +} + +/** + * Add booking status field. + */ +function bat_booking_update_7102() { + $field = array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0x01, + 'size' => 'tiny', + 'description' => 'The exportable status of the entity.', + ); + + db_add_field('bat_bookings', 'status', $field); +} diff --git a/modules/bat_booking/bat_booking.module b/modules/bat_booking/bat_booking.module new file mode 100644 index 000000000..07446b14f --- /dev/null +++ b/modules/bat_booking/bat_booking.module @@ -0,0 +1,645 @@ + t('BAT Booking'), + // The entity class and controller class extend the classes provided by the + // Entity API. + 'entity class' => 'BatBooking', + 'controller class' => 'BatBookingController', + 'base table' => 'bat_bookings', + 'fieldable' => TRUE, + 'entity keys' => array( + 'id' => 'booking_id', + 'bundle' => 'type', + 'label' => 'label', + ), + // Bundles are defined by the booking bundles below. + 'bundles' => array(), + // Bundle keys tell the FieldAPI how to extract information from the bundle + // objects. + 'bundle keys' => array( + 'bundle' => 'type', + ), + 'label callback' => 'entity_class_label', + 'uri callback' => 'entity_class_uri', + 'creation callback' => 'bat_booking_create', + 'access callback' => 'bat_booking_access', + 'access arguments' => array( + 'user key' => 'uid', + 'access tag' => 'bat_booking_access', + ), + 'permission labels' => array( + 'singular' => t('booking'), + 'plural' => t('bookings'), + ), + 'module' => 'bat_booking', + // The information below is used by the BatBookingUIController (which extends + // the EntityDefaultUIController). + 'admin ui' => array( + 'path' => 'admin/bat/config/booking', + 'file' => 'bat_booking.admin.inc', + 'controller class' => 'BatBookingUIController', + 'menu wildcard' => '%bat_booking', + ), + 'metadata controller class' => 'BatBookingMetadataController', + 'translation' => array( + 'entity_translation' => array( + 'base path' => 'admin/bat/config/booking/%bat_booking', + 'default settings' => array( + 'default_language' => LANGUAGE_NONE, + 'hide_language_selector' => FALSE, + ), + ), + ), + ); + + $return['bat_booking_type'] = array( + 'label' => t('BAT Booking Type'), + 'entity class' => 'BatBookingType', + 'controller class' => 'BatBookingTypeController', + 'base table' => 'bat_booking_types', + 'fieldable' => TRUE, + 'bundle of' => 'bat_booking', + 'exportable' => TRUE, + 'entity keys' => array( + 'id' => 'id', + 'name' => 'type', + 'label' => 'label', + ), + 'access callback' => 'bat_booking_type_access', + 'module' => 'bat_booking', + // Enable the entity API's admin UI. + 'admin ui' => array( + 'path' => 'admin/bat/config/booking-types', + 'file' => 'bat_booking_type.admin.inc', + 'controller class' => 'BatBookingTypeUIController', + ), + ); + + return $return; +} + +/** + * Implements hook_entity_info_alter(). + * + * We are adding the info about the booking types via a hook to avoid a recursion + * issue as loading the room types requires the entity info as well. + */ +function bat_booking_entity_info_alter(&$entity_info) { + foreach (bat_booking_get_types() as $type => $info) { + $entity_info['bat_booking']['bundles'][$type] = array( + 'label' => $info->label, + 'admin' => array( + 'path' => 'admin/bat/config/booking-types/manage/%bat_booking_type', + 'real path' => 'admin/bat/config/booking-types/manage/' . $type, + 'bundle argument' => 5, + 'access arguments' => array('bypass bat_booking entities access'), + ), + ); + } +} + +/** + * Implements hook_permission(). + */ +function bat_booking_permission() { + $permissions = array(); + + // Permission for Bat Booking Types. + $permissions += array( + 'administer bat_booking_type entities' => array( + 'title' => t('Administer booking types'), + 'description' => t('Allows users to add booking types and configure their fields.'), + 'restrict access' => TRUE, + ), + ); + + // Permission for Booking page. + $permissions += array( + 'book units' => array( + 'title' => t('Book units'), + 'description' => t('Allows users to access booking page.'), + ), + ); + + $permissions += bat_entity_access_permissions('bat_booking'); + + return $permissions; +} + +/** + * Implements hook_views_api(). + */ +function bat_booking_views_api() { + return array( + 'api' => 3, + 'path' => drupal_get_path('module', 'bat_booking') . '/views', + ); +} + +/** + * Determines whether the given user has access to a unit. + * + * @param string $op + * The operation being performed. One of 'view', 'update', 'create', 'delete' + * or just 'edit' (being the same as 'create' or 'update'). + * @param BatBooking $booking + * Optionally a booking to check access for. If nothing is + * given, access for all bookings is determined. + * @param object $account + * The user to check for. Leave it to NULL to check for the global user. + * + * @return boolean + * Whether access is allowed or not. + */ +function bat_booking_access($op, $booking = NULL, $account = NULL) { + return bat_entity_access($op, $booking, $account, 'bat_booking'); +} + +/** + * Access callback: Checks whether the user has permission to add a booking. + * + * @return bool + * TRUE if the user has add permission, otherwise FALSE. + */ +function bat_booking_add_access() { + if (user_access('administer bat_booking_type entities')) { + return TRUE; + } + + $bundles = bat_booking_get_types(); + foreach ($bundles as $bundle) { + if (bat_booking_access('create', bat_booking_create(array('type' => $bundle->type, 'uid' => 0)))) { + return TRUE; + } + } + + return FALSE; +} + +/** + * The class used for booking entities + */ +class BatBooking extends Entity { + + public function __construct($values = array()) { + parent::__construct($values, 'bat_booking'); + } + + /** + * {@inheritdoc} + */ + protected function defaultLabel() { + // If the user has configured a field to store the booking name, return that + // field's value. + $booking_type = bat_booking_type_load($this->type); + if (isset($booking_type->default_booking_label_field_name) && ($booking_type->default_booking_label_field_name != '')) { + $event_wrapper = entity_metadata_wrapper('bat_booking', $this); + $value = $event_wrapper->{$booking_type->default_booking_label_field_name}->value(array('sanitize' => TRUE)); + // Handle entity reference fields - if this is an object, return its + // label. + if (is_object($value)) { + $field_info = field_info_field($booking_type->default_booking_label_field_name); + + if ($field_info['type'] == 'entityreference') { + return entity_label($field_info['settings']['target_type'], $value); + } + } + elseif ($value) { + return $value; + } + } + + // If we got this far, a field is not configured, we don't support its + // type, or the field is empty. Return booking label. + return $this->label; + } + +} + +/** + * The class used for booking type entities + */ +class BatBookingType extends Entity { + + /** + * The booking type. + * + * @var string + */ + public $type; + + /** + * The booking type label. + * + * @var string + */ + public $label; + + public function __construct($values = array()) { + parent::__construct($values, 'bat_booking_type'); + } + +} + +/** + * The MetadataController for BatBooking entities + */ +class BatBookingMetadataController extends EntityDefaultMetadataController { + + public function entityPropertyInfo() { + $info = parent::entityPropertyInfo(); + + $properties = array('booking_id', 'type', 'language', 'label', 'created', 'changed', 'uid'); + + foreach ($properties as $property) { + if (isset($info['bat_booking']['properties'][$property])) { + $info['bat_booking']['properties'][$property]['getter callback'] = 'entity_property_verbatim_get'; + $info['bat_booking']['properties'][$property]['setter callback'] = 'entity_property_verbatim_set'; + } + } + + return $info; + } + +} + +/** + * The Controller for BatBooking entities + */ +class BatBookingController extends EntityAPIController { + + public function __construct($entityType) { + parent::__construct($entityType); + } + + /** + * {@inheritdoc} + */ + public function create(array $values = array()) { + $values += array( + 'booking_id' => '', + 'is_new' => TRUE, + 'data' => '', + 'label' => '', + 'created' => '', + ); + + $booking = parent::create($values); + + return $booking; + } + + /** + * {@inheritdoc} + */ + public function save($entity) { + if (isset($entity->is_new) && $entity->is_new) { + parent::save($entity); + } + + // Set default value for label. + if (empty($entity->label)) { + $booking_type = bat_booking_type_load($entity->type); + $entity->label = $booking_type->label . ' ' . $entity->booking_id; + } + + parent::save($entity); + } + + /** + * Overriding the buildContent function to add entity specific fields. + */ + public function buildContent($entity, $view_mode = 'full', $langcode = NULL, $content = array()) { + $content = parent::buildContent($entity, $view_mode, $langcode, $content); + + return $content; + } + +} + +/** + * The Controller for BatBookingType entities + */ +class BatBookingTypeController extends EntityAPIControllerExportable { + + public function __construct($entityType) { + parent::__construct($entityType); + } + + public function create(array $values = array()) { + $values += array( + 'id' => '', + 'is_new' => TRUE, + 'data' => '', + ); + + $booking_type = parent::create($values); + + return $booking_type; + } + +} + +/** + * Access callback for the entity API. + */ +function bat_booking_type_access($op, $unit = NULL, $account = NULL) { + return user_access('administer bat_booking_type entities', $account); +} + +/** + * Create a booking object. + */ +function bat_booking_create($values = array()) { + return entity_get_controller('bat_booking')->create($values); +} + +/** + * Create a booking type object. + */ +function bat_booking_type_create($values = array()) { + return entity_get_controller('bat_booking_type')->create($values); +} + +/** + * Menu argument loader; Load a booking type by string. + * + * @param $type + * The machine-readable name of a booking type to load. + * @param bool $reset + * A boolean indicating whether the internal cache should be reset. + * + * @return array|false + * A booking type array or FALSE if $type does not exist. + */ +function bat_booking_type_load($type, $reset = FALSE) { + return bat_booking_get_types($type, $reset); +} + +/** + * Gets an array of all booking types, keyed by the type name. + * + * @param string $type_name + * If set, the type with the given name is returned. + * @param bool $reset + * A boolean indicating that the internal cache should be reset. + * + * @return BatBookingType[] + * Depending whether $type isset, an array of booking types or a single one. + */ +function bat_booking_get_types($type_name = NULL, $reset = FALSE) { + // entity_load() will get the Entity controller for our booking type + // entity and call the load function of that object. + $types = entity_load_multiple_by_name('bat_booking_type', isset($type_name) ? array($type_name) : FALSE); + return isset($type_name) ? reset($types) : $types; +} + +/** + * Saves a booking type to the db. + * + * @param BatBookingType $booking_type + * The booking type to save. + */ +function bat_booking_type_save(BatBookingType $booking_type) { + $booking_type->save(); +} + +/** + * Deletes a booking type from the db. + */ +function bat_booking_type_delete(BatBookingType $bundle) { + $bundle->delete(); +} + +/** + * Fetches a booking object. + * + * @param int $booking_id + * Integer specifying the booking id. + * @param bool $reset + * A boolean indicating whether the internal cache should be reset. + * @return BatBooking|false + * A fully-loaded $booking object or FALSE if it cannot be loaded. + * + * @see bat_booking_load_multiple() + */ +function bat_booking_load($booking_id, $reset = FALSE) { + $bookings = bat_booking_load_multiple(array($booking_id), array(), $reset); + return reset($bookings); +} + +/** + * Loads multiple bookings based on certain conditions. + * + * @param array $booking_ids + * An array of booking IDs. + * @param array $conditions + * An array of conditions to match against the {bat_bookings} table. + * @param bool $reset + * A boolean indicating that the internal cache should be reset. + * + * @return array + * An array of booking objects, indexed by booking_id. + * + * @see entity_load() + * @see bat_booking_load() + */ +function bat_booking_load_multiple($booking_ids = array(), $conditions = array(), $reset = FALSE) { + return entity_load('bat_booking', $booking_ids, $conditions, $reset); +} + +/** + * Deletes a Bat Booking. + * + * @param BatBooking $booking + * The BatBooking object that represents the booking to delete. + */ +function bat_booking_delete(BatBooking $booking) { + $booking->delete(); +} + +/** + * Implements hook_theme(). + */ +function bat_booking_theme() { + return array( + 'bat_booking_add_list' => array( + 'variables' => array('content' => array()), + 'file' => 'bat_booking.admin.inc', + ), + ); +} + +/** + * Add "Start Date" field. + */ +function bat_booking_add_start_date_field($type_bundle) { + field_info_cache_clear(); + + // "booking_start_date" field. + if (field_read_field('booking_start_date') === FALSE) { + $field = array( + 'field_name' => 'booking_start_date', + 'type' => 'datetime', + 'cardinality' => 1, + 'locked' => 1, + 'settings' => array( + 'cache_count' => 4, + 'cache_enabled' => 0, + 'granularity' => array( + 'day' => 'day', + 'hour' => 'hour', + 'minute' => 'minute', + 'month' => 'month', + 'second' => 0, + 'year' => 'year', + ), + 'profile2_private' => FALSE, + 'timezone_db' => '', + 'todate' => '', + 'tz_handling' => 'none', + ), + ); + field_create_field($field); + } + + field_cache_clear(); + + // "booking_start_date" field instance. + if (field_read_instance('bat_booking', 'booking_start_date', $type_bundle) === FALSE) { + $instance = array( + 'field_name' => 'booking_start_date', + 'entity_type' => 'bat_booking', + 'label' => 'Start Date', + 'bundle' => $type_bundle, + 'required' => FALSE, + 'widget' => array( + 'type' => 'date_popup', + ), + 'settings' => array( + 'default_value' => 'blank', + 'default_value2' => 'same', + 'default_value_code' => '', + 'default_value_code2' => '', + 'user_register_form' => FALSE, + ), + ); + field_create_instance($instance); + } +} + +/** + * Add "End Date" field. + */ +function bat_booking_add_end_date_field($type_bundle) { + field_info_cache_clear(); + + // "booking_end_date" field. + if (field_read_field('booking_end_date') === FALSE) { + $field = array( + 'field_name' => 'booking_end_date', + 'type' => 'datetime', + 'cardinality' => 1, + 'locked' => 1, + 'settings' => array( + 'cache_count' => 4, + 'cache_enabled' => 0, + 'granularity' => array( + 'day' => 'day', + 'hour' => 'hour', + 'minute' => 'minute', + 'month' => 'month', + 'second' => 0, + 'year' => 'year', + ), + 'profile2_private' => FALSE, + 'timezone_db' => '', + 'todate' => '', + 'tz_handling' => 'none', + ), + ); + field_create_field($field); + } + + field_cache_clear(); + + // "booking_end_date" field instance. + if (field_read_instance('bat_booking', 'booking_end_date', $type_bundle) === FALSE) { + $instance = array( + 'field_name' => 'booking_end_date', + 'entity_type' => 'bat_booking', + 'label' => 'End Date', + 'bundle' => $type_bundle, + 'required' => FALSE, + 'widget' => array( + 'type' => 'date_popup', + ), + 'settings' => array( + 'default_value' => 'blank', + 'default_value2' => 'same', + 'default_value_code' => '', + 'default_value_code2' => '', + 'user_register_form' => FALSE, + ), + ); + field_create_instance($instance); + } +} + +/** + * Add "Event" reference field. + */ +function bat_booking_add_event_reference_field($type_bundle) { + field_info_cache_clear(); + + // "booking_event_reference" field. + if (field_read_field('booking_event_reference') === FALSE) { + $field = array( + 'field_name' => 'booking_event_reference', + 'type' => 'entityreference', + 'cardinality' => 1, + 'locked' => 1, + 'settings' => array( + 'target_type' => 'bat_event', + ), + ); + field_create_field($field); + } + + field_cache_clear(); + + // "booking_event_reference" field instance. + if (field_read_instance('bat_booking', 'booking_event_reference', $type_bundle) === FALSE) { + $instance = array( + 'field_name' => 'booking_event_reference', + 'entity_type' => 'bat_booking', + 'label' => 'Event', + 'bundle' => $type_bundle, + 'required' => FALSE, + 'widget' => array( + 'type' => 'entityreference_autocomplete', + ), + ); + field_create_instance($instance); + } +} + +/** + * Implements hook_entity_insert(). + */ +function bat_booking_entity_insert($entity, $type) { + if ($type == 'bat_booking_type') { + bat_booking_add_start_date_field($entity->type); + bat_booking_add_end_date_field($entity->type); + bat_booking_add_event_reference_field($entity->type); + } +} diff --git a/modules/bat_booking/bat_booking_example/bat_booking_example.info b/modules/bat_booking/bat_booking_example/bat_booking_example.info new file mode 100644 index 000000000..27ff6f3d1 --- /dev/null +++ b/modules/bat_booking/bat_booking_example/bat_booking_example.info @@ -0,0 +1,11 @@ +name = BAT Booking Example +description = BAT Booking Example +core = 7.x + +package = BAT + +dependencies[] = bat_booking +dependencies[] = bat_facets + +files[] = views/bat_booking_example.views.inc +files[] = views/bat_booking_example_book_this_field.inc diff --git a/modules/bat_booking/bat_booking_example/bat_booking_example.install b/modules/bat_booking/bat_booking_example/bat_booking_example.install new file mode 100644 index 000000000..df0f0bf9e --- /dev/null +++ b/modules/bat_booking/bat_booking_example/bat_booking_example.install @@ -0,0 +1,69 @@ + 'Availability Example', + 'type' => 'availability_example', + 'fixed_event_states' => 1, + 'event_granularity' => 'bat_daily', + )); + + bat_event_type_save($event); +} + +/** + * Creates the default event states. + */ +function bat_booking_example_create_standard_event_states() { + $event_state = array( + 'label' => 'Available', + 'color' => '#8BA175', + 'calendar_label' => 'AV', + ); + + bat_event_save_state($event_state, 'availability_example'); + + $event_state = array( + 'label' => 'Not Available', + 'color' => '#CC2727', + 'calendar_label' => 'N/A', + ); + + bat_event_save_state($event_state, 'availability_example'); + + $event_state = array( + 'label' => 'Booked', + 'color' => '#1A1A73', + 'calendar_label' => 'Booked', + 'blocking' => 1, + ); + + bat_event_save_state($event_state, 'availability_example'); +} + +/** + * Delete the "Availability Example" event type. + */ +function bat_booking_example_delete_availability_event_type() { + if ($event_type = bat_event_type_load('availability_example')) { + bat_event_type_delete($event_type); + } +} diff --git a/modules/bat_booking/bat_booking_example/bat_booking_example.module b/modules/bat_booking/bat_booking_example/bat_booking_example.module new file mode 100644 index 000000000..33565a199 --- /dev/null +++ b/modules/bat_booking/bat_booking_example/bat_booking_example.module @@ -0,0 +1,199 @@ + 'Booking', + 'page callback' => 'bat_booking_confirmation_page', + 'page arguments' => array(1, 2, 3), + 'access arguments' => array('book units'), + 'type' => MENU_CALLBACK, + ); + + return $items; +} + +/** + * Implements hook_theme(). + */ +function bat_booking_example_theme() { + return array( + 'booking_confirmation_page' => array( + 'template' => 'booking_confirmation_page', + 'variables' => array( + 'header' => NULL, + 'form' => NULL, + ), + ), + ); +} + +/** + * Implements hook_views_api(). + */ +function bat_booking_example_views_api() { + return array( + 'api' => 3, + 'path' => drupal_get_path('module', 'bat_booking_example') . '/views', + ); +} + +/** + * + */ +function bat_booking_confirmation_page($start_date, $end_date, $type_id) { + $header = $start_date->format('Y-m-d') . ' - ' . $end_date->format('Y-m-d'); + $form = drupal_get_form('bat_booking_confirmation_form', $start_date, $end_date, $type_id); + + return array( + '#theme' => 'booking_confirmation_page', + '#header' => $header, + '#form' => $form, + ); +} + +/** + * + */ +function bat_booking_confirmation_form($form, &$form_state, $start_date, $end_date, $type_id) { + $form['start_date'] = array( + '#type' => 'hidden', + '#value' => $start_date, + ); + + $form['end_date'] = array( + '#type' => 'hidden', + '#value' => $end_date, + ); + + $form['type_id'] = array( + '#type' => 'hidden', + '#value' => $type_id, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => 'Confirm booking', + ); + + return $form; +} + +/** + * + */ +function bat_booking_confirmation_form_submit($form, &$form_state) { + global $user; + + $event_type = 'availability_example'; + + $start_date = $form_state['values']['start_date']; + $end_date = $form_state['values']['end_date']; + $end_date->sub(new DateInterval('PT1M')); + + $type_id = $form_state['values']['type_id']; + + $state_ids = array_keys(bat_event_get_states($event_type)); + + $state_store = new DrupalDBStore($event_type, DrupalDBStore::BAT_STATE); + + $valid_states = array_merge(array(0), array_slice($state_ids, 0, 1)); + + $drupal_units = bat_unit_load_multiple(FALSE, array('type_id' => $type_id)); + $bat_units = array(); + foreach ($drupal_units as $unit_id => $unit) { + $bat_units[] = new Unit($unit_id, $unit->getEventDefaultValue($event_type)); + } + + if (count($bat_units)) { + $calendar = new Calendar($bat_units, $state_store); + + $response = $calendar->getMatchingUnits($start_date, $end_date, $valid_states, array()); + $valid_unit_ids = array_keys($response->getIncluded()); + + if (count($valid_unit_ids)) { + // Create a new Event. + $event = bat_event_create(array( + 'type' => $event_type, + 'start_date' => $start_date->format('Y-m-d H:i:s'), + 'end_date' => $end_date->format('Y-m-d H:i:s'), + 'uid' => $user->uid, + )); + + $event->event_bat_unit_reference[LANGUAGE_NONE][0]['target_id'] = reset($valid_unit_ids); + $event->event_state_reference[LANGUAGE_NONE][0]['state_id'] = end($state_ids); + + $event->save(); + + // Create a new Booking. + $booking = bat_booking_create(array( + 'type' => 'standard', + 'label' => 'Example Booking', + )); + + $booking->booking_start_date[LANGUAGE_NONE][0]['value'] = $start_date->format('Y-m-d H:i:s'); + $booking->booking_end_date[LANGUAGE_NONE][0]['value'] = $end_date->format('Y-m-d H:i:s'); + $booking->booking_event_reference[LANGUAGE_NONE][0]['target_id'] = $event->event_id; + + $booking->save(); + + drupal_set_message(t('Booking created')); + } + else { + drupal_set_message(t('No units'), 'error'); + } + } +} + +/** + * Loads a DateTime object from a string. + * + * @param string $start_date + * Expects to see a date in the form Y-m-d + * + * @return DateTime + * Object or null if invalid + */ +function start_date_load($start_date) { + $start_date = check_plain($start_date); + + try { + $sd = new DateTime($start_date); + } + catch (Exception $e) { + $sd = 0; + } + + return $sd; +} + +/** + * Loads a DateTime object from a string. + * + * @param string $end_date + * Expects to see a date in the form Y-m-d + * + * @return DateTime + * Object or null if invalid + */ +function end_date_load($end_date) { + $end_date = check_plain($end_date); + + try { + $ed = new DateTime($end_date); + } + catch (Exception $e) { + $ed = 0; + } + + return $ed; +} diff --git a/modules/bat_booking/bat_booking_example/booking_confirmation_page.tpl.php b/modules/bat_booking/bat_booking_example/booking_confirmation_page.tpl.php new file mode 100644 index 000000000..14345a6af --- /dev/null +++ b/modules/bat_booking/bat_booking_example/booking_confirmation_page.tpl.php @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/modules/bat_booking/bat_booking_example/views/bat_booking_example.views.inc b/modules/bat_booking/bat_booking_example/views/bat_booking_example.views.inc new file mode 100644 index 000000000..f32ce34bb --- /dev/null +++ b/modules/bat_booking/bat_booking_example/views/bat_booking_example.views.inc @@ -0,0 +1,18 @@ +item_type == 'bat_type') { + $data['search_api_index_' . $index->machine_name]['book_this'] = array( + 'field' => array( + 'title' => t('Book this'), + 'help' => t('Display links to create a new booking.'), + 'handler' => 'bat_booking_example_book_this_field', + ), + ); + } + } +} diff --git a/modules/bat_booking/bat_booking_example/views/bat_booking_example_book_this_field.inc b/modules/bat_booking/bat_booking_example/views/bat_booking_example_book_this_field.inc new file mode 100644 index 000000000..3d28f4e03 --- /dev/null +++ b/modules/bat_booking/bat_booking_example/views/bat_booking_example_book_this_field.inc @@ -0,0 +1,13 @@ +entity); + } + +} diff --git a/modules/bat_booking/bat_booking_type.admin.inc b/modules/bat_booking/bat_booking_type.admin.inc new file mode 100644 index 000000000..26d5c44c3 --- /dev/null +++ b/modules/bat_booking/bat_booking_type.admin.inc @@ -0,0 +1,138 @@ +label .= ' (cloned)'; + $booking_type->type = ''; + } + + $form['label'] = array( + '#title' => t('Booking type name'), + '#type' => 'textfield', + '#default_value' => $booking_type->label, + '#description' => t('The human-readable name of this booking type.'), + '#required' => TRUE, + '#size' => 30, + '#weight' => -100, + ); + + // Machine-readable type name. + $form['type'] = array( + '#type' => 'machine_name', + '#default_value' => isset($booking_type->type) ? $booking_type->type : '', + '#maxlength' => 32, + '#machine_name' => array( + 'exists' => 'bat_booking_get_types', + 'source' => array('label'), + ), + '#description' => t('A unique machine-readable name for this booking type. It must only contain lowercase letters, numbers, and underscores.'), + '#weight' => -99, + ); + + if ($op == 'edit') { + $form['type']['#disabled'] = TRUE; + } + + // Add the field related form elements. + $form_state['bat_booking_type'] = $booking_type; + field_attach_form('bat_booking_type', $booking_type, $form, $form_state); + + $form['additional_settings'] = array( + '#type' => 'vertical_tabs', + '#weight' => 99, + ); + + if (!isset($booking_type->is_new)) { + $fields_options = array(); + $fields = field_info_instances('bat_booking', $booking_type->type); + + foreach ($fields as $field) { + $field_info = field_info_field($field['field_name']); + $fields_options[$field['field_name']] = $field['field_name']; + } + + $form['event_label'] = array( + '#type' => 'fieldset', + '#group' => 'additional_settings', + '#title' => t('Label Source'), + '#tree' => TRUE, + '#weight' => 80, + ); + + $form['event_label']['default_booking_label_field_name'] = array( + '#type' => 'select', + '#title' => t('Select your label field', array('@booking' => $booking_type->label)), + '#default_value' => isset($booking_type->default_booking_label_field_name) ? $booking_type->default_booking_label_field_name : NULL, + '#empty_option' => t('- Select a field -'), + '#description' => t('If you select a field here, its value will be used as the label for your booking. BAT will fall back to using the property name as the label if the field has no value.'), + '#options' => $fields_options, + ); + } + + $form['actions'] = array( + '#type' => 'actions', + '#tree' => FALSE, + ); + + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save booking type'), + '#weight' => 100, + '#submit' => array('bat_booking_type_form_submit'), + ); + + $form['#validate'][] = 'bat_booking_type_form_validate'; + return $form; +} + +/** + * Form API validation callback for the booking type form. + */ +function bat_booking_type_form_validate(&$form, &$form_state) { + // Notify field widgets to validate their data. + entity_form_field_validate('bat_booking_type', $form, $form_state); +} + +/** + * Form API submit callback for the booking type form. + */ +function bat_booking_type_form_submit(&$form, &$form_state) { + if (isset($form_state['values']['event_label']['default_booking_label_field_name'])) { + $form_state['bat_booking_type']->default_booking_label_field_name = $form_state['values']['event_label']['default_booking_label_field_name']; + } + + $booking_type = entity_ui_controller('bat_booking_type')->entityFormSubmitBuildEntity($form, $form_state); + // Save and go back. + $booking_type->save(); + + $form_state['booking_type'] = $booking_type; + + $form_state['redirect'] = 'admin/bat/config/booking-types'; +} diff --git a/modules/bat_booking/views/bat_booking.views.inc b/modules/bat_booking/views/bat_booking.views.inc new file mode 100644 index 000000000..f9838e68c --- /dev/null +++ b/modules/bat_booking/views/bat_booking.views.inc @@ -0,0 +1,30 @@ + array( + 'title' => t('Edit Link'), + 'help' => t('Provide a link to the edit form for the booking.'), + 'handler' => 'bat_booking_handler_edit_link_field', + ), + ); + $data['bat_bookings']['delete_unit'] = array( + 'field' => array( + 'title' => t('Delete Link'), + 'help' => t('Provide a link to delete the booking.'), + 'handler' => 'bat_booking_handler_delete_link_field', + ), + ); + + $data['bat_bookings']['nights'] = array( + 'field' => array( + 'title' => t('Nights'), + 'help' => t('Provide number of nights.'), + 'handler' => 'bat_booking_handler_night_field', + ), + ); +} diff --git a/modules/bat_booking/views/bat_booking_handler_delete_link_field.inc b/modules/bat_booking/views/bat_booking_handler_delete_link_field.inc new file mode 100644 index 000000000..c567ab354 --- /dev/null +++ b/modules/bat_booking/views/bat_booking_handler_delete_link_field.inc @@ -0,0 +1,47 @@ +additional_fields['booking_id'] = 'booking_id'; + } + + function option_definition() { + $options = parent::option_definition(); + + $options['text'] = array('default' => '', 'translatable' => TRUE); + + return $options; + } + + function options_form(&$form, &$form_state) { + parent::options_form($form, $form_state); + + $form['text'] = array( + '#type' => 'textfield', + '#title' => t('Text to display'), + '#default_value' => $this->options['text'], + ); + } + + function query() { + $this->ensure_my_table(); + $this->add_additional_fields(); + } + + function render($values) { + $text = !empty($this->options['text']) ? $this->options['text'] : t('delete'); + $booking_id = $values->{$this->aliases['booking_id']}; + + return l($text, 'admin/bat/config/booking/manage/' . $booking_id . '/delete', array('query' => drupal_get_destination())); + } + +} diff --git a/modules/bat_booking/views/bat_booking_handler_edit_link_field.inc b/modules/bat_booking/views/bat_booking_handler_edit_link_field.inc new file mode 100644 index 000000000..a894daa03 --- /dev/null +++ b/modules/bat_booking/views/bat_booking_handler_edit_link_field.inc @@ -0,0 +1,47 @@ +additional_fields['booking_id'] = 'booking_id'; + } + + function option_definition() { + $options = parent::option_definition(); + + $options['text'] = array('default' => '', 'translatable' => TRUE); + + return $options; + } + + function options_form(&$form, &$form_state) { + parent::options_form($form, $form_state); + + $form['text'] = array( + '#type' => 'textfield', + '#title' => t('Text to display'), + '#default_value' => $this->options['text'], + ); + } + + function query() { + $this->ensure_my_table(); + $this->add_additional_fields(); + } + + function render($values) { + $text = !empty($this->options['text']) ? $this->options['text'] : t('edit'); + $booking_id = $values->{$this->aliases['booking_id']}; + + return l($text, 'admin/bat/config/booking/manage/' . $booking_id, array('query' => drupal_get_destination())); + } + +} diff --git a/modules/bat_booking/views/bat_booking_handler_night_field.inc b/modules/bat_booking/views/bat_booking_handler_night_field.inc new file mode 100644 index 000000000..2833ac8a1 --- /dev/null +++ b/modules/bat_booking/views/bat_booking_handler_night_field.inc @@ -0,0 +1,23 @@ +additional_fields['booking_id'] = 'booking_id'; + } + + function query() { + $this->ensure_my_table(); + $this->add_additional_fields(); + } + + function render($values) { + $booking = bat_booking_load($values->{$this->aliases['booking_id']}); + + $start_date = new DateTime($booking->booking_start_date[LANGUAGE_NONE][0]['value']); + $end_date = new DateTime($booking->booking_end_date[LANGUAGE_NONE][0]['value']); + + return $end_date->diff($start_date)->days; + } +}