diff options
Diffstat (limited to 'sites/all/modules/advanced_help/advanced_help.module')
-rwxr-xr-x | sites/all/modules/advanced_help/advanced_help.module | 1173 |
1 files changed, 1173 insertions, 0 deletions
diff --git a/sites/all/modules/advanced_help/advanced_help.module b/sites/all/modules/advanced_help/advanced_help.module new file mode 100755 index 000000000..109bf5007 --- /dev/null +++ b/sites/all/modules/advanced_help/advanced_help.module @@ -0,0 +1,1173 @@ +<?php +/** + * @file + * Pluggable system to provide advanced help facilities for Drupal and modules. + * + * Modules utilizing this help system should create a 'help' directory in their + * module. Inside that directory place MODULENAME.help.ini which will be + * formatted like this: + * + * @code + * [buses] + * title = "How buses are tied into the system" + * file = buses + * + * [TOPIC_ID] + * title = "Title of topic" + * file = filename of topic, without the .html extension + * weight = the importance of the topic on the index page + * parent = the optional topic parent to use in the breadcrumb. + * Can be either topic or module%topic + * @endcode + * + * All topics are addressed by the module that provides the topic, and the topic + * id. Links may be embedded as in the following example: + * + * @code + * $output .= theme('advanced_help_topic', array('module' => $module, 'topic' => $topic)); + * @endcode + * + * Link to other topics using <a href="topic:module/topic">. (Using + * this format ensures the popup status remains consistent for all + * links.) + */ + +/** + * Implements hook_menu(). + * + * Strings in hook_help() should not be run through t(). + */ +function advanced_help_menu() { + $help_exists = module_exists('help') ? TRUE : FALSE; + if ($help_exists) { + // Add tabs to core Help page to access Advanced Help. + $items['admin/help/tab1'] = array( + 'title' => 'Help', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => 0, + ); + + $items['admin/help/ah'] = array( + 'title' => 'Advanced Help', + 'page callback' => 'advanced_help_index_page', + 'access arguments' => array('view advanced help index'), + 'type' => MENU_LOCAL_TASK, + 'weight' => 2, + ); + } + else { + // Make Advanced Help the normal help. + $items['admin/help/ah'] = array( + 'title' => 'Help', + 'page callback' => 'advanced_help_index_page', + 'access arguments' => array('view advanced help index'), + 'type' => MENU_NORMAL_ITEM, + 'weight' => 9, + ); + } + $items['help/ah/search/%'] = array( + 'title' => 'Search help', + 'page callback' => 'advanced_help_search_view', + 'page arguments' => array('advanced_help'), + 'access arguments' => array('view advanced help index'), + ); + + // View help topic. + $items['help/%/%'] = array( + 'page callback' => 'advanced_help_topic_page', + 'page arguments' => array(1, 2), + 'access arguments' => array('view advanced help topic'), + 'type' => MENU_CALLBACK, + ); + + return $items; +} + +/** + * Implements hook_menu_alter(). + */ +function advanced_help_menu_alter(&$callbacks) { + // We need to fix the menu item provided by search module to restrict access. + $callbacks['search/advanced_help/%menu_tail']['access callback'] = 'user_access'; + $callbacks['search/advanced_help/%menu_tail']['access arguments'] = array('view advanced help index'); +} + +/** + * Implements hook_theme(). + */ +function advanced_help_theme() { + $hooks['advanced_help_topic'] = array( + 'variables' => array( + 'module' => NULL, + 'topic' => NULL, + 'type' => 'icon', + ), + ); + + $hooks['advanced_help_popup'] = array( + 'render element' => 'content', + 'template' => 'advanced-help-popup', + ); + + return $hooks; +} + +/** + * Helper function to sort topics. + */ +function advanced_help_uasort($id_a, $id_b) { + $topics = advanced_help_get_topics(); + list($module_a, $topic_a) = $id_a; + $a = $topics[$module_a][$topic_a]; + list($module_b, $topic_b) = $id_b; + $b = $topics[$module_b][$topic_b]; + + $a_weight = isset($a['weight']) ? $a['weight'] : 0; + $b_weight = isset($b['weight']) ? $b['weight'] : 0; + if ($a_weight != $b_weight) { + return ($a_weight < $b_weight) ? -1 : 1; + } + + if ($a['title'] != $b['title']) { + return ($a['title'] < $b['title']) ? -1 : 1; + } + return 0; +} + +/** + * Helper function for grabbing search keys. Function is missing in D7. + * + * http://api.drupal.org/api/function/search_get_keys/6 + */ +function advanced_help_search_get_keys() { + static $return; + if (!isset($return)) { + // Extract keys as remainder of path + // Note: support old GET format of searches for existing links. + $path = explode('/', $_GET['q'], 4); + $keys = empty($_REQUEST['keys']) ? '' : $_REQUEST['keys']; + $return = count($path) == 4 ? $path[3] : $keys; + } + return $return; +} + +/** + * Page callback for advanced help search. + */ +function advanced_help_search_view() { + if (!module_exists('search')) { + return drupal_not_found(); + } + + $breadcrumb[] = advanced_help_l(t('Advanced help'), 'admin/help/ah'); + + if (!isset($_POST['form_id'])) { + $keys = advanced_help_search_get_keys(); + // Only perform search if there is non-whitespace search term: + $results = ''; + if (trim($keys)) { + $search_results = search_data($keys, 'advanced_help'); + $search_results = drupal_render($search_results); + // Collect the search results: + $results = array( + '#type' => 'markup', + '#markup' => $search_results, + ); + } + + // Construct the search form. + $output['advanced_help_search_form'] = drupal_get_form('advanced_help_search_form', $keys); + $output['results'] = $results; + + } + else { + $output = drupal_get_form('advanced_help_search_form', empty($keys) ? '' : $keys); + } + + $popup = !empty($_GET['popup']) && user_access('view advanced help popup'); + if ($popup) { + // Prevent devel module from spewing. + $GLOBALS['devel_shutdown'] = FALSE; + // Suppress admin_menu. + module_invoke('admin_menu', 'suppress'); + drupal_set_breadcrumb(array_reverse($breadcrumb)); + print theme('advanced_help_popup', array('content' => $output)); + return; + } + + $breadcrumb = array_merge(drupal_get_breadcrumb(), array_reverse($breadcrumb)); + drupal_set_breadcrumb($breadcrumb); + return $output; +} + +/** + * Page callback to view the advanced help topic index. + * + * @param string $module + * Name of the module. + * + * @return array + * Returns module index. + */ +function advanced_help_index_page($module = '') { + $topics = advanced_help_get_topics(); + $settings = advanced_help_get_settings(); + + $output = array(); + // Print a search widget. + $output['advanced_help_search'] = module_exists('search') ? drupal_get_form('advanced_help_search_form') : array('#markup' => t('Enable the search module to search help.')); + + $breadcrumb = array(); + if ($module) { + if (empty($topics[$module])) { + return drupal_not_found(); + } + + advanced_help_get_topic_hierarchy($topics); + $items = advanced_help_get_tree($topics, $topics[$module]['']['children']); + + $breadcrumb[] = advanced_help_l(t('Advanced help'), 'admin/help/ah'); + + drupal_set_title(t('@module help index', array('@module' => advanced_help_get_module_name($module)))); + $output['items-module'] = array( + '#theme' => 'item_list', + '#items' => $items, + ); + } + else { + // Print a module index. + $modules = array(); + $result = db_query('SELECT * FROM {system}'); + foreach ($result as $info) { + $module_info = unserialize($info->info); + $modules[$info->name] = isset($module_info['name']) ? $module_info['name'] : $info->name; + } + + asort($modules); + + $items = array(); + foreach ($modules as $module => $module_name) { + if (!empty($topics[$module]) && empty($settings[$module]['hide'])) { + if (isset($settings[$module]['index name'])) { + $name = $settings[$module]['index name']; + } + elseif (isset($settings[$module]['name'])) { + $name = $settings[$module]['name']; + } + else { + $name = t($module_name); + } + $items[] = advanced_help_l($name, "admin/help/ah/$module"); + } + } + + drupal_set_title(t('Advanced help')); + $output['items-nomodule'] = array( + '#theme' => 'item_list', + '#items' => $items, + ); + } + + $popup = !empty($_GET['popup']) && user_access('view advanced help popup'); + if ($popup) { + // Prevent devel module from spewing. + $GLOBALS['devel_shutdown'] = FALSE; + // Suppress admin_menu. + module_invoke('admin_menu', 'suppress'); + drupal_set_breadcrumb(array_reverse($breadcrumb)); + print theme('advanced_help_popup', array('content' => $output)); + return; + } + + $breadcrumb = array_merge(drupal_get_breadcrumb(), array_reverse($breadcrumb)); + drupal_set_breadcrumb($breadcrumb); + return $output; +} + +/** + * Build a tree of advanced help topics. + * + * @param array $topics + * Topics. + * @param array $topic_ids + * Topic Ids. + * @param int $max_depth + * Maximum depth for subtopics. + * @param int $depth + * Default depth for subtopics. + * + * @return array + * Returns list of topics/subtopics. + */ +function advanced_help_get_tree($topics, $topic_ids, $max_depth = -1, $depth = 0) { + uasort($topic_ids, 'advanced_help_uasort'); + $items = array(); + foreach ($topic_ids as $info) { + list($module, $topic) = $info; + $item = advanced_help_l($topics[$module][$topic]['title'], "help/$module/$topic"); + if (!empty($topics[$module][$topic]['children']) && ($max_depth == -1 || $depth < $max_depth)) { + $item .= theme('item_list', array( + 'items' => advanced_help_get_tree($topics, $topics[$module][$topic]['children'], $max_depth, $depth + 1), + )); + } + + $items[] = $item; + } + + return $items; +} + +/** + * Build a hierarchy for a single module's topics. + */ +function advanced_help_get_topic_hierarchy(&$topics) { + foreach ($topics as $module => $module_topics) { + foreach ($module_topics as $topic => $info) { + $parent_module = $module; + // We have a blank topic that we don't want parented to itself. + if (!$topic) { + continue; + } + + if (empty($info['parent'])) { + $parent = ''; + } + elseif (strpos($info['parent'], '%')) { + list($parent_module, $parent) = explode('%', $info['parent']); + if (empty($topics[$parent_module][$parent])) { + // If it doesn't exist, top level. + $parent = ''; + } + } + else { + $parent = $info['parent']; + if (empty($module_topics[$parent])) { + // If it doesn't exist, top level. + $parent = ''; + } + } + + if (!isset($topics[$parent_module][$parent]['children'])) { + $topics[$parent_module][$parent]['children'] = array(); + } + $topics[$parent_module][$parent]['children'][] = array($module, $topic); + $topics[$module][$topic]['_parent'] = array($parent_module, $parent); + } + } +} + +/** + * Implements hook_form_system_modules_alter(). + * + * Add advanced help links to the modules page. + */ +function advanced_help_form_system_modules_alter(&$form, &$form_state) { + if (!isset($form['modules'])) { + return; + } + $advanced_help_modules = drupal_map_assoc(array_keys(advanced_help_get_topics())); + foreach (element_children($form['modules']) as $group) { + foreach (element_children($form['modules'][$group]) as $module) { + if (isset($advanced_help_modules[$module])) { + $form['modules'][$group][$module]['links']['help'] = array( + '#type' => 'link', + '#title' => t('Help'), + '#href' => "admin/help/ah/$module", + '#options' => array( + 'attributes' => array( + 'class' => array('module-link', 'module-link-help'), + 'title' => t('Help'), + ), + ), + ); + } + } + } +} + +/** + * Form builder callback to build the search form. + * + * Load search/search.pages so that its template preprocess functions are + * visible and can be invoked. + */ +function advanced_help_search_form($form, &$form_state, $keys = '') { + module_load_include('inc', 'search', 'search.pages'); + $form = search_form($form, $form_state, 'admin/help/ah', $keys, 'advanced_help', t('Search help')); + + $form['basic']['inline']['submit']['#validate'] = array('search_form_validate'); + $form['basic']['inline']['submit']['#submit'] = array('advanced_help_search_form_submit'); + + return $form; +} + +/** + * Process a search form submission. + */ +function advanced_help_search_form_submit($form, &$form_state) { + $keys = empty($form_state['values']['processed_keys']) ? $form_state['values']['keys'] : $form_state['values']['processed_keys']; + if ($keys == '') { + form_set_error('keys', t('Please enter some keywords.')); + return; + } + + $popup = !empty($_GET['popup']) && user_access('view advanced help popup'); + + if ($popup) { + $form_state['redirect'] = array('help/ah/search/' . $keys, array('query' => array('popup' => 'true'))); + } + else { + $form_state['redirect'] = 'help/ah/search/' . $keys; + } +} + +/** + * Small helper function to get a module's proper name. + * + * @param string $module + * Name of the module. + * + * @return string + * Returns module's descriptive name. + */ +function advanced_help_get_module_name($module) { + $settings = advanced_help_get_settings(); + if (isset($settings[$module]['name'])) { + $name = $settings[$module]['name']; + } + else { + $info = db_query("SELECT s.info FROM {system} s WHERE s.name = :name", + array(':name' => $module)) + ->fetchField(); + $info = unserialize($info); + $name = t($info['name']); + } + return $name; +} + +/** + * Page callback to view a help topic. + */ +function advanced_help_topic_page($module, $topic) { + $info = advanced_help_get_topic($module, $topic); + if (!$info) { + return drupal_not_found(); + } + + $popup = !empty($_GET['popup']) && user_access('view advanced help popup'); + + drupal_set_title($info['title']); + + // Set up breadcrumb. + $breadcrumb = array(); + + $parent = $info; + $pmodule = $module; + + // Loop checker. + $checked = array(); + while (!empty($parent['parent'])) { + if (strpos($parent['parent'], '%')) { + list($pmodule, $ptopic) = explode('%', $parent['parent']); + } + else { + $ptopic = $parent['parent']; + } + + if (!empty($checked[$pmodule][$ptopic])) { + break; + } + $checked[$pmodule][$ptopic] = TRUE; + + $parent = advanced_help_get_topic($pmodule, $ptopic); + if (!$parent) { + break; + } + + $breadcrumb[] = advanced_help_l($parent['title'], "help/$pmodule/$ptopic"); + } + + $breadcrumb[] = advanced_help_l(advanced_help_get_module_name($pmodule), "admin/help/ah/$pmodule"); + $breadcrumb[] = advanced_help_l(t('Help'), "admin/help/ah"); + + $output = advanced_help_view_topic($module, $topic, $popup); + if (empty($output)) { + $output = t('Missing help topic.'); + } + + if ($popup) { + // Prevent devel module from spewing. + $GLOBALS['devel_shutdown'] = FALSE; + // Suppress admin_menu. + module_invoke('admin_menu', 'suppress'); + drupal_set_breadcrumb(array_reverse($breadcrumb)); + print theme('advanced_help_popup', array('content' => $output)); + return; + } + + drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help.css'); + $breadcrumb[] = l(t('Home'), ''); + drupal_set_breadcrumb(array_reverse($breadcrumb)); + return $output; +} + +/** + * Implements hook_permission(). + */ +function advanced_help_permission() { + return array( + 'view advanced help topic' => array('title' => t('View help topics')), + 'view advanced help popup' => array('title' => t('View help popups')), + 'view advanced help index' => array('title' => t('View help index')), + ); +} + +/** + * Display a help icon with a link to view the topic in a popup. + * + * @param array $variables + * An associative array containing: + * - module: The module that owns this help topic. + * - topic: The identifier for the topic + * - type + * - 'icon' to display the question mark icon + * - 'title' to display the topic's title + * - any other text to display the text. Be sure to t() it! + */ +function theme_advanced_help_topic($variables) { + $module = $variables['module']; + $topic = $variables['topic']; + $type = $variables['type']; + + $info = advanced_help_get_topic($module, $topic); + if (!$info) { + return; + } + + switch ($type) { + case 'icon': + $text = '<span>' . t('Help') . '</span>'; + $class = 'advanced-help-link'; + break; + + case 'title': + $text = $info['title']; + $class = 'advanced-help-title'; + break; + + default: + $class = 'advanced-help-title'; + $text = $type; + break; + } + + if (user_access('view advanced help popup')) { + drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help-icon.css'); + return l($text, "help/$module/$topic", array( + 'attributes' => array( + 'class' => $class, + 'onclick' => "var w=window.open(this.href, 'advanced_help_window', 'width=" . $info['popup width'] . ",height=" . $info['popup height'] . ",scrollbars,resizable'); w.focus(); return false;", + 'title' => $info['title'], + ), + 'query' => array('popup' => TRUE), + 'html' => TRUE) + ); + } + elseif (user_access('view advanced help topic')) { + return l($text, "help/$module/$topic", array( + 'attributes' => array( + 'class' => $class, + 'title' => $info['title'], + ), + 'html' => TRUE) + ); + } +} + +/** + * Load and render a help topic. + */ +function advanced_help_get_topic_filename($module, $topic) { + $info = advanced_help_get_topic_file_info($module, $topic); + if ($info) { + return "./$info[path]/$info[file]"; + } +} +/** + * Load and render a help topic. + */ +function advanced_help_get_topic_file_info($module, $topic) { + global $language; + + $info = advanced_help_get_topic($module, $topic); + if (empty($info)) { + return; + } + + // Search paths: + $paths = array( + // Allow theme override. + path_to_theme() . '/help', + // Translations. + drupal_get_path('module', $module) . "/translations/help/$language->language", + // In same directory as .inc file. + $info['path'], + ); + + foreach ($paths as $path) { + if (file_exists("./$path/$info[file]")) { + return array('path' => $path, 'file' => $info['file']); + } + } +} + +/** + * Load and render a help topic. + * + * @param string $module + * Name of the module. + * @param string $topic + * Name of the topic. + * @param bool $popup + * Whether to show in popup or not. + * + * @return string + * Returns formatted topic. + */ +function advanced_help_view_topic($module, $topic, $popup = FALSE) { + $file_info = advanced_help_get_topic_file_info($module, $topic); + if ($file_info) { + $info = advanced_help_get_topic($module, $topic); + $file = "./$file_info[path]/$file_info[file]"; + + $output = file_get_contents($file); + if (isset($info['readme file']) && $info['readme file']) { + $ext = pathinfo($file, PATHINFO_EXTENSION); + if ('md' == $ext && module_exists('markdown')) { + $filters = module_invoke('markdown', 'filter_info'); + $md_info = $filters['filter_markdown']; + + if (function_exists($md_info['process callback'])) { + $function = $md_info['process callback']; + $output = '<div class="advanced-help-topic">' . filter_xss_admin($function($output, NULL)) . '</div>'; + } + else { + $output = '<div class="advanced-help-topic"><pre class="readme">' . check_plain($output) . '</pre></div>'; + } + } + else { + $readme = ''; + if ('md' == $ext) { + $readme .= + '<p>' . + t('If you install the !module module, the text below will be filtered by the module, producing rich text.', + array('!module' => l(t('Markdown filter'), + 'https://www.drupal.org/project/markdown', + array('attributes' => array('title' => t('Link to project.')))))) . '</p>'; + } + $readme .= + '<div class="advanced-help-topic"><pre class="readme">' . check_plain($output) . '</pre></div>'; + $output = $readme; + } + return $output; + } + + // Make some exchanges. The strtr is because url() translates $ into %24 + // but we need to change it back for the regex replacement. + // + // Change 'topic:' to the URL for another help topic. + if ($popup) { + $output = preg_replace('/href="topic:([^"]+)"/', 'href="' . strtr(url('help/$1', array('query' => array('popup' => 'true'))), array('%24' => '$')) . '"', $output); + $output = preg_replace('/src="topic:([^"]+)"/', 'src="' . strtr(url('help/$1', array('query' => array('popup' => 'true'))), array('%24' => '$')) . '"', $output); + $output = preg_replace('/&topic:([^"]+)&/', strtr(url('help/$1', array('query' => array('popup' => 'true'))), array('%24' => '$')), $output); + } + else { + $output = preg_replace('/href="topic:([^"]+)"/', 'href="' . strtr(url('help/$1'), array('%24' => '$')) . '"', $output); + $output = preg_replace('/src="topic:([^"]+)"/', 'src="' . strtr(url('help/$1'), array('%24' => '$')) . '"', $output); + $output = preg_replace('/&topic:([^"]+)&/', strtr(url('help/$1'), array('%24' => '$')), $output); + } + + global $base_path; + + // Change 'path:' to the URL to the base help directory. + $output = preg_replace('/href="path:([^"]+)"/', 'href="' . $base_path . $info['path'] . '/$1"', $output); + $output = preg_replace('/src="path:([^"]+)"/', 'src="' . $base_path . $info['path'] . '/$1"', $output); + $output = str_replace('&path&', $base_path . $info['path'] . '/', $output); + + // Change 'trans_path:' to the URL to the actual help directory. + $output = preg_replace('/href="trans_path:([^"]+)"/', 'href="' . $base_path . $file_info['path'] . '/$1"', $output); + $output = preg_replace('/src="trans_path:([^"]+)"/', 'src="' . $base_path . $file_info['path'] . '/$1"', $output); + $output = str_replace('&trans_path&', $base_path . $file_info['path'] . '/', $output); + + // Change 'base_url:' to the URL to the site. + $output = preg_replace('/href="base_url:([^"]+)"/', 'href="' . strtr(url('$1'), array('%24' => '$')) . '"', $output); + $output = preg_replace('/src="base_url:([^"]+)"/', 'src="' . strtr(url('$1'), array('%24' => '$')) . '"', $output); + $output = preg_replace('/&base_url&([^"]+)"/', strtr(url('$1'), array('%24' => '$')) . '"', $output); + + // Run the line break filter if requested. + if (!empty($info['line break'])) { + // Remove the header since it adds an extra <br /> to the filter. + $output = preg_replace('/^<!--[^\n]*-->\n/', '', $output); + + $output = _filter_autop($output); + } + + if (!empty($info['navigation'])) { + $topics = advanced_help_get_topics(); + advanced_help_get_topic_hierarchy($topics); + if (!empty($topics[$module][$topic]['children'])) { + $items = advanced_help_get_tree($topics, $topics[$module][$topic]['children']); + $output .= theme('item_list', array('items' => $items)); + } + + list($parent_module, $parent_topic) = $topics[$module][$topic]['_parent']; + if ($parent_topic) { + $parent = $topics[$module][$topic]['_parent']; + $up = "help/$parent[0]/$parent[1]"; + } + else { + $up = "admin/help/ah/$module"; + } + + $siblings = $topics[$parent_module][$parent_topic]['children']; + uasort($siblings, 'advanced_help_uasort'); + $prev = $next = NULL; + $found = FALSE; + foreach ($siblings as $sibling) { + list($sibling_module, $sibling_topic) = $sibling; + if ($found) { + $next = $sibling; + break; + } + if ($sibling_module == $module && $sibling_topic == $topic) { + $found = TRUE; + continue; + } + $prev = $sibling; + } + + if ($prev || $up || $next) { + $navigation = '<div class="help-navigation clear-block">'; + + if ($prev) { + $navigation .= advanced_help_l('«« ' . $topics[$prev[0]][$prev[1]]['title'], "help/$prev[0]/$prev[1]", array('attributes' => array('class' => 'help-left'))); + } + if ($up) { + $navigation .= advanced_help_l(t('Up'), $up, array('attributes' => array('class' => $prev ? 'help-up' : 'help-up-noleft'))); + } + if ($next) { + $navigation .= advanced_help_l($topics[$next[0]][$next[1]]['title'] . ' »»', "help/$next[0]/$next[1]", array('attributes' => array('class' => 'help-right'))); + } + + $navigation .= '</div>'; + + $output .= $navigation; + } + } + + if (!empty($info['css'])) { + drupal_add_css($info['path'] . '/' . $info['css']); + } + + $output = '<div class="advanced-help-topic">' . $output . '</div>'; + drupal_alter('advanced_help_topic', $output, $popup); + + return $output; + } +} + +/** + * Get the information for a single help topic. + */ +function advanced_help_get_topic($module, $topic) { + $topics = advanced_help_get_topics(); + if (!empty($topics[$module][$topic])) { + return $topics[$module][$topic]; + } +} + +/** + * Search the system for all available help topics. + */ +function advanced_help_get_topics() { + $ini = _advanced_help_parse_ini(); + return $ini['topics']; +} + +/** + * Returns advanced help settings. + */ +function advanced_help_get_settings() { + $ini = _advanced_help_parse_ini(); + return $ini['settings']; +} + +/** + * Funtion to parse ini / txt files. + */ +function _advanced_help_parse_ini() { + global $language; + static $ini = NULL; + + if (!isset($ini)) { + $cache = cache_get('advanced_help_ini:' . $language->language); + if ($cache) { + $ini = $cache->data; + } + } + + if (!isset($ini)) { + $ini = array('topics' => array(), 'settings' => array()); + + $help_path = drupal_get_path('module', 'advanced_help') . '/modules'; + foreach (array_merge(module_list(), list_themes()) as $plugin) { + $module = is_string($plugin) ? $plugin : $plugin->name; + $module_path = drupal_get_path(is_string($plugin) ? 'module' : 'theme', $module); + $info = array(); + if (file_exists("$module_path/help/$module.help.ini")) { + $path = "$module_path/help"; + $info = parse_ini_file("./$module_path/help/$module.help.ini", TRUE); + } + elseif (file_exists("$help_path/$module/$module.help.ini")) { + $path = "$help_path/$module"; + $info = parse_ini_file("./$help_path/$module/$module.help.ini", TRUE); + } + elseif (!file_exists("$module_path/help")) { + // Look for one or more README files. + $files = file_scan_directory("./$module_path", + '/^(readme).*\.(txt|md)$/i', array('recurse' => FALSE)); + $path = "./$module_path"; + foreach ($files as $name => $fileinfo) { + $info[$fileinfo->filename] = array( + 'line break' => TRUE, + 'readme file' => TRUE, + 'file' => $fileinfo->filename, + 'title' => $fileinfo->name, + ); + } + } + + if (!empty($info)) { + // Get translated titles: + $translation = array(); + if (file_exists("$module_path/translations/help/$language->language/$module.help.ini")) { + $translation = parse_ini_file("$module_path/translations/help/$language->language/$module.help.ini", TRUE); + } + + $ini['settings'][$module] = array(); + if (!empty($info['advanced help settings'])) { + $ini['settings'][$module] = $info['advanced help settings']; + unset($info['advanced help settings']); + + // Check translated strings for translatable global settings. + if (isset($translation['advanced help settings']['name'])) { + $ini['settings']['name'] = $translation['advanced help settings']['name']; + } + if (isset($translation['advanced help settings']['index name'])) { + $ini['settings']['index name'] = $translation['advanced help settings']['index name']; + } + + } + + foreach ($info as $name => $topic) { + // Each topic should have a name, a title, a file and path. + $file = !empty($topic['file']) ? $topic['file'] : $name; + $ini['topics'][$module][$name] = array( + 'name' => $name, + 'module' => $module, + 'ini' => $topic, + 'title' => !empty($translation[$name]['title']) ? $translation[$name]['title'] : $topic['title'], + 'weight' => isset($topic['weight']) ? $topic['weight'] : 0, + 'parent' => isset($topic['parent']) ? $topic['parent'] : 0, + 'popup width' => isset($topic['popup width']) ? $topic['popup width'] : 500, + 'popup height' => isset($topic['popup height']) ? $topic['popup height'] : 500, + // Require extension. + 'file' => isset($topic['readme file']) ? $file : $file . '.html', + // Not in .ini file. + 'path' => $path, + 'line break' => isset($topic['line break']) ? $topic['line break'] : (isset($ini['settings'][$module]['line break']) ? $ini['settings'][$module]['line break'] : FALSE), + 'navigation' => isset($topic['navigation']) ? $topic['navigation'] : (isset($ini['settings'][$module]['navigation']) ? $ini['settings'][$module]['navigation'] : TRUE), + 'css' => isset($topic['css']) ? $topic['css'] : (isset($ini['settings'][$module]['css']) ? $ini['settings'][$module]['css'] : NULL), + 'readme file' => isset($topic['readme file']) ? $topic['readme file'] : FALSE, + ); + } + } + } + drupal_alter('advanced_help_topic_info', $ini); + + cache_set('advanced_help_ini:' . $language->language, $ini); + } + return $ini; +} + +/** + * Implements hook_search_info(). + * + * Returns title for the tab on search page & path component after 'search/'. + */ +function advanced_help_search_info() { + return array( + 'title' => t('Help'), + 'path' => 'advanced_help', + ); +} + +/** + * Implements hook_search_execute(). + */ +function advanced_help_search_execute($keys = NULL) { + $topics = advanced_help_get_topics(); + + $query = db_select('search_index', 'i', array('target' => 'slave')) + ->extend('SearchQuery') + ->extend('PagerDefault'); + $query->join('advanced_help_index', 'ahi', 'i.sid = ahi.sid'); + $query->searchExpression($keys, 'help'); + + // Only continue if the first pass query matches. + if (!$query->executeFirstPass()) { + return array(); + } + + $results = array(); + + $find = $query->execute(); + foreach ($find as $item) { + $sids[] = $item->sid; + } + + $query = db_select('advanced_help_index', 'ahi'); + $result = $query + ->fields('ahi') + ->condition('sid', $sids, 'IN') + ->execute(); + + foreach ($result as $sid) { + // Guard against removed help topics that are still indexed. + if (empty($topics[$sid->module][$sid->topic])) { + continue; + } + $info = $topics[$sid->module][$sid->topic]; + $text = advanced_help_view_topic($sid->module, $sid->topic); + $results[] = array( + 'link' => advanced_help_url("help/$sid->module/$sid->topic"), + 'title' => $info['title'], + 'snippet' => search_excerpt($keys, $text), + ); + } + return $results; +} + +/** + * Implements hook_search_reset(). + */ +function advanced_help_search_reset() { + variable_del('advanced_help_last_cron'); +} + +/** + * Implements hook_search_status(). + */ +function advanced_help_search_status() { + $topics = advanced_help_get_topics(); + $total = 0; + foreach ($topics as $module => $module_topics) { + foreach ($module_topics as $topic => $info) { + $file = advanced_help_get_topic_filename($module, $topic); + if ($file) { + $total++; + } + } + } + + $last_cron = variable_get('advanced_help_last_cron', array('time' => 0)); + $indexed = 0; + if ($last_cron['time'] != 0) { + $indexed = db_query("SELECT COUNT(*) FROM {search_dataset} sd WHERE sd.type = 'help' AND sd.sid IS NOT NULL AND sd.reindex = 0")->fetchField(); + } + return array('remaining' => $total - $indexed, 'total' => $total); +} + +/** + * Gets search id for each topic. + * + * Get or create an sid (search id) that correlates to each topic for + * the search system. + */ +function advanced_help_get_sids(&$topics) { + global $language; + $result = db_query("SELECT * FROM {advanced_help_index} WHERE language = :language", + array(':language' => $language->language)); + foreach ($result as $sid) { + if (empty($topics[$sid->module][$sid->topic])) { + db_query("DELETE FROM {advanced_help_index} WHERE sid = :sid", + array(':sid' => $sid->sid)); + } + else { + $topics[$sid->module][$sid->topic]['sid'] = $sid->sid; + } + } +} + +/** + * Implements hook_update_index(). + */ +function advanced_help_update_index() { + global $language; + + // If we got interrupted by limit, this will contain the last module + // and topic we looked at. + $last = variable_get('advanced_help_last_cron', array('time' => 0)); + $limit = intval(variable_get('search_cron_limit', 100)); + $topics = advanced_help_get_topics(); + advanced_help_get_sids($topics); + + $count = 0; + + foreach ($topics as $module => $module_topics) { + // Fast forward if necessary. + if (!empty($last['module']) && $last['module'] != $module) { + continue; + } + + foreach ($module_topics as $topic => $info) { + // Fast forward if necessary. + if (!empty($last['topic']) && $last['topic'] != $topic) { + continue; + } + + // If we've been looking to catch up, and we have, reset so we + // stop fast forwarding. + if (!empty($last['module'])) { + unset($last['topic']); + unset($last['module']); + } + + $file = advanced_help_get_topic_filename($module, $topic); + if ($file && (empty($info['sid']) || filemtime($file) > $last['time'])) { + if (empty($info['sid'])) { + $info['sid'] = db_insert('advanced_help_index') + ->fields(array( + 'module' => $module, + 'topic' => $topic, + 'language' => $language->language, + )) + ->execute(); + } + + search_index($info['sid'], 'help', '<h1>' . $info['title'] . '</h1>' . file_get_contents($file)); + $count++; + if ($count >= $limit) { + $last['topic'] = $topic; + $last['module'] = $module; + // Don't change time if we stop. + variable_set('advanced_help_last_cron', $last); + return; + } + } + } + } + + variable_set('advanced_help_last_cron', array('time' => time())); +} + +/** + * Fill in a bunch of page variables for our specialized popup page. + */ +function template_preprocess_advanced_help_popup(&$variables) { + // Add favicon. + if (theme_get_setting('toggle_favicon')) { + drupal_add_html_head('<link rel="shortcut icon" href="' . check_url(theme_get_setting('favicon')) . '" type="image/x-icon" />'); + } + + global $theme; + // Construct page title. + if (drupal_get_title()) { + $head_title = array( + strip_tags(drupal_get_title()), + variable_get('site_name', 'Drupal'), + ); + } + else { + $head_title = array(variable_get('site_name', 'Drupal')); + if (variable_get('site_slogan', '')) { + $head_title[] = variable_get('site_slogan', ''); + } + } + + drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help-popup.css'); + drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help.css'); + + $variables['head_title'] = implode(' | ', $head_title); + $variables['base_path'] = base_path(); + $variables['front_page'] = url(); + $variables['breadcrumb'] = theme('breadcrumb', array('breadcrumb' => drupal_get_breadcrumb())); + $variables['feed_icons'] = drupal_get_feeds(); + $variables['head'] = drupal_get_html_head(); + $variables['language'] = $GLOBALS['language']; + $variables['language']->dir = $GLOBALS['language']->direction ? 'rtl' : 'ltr'; + $variables['logo'] = theme_get_setting('logo'); + $variables['messages'] = theme('status_messages'); + $variables['site_name'] = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : ''); + $variables['css'] = drupal_add_css(); + $css = drupal_add_css(); + + // Remove theme css. + foreach ($css as $key => $value) { + if ($value['group'] == CSS_THEME) { + $exclude[$key] = FALSE; + } + } + $css = array_diff_key($css, $exclude); + + $variables['styles'] = drupal_get_css($css); + $variables['scripts'] = drupal_get_js(); + $variables['title'] = drupal_get_title(); + + // This function can be called either with a render array or + // an already rendered string. + if (is_array($variables['content'])) { + $variables['content'] = drupal_render($variables['content']); + } + // Closure should be filled last. + // There has never been a theme hook for closure (going back to + // first release 2008-04-17). Unable to figure out its purpose. + // $variables['closure'] = theme('closure'); +} + +/** + * Format a link but preserve popup identity. + */ +function advanced_help_l($text, $dest, $options = array()) { + $popup = !empty($_GET['popup']) && user_access('view advanced help popup'); + if ($popup) { + if (empty($options['query'])) { + $options['query'] = array(); + } + + if (is_array($options['query'])) { + $options['query'] += array('popup' => TRUE); + } + else { + $options['query'] += '&popup=TRUE'; + } + } + + return l($text, $dest, $options); +} + +/** + * Format a URL but preserve popup identity. + */ +function advanced_help_url($dest, $options = array()) { + $popup = !empty($_GET['popup']) && user_access('view advanced help popup'); + if ($popup) { + if (empty($options['query'])) { + $options['query'] = array(); + } + + $options['query'] += array('popup' => TRUE); + } + + return url($dest, $options); +} |