You are here

bootstrap_tour.module in Bootstrap Tour 7.2

Same filename and directory in other branches
  1. 7 bootstrap_tour.module

File

bootstrap_tour.module
View source
<?php

/**
 * @file
 * bootstrap_tour.module
 */

/**
 * Implements hook_entity_info().
 */
function bootstrap_tour_entity_info() {

  // If this is called too early before update runs, it'll fatal.
  if (!module_exists('inline_entity_form')) {
    return array();
  }
  $bootstrap_tour_entity_info['bootstrap_tour'] = array(
    'label' => t('Site Tour'),
    'entity class' => 'Entity',
    'controller class' => 'BootstrapTourEntityController',
    'base table' => 'bootstrap_tour_tour',
    'fieldable' => TRUE,
    'exportable' => TRUE,
    'entity keys' => array(
      'id' => 'bootstrap_tour_id',
      'label' => 'title',
      'name' => 'name',
    ),
    'static cache' => TRUE,
    'admin ui' => array(
      'path' => 'admin/structure/tours',
      'controller class' => 'BootstrapTourUIController',
      'file' => 'includes/bootstrap_tour.admin.inc',
    ),
    'module' => 'bootstrap_tour',
    'access callback' => 'bootstrap_tour_admin_access',
    'views controller class' => 'EntityDefaultViewsController',
    'bundles' => array(
      // Entity defaults to entity type for single bundles.
      'bootstrap_tour' => array(
        'label' => t('Site Tour'),
        'admin' => array(
          'path' => 'admin/structure/tours',
          'access arguments' => array(
            'administer bootstrap tours',
          ),
        ),
      ),
    ),
  );
  $bootstrap_tour_entity_info['bootstrap_tour_step'] = array(
    'label' => t('Site Tour Step'),
    'entity class' => 'Entity',
    'controller class' => 'EntityAPIController',
    'base table' => 'bootstrap_tour_step',
    'fieldable' => FALSE,
    'entity keys' => array(
      'id' => 'bootstrap_tour_step_id',
      'label' => 'title',
      'name' => 'name',
    ),
    'static cache' => TRUE,
    'module' => 'bootstrap_tour',
    'access callback' => 'bootstrap_tour_admin_access',
    'views controller class' => 'EntityDefaultViewsController',
    'inline entity form' => array(
      'controller' => 'BootstrapTourStepInlineEntityFormController',
      'file' => 'includes/bootstrap_tour_step.inline_entity_form.inc',
    ),
  );
  return $bootstrap_tour_entity_info;
}

/**
 * Implements hook_menu().
 */
