array( 'page callback' => 'guideme_ajax_mark_done', 'page arguments' => array(2), 'access callback' => 'guideme_access', 'access arguments' => array(2), 'type' => MENU_CALLBACK, ), ); } /** * Implements hook_hook_info(). */ function guideme_hook_info() { return array( 'guideme_path' => array('group' => 'guideme'), 'guideme_path_alter' => array('group' => 'guideme'), ); } /** * Implements hook_permission(). */ function guideme_permission() { $guide_paths = guideme_get_guide_paths(); $permission = array(); if (!empty($guide_paths)) { foreach ($guide_paths as $key => $value) { $permission["use $key guided path"] = array( 'title' => t("Use the %path guided path", array('%path' => $key)), 'description' => t("Allow a user to follow the %path guided step by step instructional path.", array('%path' => $key)), ); } } return $permission; } /** * Implements hook_init(). */ function guideme_init() { global $user; $map = guideme_get_map(); $url = drupal_is_front_page() ? '' : current_path(); list($guide_path_id, $step_url_key) = guidemap_fetch_appropriate_guide_path($map, $url); // Check with the path alias. if (empty($guide_path_id) && $url !== '') { $url = request_path(); list($guide_path_id, $step_url_key) = guidemap_fetch_appropriate_guide_path($map, $url); } if (!empty($guide_path_id)) { if (user_access("use $guide_path_id guided path") && !guideme_has_user_completed_guide_path($user->uid, $guide_path_id)) { guideme_initialize_guide_path($guide_path_id, $step_url_key); } } } /** * Access callback: check if user has access to guide path. * * @param string $guide_path_id * * @return bool */ function guideme_access($guide_path_id) { return user_access("use $guide_path_id guided path"); } /** * Page callback: AJAX, mark guide path as done. * * @param string $guide_path_id */ function guideme_ajax_mark_done($guide_path_id) { global $user; if ($_POST['token'] === $_SESSION['guideme']['token']) { guideme_user_completed_guide_path($user->uid, $guide_path_id); return ''; } else { drupal_access_denied(); } } /** * Helper function to get the registered guide paths. * Uses both static and persistent caching. * * @return array */ function guideme_get_guide_paths() { $guide_paths = &drupal_static(__FUNCTION__); if (!isset($guide_paths)) { $cache = cache_get('guideme:guide_paths'); if ($cache) { $guide_paths = $cache->data; } else { $guide_paths = module_invoke_all('guideme_path'); drupal_alter('guideme_path', $guide_paths); $guide_paths = guideme_add_defaults($guide_paths); cache_set('guideme:guide_paths', $guide_paths, 'cache', CACHE_TEMPORARY); } } return $guide_paths; } /** * Helper function to get a single guide path. * * @param string $guide_path_id * * @return array|null */ function guideme_get_guide_path($guide_path_id) { $guide_paths = guideme_get_guide_paths(); return !empty($guide_paths[$guide_path_id]) ? $guide_paths[$guide_path_id] : NULL; } /** * Helper function to get the registered paths map. * Uses both static and persistent caching. * * @return array */ function guideme_get_map() { $map = &drupal_static(__FUNCTION__); if (!isset($map)) { $cache = cache_get('guideme:map'); if ($cache) { $map = $cache->data; } else { $guide_paths = guideme_get_guide_paths(); $map = guideme_map_guide_paths($guide_paths); cache_set('guideme:map', $map, 'cache', CACHE_TEMPORARY); } } return $map; } /** * Extract URI mapping from the registered paths for quick retreival and * to easily initiate a guide path. * * @param array $guide_paths * * @return array */ function guideme_map_guide_paths($guide_paths) { $map = array(); // First, sort by weight. uasort($guide_paths, 'drupal_sort_weight'); // Extract the URIs. foreach ($guide_paths as $key => $info) { $map[$key] = array_keys($info['steps']); } return $map; } /** * Set default values on guide paths that left out optional parameters, * like weight. * * @param array $guide_paths * * @return array */ function guideme_add_defaults($guide_paths) { foreach ($guide_paths as $key => $info) { if (!isset($info['weight'])) { $guide_paths[$key]['weight'] = 0; } } return $guide_paths; } /** * Check if the current path is registered for a guided path. * * @param array $map * @param string $current_path * * @return string|null */ function guidemap_fetch_appropriate_guide_path($map, $current_path) { foreach ($map as $key => $paths) { if (in_array($current_path, $paths)) { return array($key, $current_path); } else { foreach ($paths as $path) { if (strpos($path, '/*')) { $regex = str_replace(array('/', '*'), array('\\/', '[a-zA-Z0-9\-_]+'), $path); if (preg_match("/$regex/", $current_path)) { return array($key, $path); } } } } } } /** * Check if user has completed the path. * * @param int $uid * @param string $guide_path * * @return bool */ function guideme_has_user_completed_guide_path($uid, $guide_path) { $result = db_select('guideme_user_completion', 'gc') ->fields('gc', array('uid')) ->condition('gc.uid', $uid) ->condition('gc.guide_path', $guide_path) ->execute(); if (!empty($result)) { return !!$result->fetchField(); } else { return FALSE; } } /** * Set user has completed the path. * * @param int $uid * @param string $guide_path */ function guideme_user_completed_guide_path($uid, $guide_path) { db_merge('guideme_user_completion') ->key(array('uid' => $uid, 'guide_path' => $guide_path)) ->fields(array('uid' => $uid, 'guide_path' => $guide_path)) ->execute(); } /** * Initialize the guide path. * * @param string $guide_path_id * @param string $url */ function guideme_initialize_guide_path($guide_path_id, $url) { $guide_path = guideme_get_guide_path($guide_path_id); $page_steps = $guide_path['steps'][$url]; $url_clean = str_replace(array('*'), '_', $url); $token = uniqid(TRUE); $_SESSION['guideme']['token'] = $token; guideme_add_assets(); drupal_add_js(array('guideMe' => array( 'id' => $guide_path_id, 'cookie_id' => "guideMe:$guide_path_id:$url_clean", 'steps' => $page_steps, 'token' => $token, )), 'setting'); } /** * Helper function to add all Guideme assets. */ function guideme_add_assets() { static $added = FALSE; if (!$added) { $path = drupal_get_path('module', 'guideme'); drupal_add_js("$path/js/vendor/joyride/jquery.joyride-2.1.js"); drupal_add_js("$path/js/guideme.js"); drupal_add_css("$path/css/vendor/joyride/joyride-2.1.css"); drupal_add_css("$path/css/guideme.css"); $added = TRUE; } }