function bootstrap_tour_menu() {
  $items = array();
  $items['admin/structure/tours'] = array(
    'title' => 'Site Tours',
    'access arguments' => array(
      'administer bootstrap tours',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['bootstrap_tour/ajax/end_current_tour'] = array(
    'title' => 'AJAX callback to end current tour',
    'page callback' => 'bootstrap_tour_end_current_tour',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_permission().
 */
function bootstrap_tour_permission() {
  return array(
    'administer bootstrap tours' => array(
      'title' => t('Administer bootstrap tours'),
      'description' => t('Allows a user to add, edit, and delete bootstrap tours.'),
    ),
  );
}

/**
 * Bootstrap Tour access callback.
 */
function bootstrap_tour_admin_access($op, $tour = NULL, $account = NULL) {
  return user_access('administer bootstrap tours', $account);
}

/**
 * Implements hook_views_api().
 */
function bootstrap_tour_views_api() {
  return array(
    'api' => 3,
  );
}

/**
 * Implementation of hook_libraries_info().
 * Tell Drupal about the Bootstrap Tour library.
 */
function bootstrap_tour_libraries_info() {
  $libraries['bootstrap_tour'] = array(
    'name' => 'Bootstrap Tour',
    'vendor url' => 'http://bootstraptour.com',
    'download url' => 'https://github.com/sorich87/bootstrap-tour/releases',
    'path' => 'build',
    'version' => '0.5.0',
    // @TODO: Move this into version_callback and version_arguments.
    'files' => array(
      'js' => array(
        'js/bootstrap-tour.min.js',
      ),
      'css' => array(
        'css/bootstrap-tour.min.css',
      ),
    ),
    'variants' => array(
      'minified' => array(
        'files' => array(
          'js' => array(
            'js/bootstrap-tour.min.js',
          ),
          'css' => array(
            'css/bootstrap-tour.min.css',
          ),
        ),
      ),
      'source' => array(
        'files' => array(
          'js' => array(
            'js/bootstrap-tour.js',
          ),
          'css' => array(
            'css/bootstrap-tour.css',
          ),
        ),
      ),
    ),
  );
  return $libraries;
}

/**
 * Remove tour cookies from session
 */
function bootstrap_tour_cleanup() {
  if (isset($_SESSION['tour'])) {
    unset($_SESSION['tour']);
  }
  if (isset($_SESSION['nexttour'])) {
    unset($_SESSION['nexttour']);
  }
}

/**
 * Helper function to load a file from local disk or from remote url
 * @param $type
 * @param $filename
 * @param $uri
 */
function bootstrap_tour_load_file($type, $filename, $uri) {
  $file = 'public://' . $filename;
  $realfile = drupal_realpath($file);
  $options = NULL;
  if (!file_exists($realfile)) {
    global $base_url;
    $base = parse_url($base_url);
    $url = $base['scheme'] . '://' . $uri;
    if (system_retrieve_file($url, $file, FALSE, FILE_EXISTS_REPLACE) === FALSE) {

      // If we can't cache the file locally, use the remote CDN link
      $file = $url;
      $options = 'external';
    }
  }
  if ($type == 'js') {
    drupal_add_js($file, $options);
  }
  else {
    drupal_add_css($file, $options);
  }
}

/**
 * Helper function to actually run a tour. Can be called from anywhere.
 */
function bootstrap_tour_run_tour($id, $force = FALSE) {
  $tour = bootstrap_tour_load($id);
  if (!bootstrap_tour_access($tour) || empty($tour->steps)) {
    bootstrap_tour_cleanup();
    return;
  }
  if ($tour->autorun && !empty($tour->start_path)) {
    $tour->steps[0]->path = $tour->start_path;
  }
  $first_path = $tour->steps[0]->path;
  if (empty($first_path)) {
    bootstrap_tour_cleanup();
    return;
  }
  if (count($tour->steps) > 1) {

    // Set this as the current tour in the session.
    $_SESSION['tour'] = $tour->name;
  }
  if ($first_path == current_path() || $first_path == request_path() || $first_path == '<front>' && request_path() == '') {
    if (empty($_GET['step']) || $_GET['step'] == 0) {

      // We're starting the tour over.
      if (isset($_SESSION['nexttour'])) {
        unset($_SESSION['nexttour']);
      }
    }
  }
  drupal_alter('bootstrap_tour', $tour);
  $step_path = '';
  foreach ($tour->steps as $key => &$step) {

    // If a path isn't specified, then use the path from the previous step.
    if ($step->path) {
      $step_path = $step->path;
    }
    else {
      $step->path = $step_path;
    }

    // Determine we are on the first step of the tour.
    if ($key == 0 && ($step->path == current_path() || $step->path == request_path() || $step->path == '<front>' && request_path() == '')) {
      if (!empty($_GET['tour']) && (empty($_GET['step']) || $_GET['step'] == 0)) {
        $tour->isFirstStep = TRUE;
      }
    }

    // Filter user supplied content.
    $step->title = check_plain($step->title);
    $step->content = check_markup($step->content, $step->content_text_format);
  }
  $tour->force = $force;
  $tour->cleanUrls = variable_get('clean_url', 0);
  drupal_add_js(array(
    'bootstrapTour' => array(
      'tour' => $tour,
    ),
  ), 'setting');
  if (module_exists('libraries') && ($library = libraries_detect('bootstrap_tour')) && !empty($library['installed'])) {
    libraries_load('bootstrap_tour');
  }
  else {

    // Grab the Bootstrap Tour JS from CDNJS if the library isn't installed.
    bootstrap_tour_load_file('css', 'bootstrap-tour.min.css', 'cdnjs.cloudflare.com/ajax/libs/bootstrap-tour/0.6.1/css/bootstrap-tour.min.css');
    bootstrap_tour_load_file('js', 'bootstrap-tour.js', 'cdnjs.cloudflare.com/ajax/libs/bootstrap-tour/0.6.1/js/bootstrap-tour.js');
  }
  drupal_add_js(drupal_get_path('module', 'bootstrap_tour') . '/js/bootstrap-tour.js');
  drupal_add_css(drupal_get_path('module', 'bootstrap_tour') . '/css/bootstrap-tour.css');
}

/**
 * Implementation of hook_init().
 * Load all the tours and figure out if any are set to auto-run and start on the current page.
 * If so, go ahead and run that one.
 */
function bootstrap_tour_page_build(&$page) {

  // Try and detect if we are in an AJAX request and bail if so.
  if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
    return;
  }
  $tours = bootstrap_tour_load_config();

  // Force a tour to run if the tour name is in the session vars.
  $current_name = !empty($_SESSION['tour']) ? $_SESSION['tour'] : NULL;

  // We can also jump to a new tour by specifying ?tour=tourname in the query
  // string, but only if it's the first step OR in the tour we were already
  // on. This is to prevent weird behavior if we switch domains on the same
  // site - ie. we shouldn't be able to start a new tour mid-stream.
  if (!empty($_GET['tour']) && ($_GET['tour'] == $current_name || empty($_GET['step']))) {
    $current_name = $_GET['tour'];
  }
  if (!empty($current_name)) {
    foreach ($tours as $id => $tour) {
      if ($tour->name == $current_name) {
        bootstrap_tour_run_tour($id, TRUE);
        return;
      }
    }
  }

  // Force the next tour to run if the tour name is in the session vars.
  if (!empty($_SESSION['nexttour'])) {
    foreach ($tours as $id => $tour) {
      if ($_SESSION['nexttour'] == $tour->name) {
        bootstrap_tour_run_tour($id, TRUE);
        return;
      }
    }
  }

  // Otherwise, only run the tour if it's set to auto-run and we're on the path of one of the steps.
  foreach ($tours as $id => $tour) {
    if ($tour->autorun) {
      $path = $tour->start_path;
      if ($path == current_path() || $path == request_path() || $path == '<front>' && request_path() == '') {
        bootstrap_tour_run_tour($id);
      }
    }
  }
}

/**
 * Helper function to end the current tour.
 */
function bootstrap_tour_end_current_tour() {
  if (isset($_SESSION['tour'])) {
    unset($_SESSION['tour']);
  }
}

/**
 * Helper function to get all bootstrap tour names and ids in an array keyed by id.
 */
function bootstrap_tour_load_config($include_disabled = FALSE) {
  if (!$include_disabled) {
    $tours = db_query('SELECT bootstrap_tour_id, name, autorun, start_path FROM {bootstrap_tour_tour} WHERE enabled = 1')
      ->fetchAllAssoc('bootstrap_tour_id');
  }
  else {
    $tours = db_query('SELECT bootstrap_tour_id, name, autorun, start_path FROM {bootstrap_tour_tour}')
      ->fetchAllAssoc('bootstrap_tour_id');
  }
  return $tours;
}

/**
 * Callback function to{bo load a complete bootstrap tour, including all steps, by id.
 */
function bootstrap_tour_load($id) {
  $tour = entity_load('bootstrap_tour', array(
    $id,
  ), array(), FALSE);
  $tour = reset($tour);
  $wrapper = entity_metadata_wrapper('bootstrap_tour', $tour);
  $tour->steps = $wrapper->bootstrap_tour_step_reference
    ->value();
  return $tour;
}

/**
 * Load all the tours (and caches them).
 */
function bootstrap_tour_load_all($reset = FALSE) {
  $all_tours =& drupal_static(__FUNCTION__);
  if (!isset($all_tours) || $reset) {
    if (!$reset && ($cache = cache_get('bootstrap_tours_all'))) {
      $all_tours = $cache->data;
    }
    else {
      $all_tours = entity_load('bootstrap_tour');
      foreach ($all_tours as $id => $tour) {
        $wrapper = entity_metadata_wrapper('bootstrap_tour', $tour);
        $all_tours[$id]->steps = $wrapper->bootstrap_tour_step_reference
          ->value();
      }
      cache_set('bootstrap_tours_all', $all_tours);
    }
  }
  return $all_tours;
}

/**
 * Implements hook_bootstrap_tour_insert().
 */
function bootstrap_tour_bootstrap_tour_insert($tour) {
  bootstrap_tour_bootstrap_tour_update($tour);
}

/**
 * Implements hook_bootstrap_tour_insert().
 */
function bootstrap_tour_bootstrap_tour_update($tour) {
  cache_clear_all('bootstrap_tours_all', 'cache');
  drupal_static_reset('bootstrap_tour_load_all');
}

/**
 * Checks access for user for tour.
 *
 * @param
 *  Loaded tour event.
 * @param $account
 *  An account object, default to current user.
 *
 * @return
 *  True if user has access, false if they don't.
 */
function bootstrap_tour_access($tour, $account = NULL) {
  if (!$account) {
    global $user;
    $account = $user;
  }
  if (empty($tour) || empty($tour->enabled)) {
    return FALSE;
  }
  $access = TRUE;
  if ($account->uid != 1 && !empty($tour->roles)) {

    // Compare the tour's roles to the user's roles and if there aren't any overlaps and
    // the user isn't user 1, cancel running the tour.
    $tour_roles = explode(',', $tour->roles);
    $account_roles = array_keys($account->roles);
    $compare = array_intersect($tour_roles, $account_roles);
    if (empty($compare)) {
      $access = FALSE;
    }
  }
  $access_array = module_invoke_all('bootstrap_tour_access', $tour, $account, $access);
  return !in_array(FALSE, $access_array, TRUE) && $access;
}

Functions

Namesort descending Description
bootstrap_tour_access Checks access for user for tour.
bootstrap_tour_admin_access Bootstrap Tour access callback.
bootstrap_tour_bootstrap_tour_insert Implements hook_bootstrap_tour_insert().
bootstrap_tour_bootstrap_tour_update Implements hook_bootstrap_tour_insert().
bootstrap_tour_cleanup Remove tour cookies from session
bootstrap_tour_end_current_tour Helper function to end the current tour.
bootstrap_tour_entity_info Implements hook_entity_info().
bootstrap_tour_libraries_info Implementation of hook_libraries_info(). Tell Drupal about the Bootstrap Tour library.
bootstrap_tour_load Callback function to{bo load a complete bootstrap tour, including all steps, by id.
bootstrap_tour_load_all Load all the tours (and caches them).
bootstrap_tour_load_config Helper function to get all bootstrap tour names and ids in an array keyed by id.
bootstrap_tour_load_file Helper function to load a file from local disk or from remote url
bootstrap_tour_menu Implements hook_menu().
bootstrap_tour_page_build Implementation of hook_init(). Load all the tours and figure out if any are set to auto-run and start on the current page. If so, go ahead and run that one.
bootstrap_tour_permission Implements hook_permission().
bootstrap_tour_run_tour Helper function to actually run a tour. Can be called from anywhere.
bootstrap_tour_views_api Implements hook_views_api().