diff --git a/includes/common.inc b/includes/common.inc index e380270..456cff6 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -3525,6 +3525,21 @@ function element_children(&$elements, $sort = FALSE) { } /** + * Determine whether help topics for a given module exist. + * + * @param $module + * The module name. + * @return + * TRUE if the module is enabled and provides help topics, and the help module is enabled. + */ +function help_exists($module) { + if (drupal_function_exists('help_get_topics') && module_exists($module)) { + $topics = help_get_topics(); + return isset($topics[$module]); + } +} + +/** * Provide theme registration for themes across .inc files. */ function drupal_common_theme() { @@ -3596,9 +3611,9 @@ function drupal_common_theme() { 'list' => array( 'arguments' => array('elements' => NULL), ), - 'more_help_link' => array( - 'arguments' => array('url' => NULL), - ), + 'help_link' => array( + 'arguments' => array('module' => NULL, 'topic' => NULL, 'title' => NULL, 'attributes' => NULL), + ), 'xml_icon' => array( 'arguments' => array('url' => NULL), ), diff --git a/includes/menu.inc b/includes/menu.inc index 9d50428..62cd96e 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -1299,10 +1299,9 @@ function menu_get_active_help() { if ($help = $function($router_path, $arg)) { $output .= $help . "\n"; } - // Add "more help" link on admin pages if the module provides a - // standalone help page. - if ($arg[0] == "admin" && module_exists('help') && $function('admin/help#' . $arg[2], $empty_arg) && $help) { - $output .= theme("more_help_link", url('admin/help/' . $arg[2])); + // Add "more help" link on admin pages if the module provides help topics. + if ($arg[0] == "admin" && help_exists($arg[2]) && $help) { + $output .= theme("help_link", $arg[2]); } } return $output; diff --git a/includes/theme.inc b/includes/theme.inc index fd28cd1..0c43e3b 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -1590,13 +1590,6 @@ function theme_list($elements) { } /** - * Returns code that emits the 'more help'-link. - */ -function theme_more_help_link($url) { - return ''; -} - -/** * Return code that emits an XML icon. * * For most use cases, this function has been superseded by theme_feed_icon(). diff --git a/modules/blog/blog.test b/modules/blog/blog.test index e8dd6dd..9b986b1 100644 --- a/modules/blog/blog.test +++ b/modules/blog/blog.test @@ -91,15 +91,6 @@ class BlogTestCase extends DrupalWebTestCase { $response2 = ($admin) ? 200 : 403; - // View blog help node. - $this->drupalGet('admin/help/blog'); - $this->assertResponse($response2); - if ($response2 == 200) { - $this->assertTitle(t('Blog | Drupal'), t('Blog help node was displayed')); - $this->assertText(t('Blog'), t('Blog help node was displayed')); - $this->assertText(t('Home ' . $crumb . ' Administer ' . $crumb . ' Help'), t('Breadcrumbs were displayed')); - } - // Verify the blog block was displayed. $this->drupalGet(''); $this->assertResponse(200); diff --git a/modules/dblog/dblog.test b/modules/dblog/dblog.test index 1a15f30..4abd3a0 100644 --- a/modules/dblog/dblog.test +++ b/modules/dblog/dblog.test @@ -118,13 +118,6 @@ class DBLogTestCase extends DrupalWebTestCase { private function verifyReports($response = 200) { $quote = '''; - // View dblog help node. - $this->drupalGet('admin/help/dblog'); - $this->assertResponse($response); - if ($response == 200) { - $this->assertText(t('Database logging'), t('DBLog help was displayed')); - } - // View dblog report node. $this->drupalGet('admin/reports/dblog'); $this->assertResponse($response); diff --git a/modules/forum/forum.test b/modules/forum/forum.test index 2213a10..13ff613 100644 --- a/modules/forum/forum.test +++ b/modules/forum/forum.test @@ -279,15 +279,6 @@ class ForumTestCase extends DrupalWebTestCase { $response2 = ($admin) ? 200 : 403; - // View forum help node. - $this->drupalGet('admin/help/forum'); - $this->assertResponse($response2); - if ($response2 == 200) { - $this->assertTitle(t('Forum | Drupal'), t('Forum help node was displayed')); - $this->assertText(t('Forum'), t('Forum help node was displayed')); - $this->assertText(t('Home ' . $crumb . ' Administer ' . $crumb . ' Help'), t('Breadcrumbs were displayed')); - } - // Verify the forum blocks were displayed. $this->drupalGet(''); $this->assertResponse(200); diff --git a/modules/help/help-rtl.css b/modules/help/help-rtl.css index e7162b8..858a9f8 100644 --- a/modules/help/help-rtl.css +++ b/modules/help/help-rtl.css @@ -9,3 +9,23 @@ padding-right: 0; padding-left: 0; } + +.help-topic .toc-block { + float: left; +} + +.help-left { + float: right; +} + +.help-right { + float: left; +} + +.help-previous { + float: right; +} + +.help-next { + float: left; +} \ No newline at end of file diff --git a/modules/help/help.admin.inc b/modules/help/help.admin.inc index 2d631b2..485b9b7 100644 --- a/modules/help/help.admin.inc +++ b/modules/help/help.admin.inc @@ -3,74 +3,677 @@ /** * @file - * Admin page callbacks for the help module. + * Page callbacks for the help module. */ /** - * Menu callback; prints a page listing a glossary of Drupal terminology. + * Menu callback for the help overview page. + * + * @return + * A page displaying available help topics for modules. */ -function help_main() { - // Add CSS - drupal_add_css(drupal_get_path('module', 'help') . '/help.css', array('preprocess' => FALSE)); - $output = '

' . t('Help topics') . '

' . t('Help is available on the following items:') . '

' . help_links_as_list(); +function help_by_module() { + $items = array(); + $menu_items = array(); + + $topics = help_get_topics(); + $settings = help_get_settings(); + + help_get_topic_hierarchy($topics); + + $modules = module_rebuild_cache(); + foreach ($modules as $file) { + $module = $file->name; + if (empty($topics[$module]) || !empty($settings[$module]['hide'])) { + continue; + } + + // Fetch help links. + $items = help_get_tree($topics, $topics[$module]['']['children'], array(), 0); + + // Sort in ascending order of keys. + ksort($items); + + // Retrieve the name to use. + if (isset($settings[$module]['index name'])) { + $name = $settings[$module]['index name']; + } + elseif (isset($settings[$module]['name'])) { + $name = $settings[$module]['name']; + } + else { + $name = t($file->info['name']); + } + + $menu_items[$name] = array($file->info['description'], $items); + } + + drupal_add_css(drupal_get_path('module', 'help') . '/help.css'); + return theme('system_admin_by_module', $menu_items); +} + +/** + * Menu callback. Returns a page with help topics for a module. + * + * @param $module + * The module that owns this help topic page. + * @param $topic + * Optional identifier for the topic, or NULL to display an index of available topics. + * + * @return + * Themed output of a help topic page. + */ +function help_topic_page($module, $topic = NULL) { + drupal_add_css(drupal_get_path('module', 'help') . '/help.css'); + + $info = help_get_topic_info($module, $topic); + if (isset($topic) && !$info) { + // Return error 404 if the topic does not exist. + return drupal_not_found(); + } + + drupal_set_title($info['title']); + + // Set up breadcrumb. + $breadcrumb = array(); + + $parent = $info; + $parent_module = $module; + + $checked = array(); + // Crawl up parent tree looking for the breadcrumb trail. + while (!empty($parent['parent'])) { + if (strpos($parent['parent'], '%')) { + list($parent_module, $parent_topic) = explode('%', $parent['parent']); + } + else { + $parent_topic = $parent['parent']; + } + + // Mark the topic as checked to prevent processing again. + if (!empty($checked[$parent_module][$parent_topic])) { + break; + } + $checked[$parent_module][$parent_topic] = TRUE; + + $parent = help_get_topic_info($parent_module, $parent_topic); + if (!$parent) { + break; + } + + // Add the current topic to the breadcrumb. + $breadcrumb[] = help_l($parent['title'], "admin/help/$parent_module/$parent_topic"); + } + + $output = help_view_topic($module, $topic); + if (empty($output)) { + $output = help_view_module($module); + drupal_set_title(t('@module', array('@module' => help_get_module_name($module)))); + } + else { + $breadcrumb[] = help_l(help_get_module_name($parent_module), "admin/help/$parent_module"); + } + + $breadcrumb[] = help_l(t('Help'), "admin/help"); + $breadcrumb[] = help_l(t('Administer'), 'admin'); + $breadcrumb[] = l(t('Home'), ''); + + drupal_set_breadcrumb(array_reverse($breadcrumb)); + return $output; } /** - * Menu callback; prints a page listing general help for a module. + * Load and render a help topics listing. + * + * @param $module + * The module name of the topics to list. + * + * @return + * Themed output of links to help topics. */ -function help_page($name) { +function help_view_module($module) { $output = ''; - if (module_hook($name, 'help')) { - $module = drupal_parse_info_file(drupal_get_path('module', $name) . '/' . $name . '.info'); - drupal_set_title($module['name']); - $temp = module_invoke($name, 'help', "admin/help#$name", drupal_help_arg()); - if (empty($temp)) { - $output .= t("No help is available for module %module.", array('%module' => $module['name'])); + $items = array(); + $topics = help_get_topics(); + + help_get_topic_hierarchy($topics); + + if (!empty($topics[$module])) { + $items = help_get_tree($topics, $topics[$module]['']['children'], array(), 0); + $output = theme('item_list', $items, NULL, 'ul', array('class' => 'toc')); + } + else { + $output = t('No help topic on this subject is available.'); + } + + return $output; + } + +/** + * Load and render a help topic. + * + * @param $module + * The module that owns this help topic. + * @param $topic + * The topic to render. + * + * @return + * Themed output of the help topic. + */ +function help_view_topic($module, $topic) { + $file_info = help_get_topic_file_info($module, $topic); + if (!$file_info) { + return; + } + + $info = help_get_topic_info($module, $topic); + $file = './' . $file_info['path'] . '/' . $file_info['file']; + $content = file_get_contents($file); + + // 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. + $content = preg_replace('/\[topic:([^"]+)\]/', strtr(url('admin/help/$1'), array('%24' => '$')), $content); + + global $base_path; + + // Change '[url:X]' to the URL to the site. + $content = preg_replace('/\[url:([^"]+)\]/', strtr(url('$1'), array('%24' => '$')), $content); + + // Change '[path]' to the URL to the base help directory. + $content = str_replace('[path]', $base_path . $info['path'] . '/', $content); + + // Change '[trans_path]' to the URL to the actual help directory. + $content = str_replace('[trans_path]', $base_path . $file_info['path'] . '/', $content); + + // Change '[base_url]' to the URL to the site. + $content = str_replace('[base_url]', $base_path, $content); + + // Run the line break filter if requested. + if (!empty($info['line break'])) { + // Remove the header since it adds an extra
to the filter. + $content = preg_replace('/^\n/', '', $content); + // This calls the line break filter. + $content = filter_filter('process', 1, -1, $content); + } + + $navigation = $children = $links = array(); + + if (!empty($info['navigation'])) { + $topics = help_get_topics(); + help_get_topic_hierarchy($topics); + + if (!empty($topics[$module]['']['children'])) { + $tree = array($topic); + if (!empty($topics[$module][$topic]['parent'])) { + array_push($tree, $topics[$module][$topic]['parent']); + } + $items = help_get_tree($topics, $topics[$module]['']['children'], $tree); + if (count($items) > 1) { + // Pass on the table of contents block to display links to parent and sibling topics. + $navigation = $items; + } + } + + if (!empty($topics[$module][$topic]['children'])) { + // Pass on an ordered list to display links to immediate children. + $children = help_get_tree($topics, $topics[$module][$topic]['children']); + } + + // Determine the path for "Up" link. + list($parent_module, $parent_topic) = $topics[$module][$topic]['_parent']; + if ($parent_topic) { + // Link to a parent topic. + $parent = $topics[$module][$topic]['_parent']; + $up = "admin/help/$parent[0]/$parent[1]"; } else { - $output .= $temp; + $up = "admin/help/$module"; + } + + $siblings = $topics[$parent_module][$parent_topic]['children']; + // Sort topics according to weight. + uasort($siblings, '_help_uasort'); + $prev = $next = NULL; + $found = FALSE; + // Find the previous and next topic. + 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; } - // Only print list of administration pages if the module in question has - // any such pages associated to it. - $admin_tasks = system_get_module_admin_tasks($name); - if (!empty($admin_tasks)) { - ksort($admin_tasks); - $output .= theme('item_list', $admin_tasks, t('@module administration pages', array('@module' => $module['name']))); + // Bottom navigation links. + if ($prev || $up || $next) { + if ($prev) { + $links[] = array( + 'title' => '« ' . $topics[$prev[0]][$prev[1]]['title'], + 'href' => "admin/help/$prev[0]/$prev[1]", + 'attributes' => array('class' => 'help-left') + ); + } + if ($up) { + $links[] = array( + 'title' => t('Up'), + 'href' => $up, + 'attributes' => array('class' => $prev ? 'help-up' : 'help-up-noleft'), + ); + } + if ($next) { + $links[] = array( + 'title' => $topics[$next[0]][$next[1]]['title'] . ' »', + 'href' => "admin/help/$next[0]/$next[1]", + 'attributes' => array('class' => 'help-right'), + ); + } } + } + if (!empty($info['css'])) { + drupal_add_css($info['path'] . '/' . $info['css']); + } + + return theme('help_topic', $content, $navigation, $children, $links); +} + +/** + * Theme a help topic. + * + * @param $content + * The main help content. + * @param $navigation + * Array of entries for the floating navigation block. + * @param $children + * Array of links to the immediate children of the topic. + * @param $links + * Array of links for next, up, and previous topics. + */ +function theme_help_topic($content = '', $navigation = array(), $children = array(), $links = array()) { + $output = '
'; + // Render navigation block. + if (!empty($navigation)) { + $output .= theme('item_list', $navigation, NULL, 'ul', array('class' => 'toc-block')); + } + // Add main content. + $output .= $content; + // Render children links. + if (!empty($children)) { + $output .= theme('item_list', $children, NULL, 'ul', array('class' => 'toc')); } + // Render next, up, previous links. + if (!empty($links)) { + $output .= '
'; + foreach ($links as $link) { + $output .= l($link['title'], $link['href'], $link); + } + $output .= '
'; + } + + $output .= '
'; + return $output; } -function help_links_as_list() { - $empty_arg = drupal_help_arg(); - $module_info = module_rebuild_cache(); +/** + * Build a tree of help topics. + * + * @param $topics + * An array of topics. + * @param $topic_ids + * An array of child topic_ids. + * @param $tree_parents + * + * @param $max_depth + * The maximum depth of children. + * @param $depth + * The current depth. + * + * @return + * An array of topics. + */ +function help_get_tree($topics, $topic_ids, $tree_parents = array(), $max_depth = -1, $depth = 0) { + $items = array(); + if (!empty($topic_ids)) { + uasort($topic_ids, '_help_uasort'); + foreach ($topic_ids as $info) { + list($module, $topic) = $info; + $item = help_l($topics[$module][$topic]['title'], "admin/help/$module/$topic"); + if (!empty($tree_parents) && $topic == end($tree_parents)) { + $children = !empty($topics[$module][$topic]['children']) ? $topics[$module][$topic]['children'] : array(); + $item .= theme('item_list', help_get_tree($topics, $children, $tree_parents, $max_depth, $depth + 1)); + } + elseif (empty($tree_parents) && !empty($topics[$module][$topic]['children']) && ($max_depth == -1 || $depth < $max_depth)) { + $item .= theme('item_list', help_get_tree($topics, $topics[$module][$topic]['children'], $tree_parents, $max_depth, $depth + 1)); + } + + $items[] = $item; + } + } + + return $items; + } + +/** + * Build a hierarchy for a single module's topics. + * + * @param topics + * Array of modules topics. + * + */ +function 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 this item's parent is unavailable, + // treat it as top level instead. + $parent = ''; + } + } + else { + $parent = $info['parent']; + if (empty($module_topics[$parent])) { + // If this item's parent is unavailable, + // treat it as top level instead. + $parent = ''; + } + } - $modules = array(); - foreach (module_implements('help', TRUE) as $module) { - if (module_invoke($module, 'help', "admin/help#$module", $empty_arg)) { - $modules[$module] = $module_info[$module]->info['name']; + 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); } } - asort($modules); +} + +/** + * Get the information for a single help topic. + * + * @param $module + * Module name of the topic. + * @param $topic + * Topic to retrieve. + * + * @return + * An array of the topics information, or NULL if the topic doesn't exist. + */ +function help_get_topic_info($module, $topic) { + $topics = help_get_topics(); + if (!empty($topics[$module][$topic])) { + return $topics[$module][$topic]; + } +} - // Output pretty four-column list - $count = count($modules); - $break = ceil($count / 4); - $output = '
'; - return $output; + return $cache; +} + +/** + * Sort topic information array in ascending order. + * + * @param $id_a + * Module/topic 1 + * @param $id_b + * Module/topic 2 + * + * @return + * -1 = $id_a comes before $id_b. + * 1 = $id_b comes before $id_a. + */ +function _help_uasort($id_a, $id_b) { + $topics = 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; + // Sort by topic weight when weights are unequal. + if ($a_weight != $b_weight) { + return ($a_weight < $b_weight) ? -1 : 1; + } + + // Otherwise sort by the title. + if ($a['title'] != $b['title']) { + return ($a['title'] < $b['title']) ? -1 : 1; + } + + return 0; +} + +/** + * Return help topic filename. + * + * @param $module + * The module that owns the help topic. + * @param $topic + * Name of the topic. + * + * @return + * The path to the file of the topic. + */ +function help_get_topic_filename($module, $topic) { + $info = help_get_topic_file_info($module, $topic); + if ($info) { + return "./$info[path]/$info[file]"; + } +} + +/** + * Return information for the help topic file. + * + * Checks a list of possible locations for a help topic file, allowing + * translations and the current theme to override the default + * location of the file. + * + * @param $module + * The module that owns the help topic. + * @param $topic + * Name of the topic. + * + * @return + * An array containing the path and filename of the topic. + * + */ +function help_get_topic_file_info($module, $topic) { + init_theme(); + global $language; + + $info = help_get_topic_info($module, $topic); + if (empty($info)) { + return; + } + + // Search paths: + $paths = array( + path_to_theme() . '/help', // Allow theme override. + drupal_get_path('module', $module) . "/translations/help/$language->language", // Translations. + $info['path'], // In same directory as .inc file. + ); + + foreach ($paths as $path) { + if (file_exists("./$path/$info[file]")) { + return array('path' => $path, 'file' => $info['file']); + } + } +} + +/** + * Helper function to get a module's proper name. + * + * @param $module + * Module name to look up. + * + * @return + * The name of the module. + */ +function help_get_module_name($module) { + $settings = help_get_settings(); + if (isset($settings[$module]['name'])) { + $name = $settings[$module]['name']; + } + else { + $info = db_query("SELECT * FROM {system} WHERE name = :name", array(':name' => $module))->fetchObject(); + $info = unserialize($info->info); + $name = t($info['name']); + } + return $name; +} + +/** + * Format a link. + * + * @param $text + * The text to be enclosed with the anchor tag. + * @param $path + * The Drupal path being linked to, such as "admin/content/node". Can be an + * external or internal URL. + * - If you provide the full URL, it will be considered an external URL. + * - If you provide only the path (e.g. "admin/content/node"), it is + * considered an internal link. In this case, it must be a system URL + * as the url() function will generate the alias. + * - If you provide '', it generates a link to the site's + * base URL (again via the url() function). + * - If you provide a path, and 'alias' is set to TRUE (see below), it is + * used as is. + * @param $options + * An associative array of additional options, with the following keys: + * - 'attributes' + * An associative array of HTML attributes to apply to the anchor tag. + * - 'query' + * A query string to append to the link, or an array of query key/value + * properties. + * - 'fragment' + * A fragment identifier (named anchor) to append to the link. + * Do not include the '#' character. + * - 'absolute' (default FALSE) + * Whether to force the output to be an absolute link (beginning with + * http:). Useful for links that will be displayed outside the site, such + * as in an RSS feed. + * - 'html' (default FALSE) + * Whether the title is HTML, or just plain-text. For example for making + * an image a link, this must be set to TRUE, or else you will see the + * escaped HTML. + * - 'alias' (default FALSE) + * Whether the given path is an alias already. + * @return + * an HTML string containing a link to the given path. + * + */ +function help_l($text, $dest, $options = array()) { + return l($text, $dest, $options); } diff --git a/modules/help/help.css b/modules/help/help.css index 748e190..c15de7a 100644 --- a/modules/help/help.css +++ b/modules/help/help.css @@ -5,6 +5,94 @@ width: 22%; padding-right: 3%; /* LTR */ } + .help-items-last { padding-right: 0; /* LTR */ } + +.help-topic h3, +.help-topic h4, +.help-topic h5, +.help-topic h6, +.help-topic dt { + font-weight: bold; +} + +.help-topic li h3, +.help-topic li h4, +.help-topic li h5, +.help-topic li h6 { + font-weight: normal; +} + +div.item-list ul li { + margin: .15em 0 .15em 1.5em; +} + +.help-topic code, +.help-topic pre { + background: #f1f1f1; + border: 1px solid #BFBFBF; + display: block; + font-size: 1.15em; + margin: 1em; + padding: .2em; +} + +.help-topic .toc-block, .help-topic .toc { + background-color: #fff; + border: 1px solid #D0EBFF; + float: right; /* LTR */ + margin: 0 5px; + padding: 5px 10px 5px 5px; +} + +.help-topic .toc { + display: table; + float: none; +} + +.help-left { + display: block; + float: left; /* LTR */ + text-align: left; + width: 42%; +} + +.help-right { + display: block; + float: right; /* LTR */ + text-align: right; + width: 42%; +} + +.help-up { + display: block; + float: left; /* LTR */ + margin: 0 5%; + width: 4%; +} + +.help-up-noleft { + display: block; + float: left; /* LTR */ + margin: 0 5%; + text-align: right; + width: 42%; +} + +.help-box { + margin: .5em; +} + +.help-navigation { + border-top: 1px dotted #ccc; +} + +.help-previous { + float: left; /* LTR */ +} + +.help-next { + float: right; /* LTR */ +} diff --git a/modules/help/help.info b/modules/help/help.info index 678088e..0978aeb 100644 --- a/modules/help/help.info +++ b/modules/help/help.info @@ -1,6 +1,6 @@ ; $Id: help.info,v 1.7 2008-10-11 02:32:47 webchick Exp $ name = Help -description = Manages the display of online help. +description = Manages the display of help topics. package = Core version = VERSION core = 7.x diff --git a/modules/help/help.module b/modules/help/help.module index af3d35d..8e2a114 100644 --- a/modules/help/help.module +++ b/modules/help/help.module @@ -3,29 +3,56 @@ /** * @file - * Manages displaying online help. + * Manages displaying help topics. + */ + + /** + * Implementation of hook_perm(). */ +function help_perm() { + return array('access help' => + array( + 'title' => 'Access help', + 'description' => t('View help content.'), + ) + ); +} + +/** + * Implementation of hook_theme(). + */ +function help_theme() { + return array( + 'help_topic' => array( + 'arguments' => array('content' => '', 'navigation' => array(), 'children' => array(), 'links' => array()), + ), + ); +} /** * Implementation of hook_menu(). */ function help_menu() { + $items = array(); + $items['admin/help'] = array( 'title' => 'Help', - 'page callback' => 'help_main', - 'access arguments' => array('access administration pages'), + 'page callback' => 'help_by_module', + 'access arguments' => array('access help'), 'weight' => 9, ); - - foreach (module_implements('help', TRUE) as $module) { - $items['admin/help/' . $module] = array( - 'title' => $module, - 'page callback' => 'help_page', - 'page arguments' => array(2), - 'access arguments' => array('access administration pages'), - 'type' => MENU_CALLBACK, - ); - } + $items['admin/help/%'] = array( + 'page callback' => 'help_topic_page', + 'page arguments' => array(2), + 'access arguments' => array('access help'), + 'type' => MENU_CALLBACK, + ); + $items['admin/help/%/%'] = array( + 'page callback' => 'help_topic_page', + 'page arguments' => array(2, 3), + 'access arguments' => array('access help'), + 'type' => MENU_CALLBACK, + ); return $items; } @@ -36,11 +63,44 @@ function help_menu() { function help_help($path, $arg) { switch ($path) { case 'admin/help': - $output = '

' . t('This guide provides context sensitive help on the use and configuration of Drupal and its modules, and is a supplement to the more extensive online Drupal handbook. The online handbook may contain more up-to-date information, is annotated with helpful user-contributed comments, and serves as the definitive reference point for all Drupal documentation.', array('@drupal' => 'http://drupal.org', '@handbook' => 'http://drupal.org/handbook')) . '

'; - return $output; - case 'admin/help#help': - $output = '

' . t('The help module provides context sensitive help on the use and configuration of Drupal and its modules, and is a supplement to the more extensive online Drupal handbook. The online handbook may contain more up-to-date information, is annotated with helpful user-contributed comments, and serves as the definitive reference point for all Drupal documentation.', array('@drupal' => 'http://drupal.org', '@handbook' => 'http://drupal.org/handbook')) . '

'; - $output .= '

' . t('For more information, see the online handbook entry for Help module.', array('@help' => 'http://drupal.org/handbook/modules/help/')) . '

'; - return $output; + return '

' . t('This guide provides context sensitive help on the use and configuration of Drupal and its modules, and is a supplement to the more extensive online Drupal handbook. The online handbook may contain more up-to-date information, is annotated with helpful user-contributed comments, and serves as the definitive reference point for all Drupal documentation.', array('@drupal' => 'http://drupal.org', '@handbook' => 'http://drupal.org/handbook')) . '

'; } } + +/** + * Return code that emits a link to view a topic. + * + * @param $module + * The module that owns this help topic. + * @param $topic + * Optional identifier for the topic. NULL value displays all available topics. + * @param $title + * Optional title or label for the link. Default is "More help". + * @param $attributes + * An array of attributes to include in hyperlink. + */ +function theme_help_link($module, $topic = NULL, $title = NULL, $attributes = array()) { + static $js_added = FALSE; + + if (!isset($title)) { + $title = t('More help'); + } + + // Check for the function existence and include help.admin.inc. + drupal_function_exists('help_get_topic_info'); + // Fetch the information on the module/topic. + $info = help_get_topic_info($module, $topic); + + if (isset($topic) && !$info) { + // Return if the explicitly specified topic doesn't exist. + return; + } + + // Set the topic title as the hyperlink's title attribute. + $attributes += array('title' => $info['title']); + + // Trim the trailing slash if no topic is specified. + $output = l($title, trim("admin/help/$module/$topic", '/'), array('attributes' => $attributes)); + + return ''; +} diff --git a/modules/help/help.test b/modules/help/help.test index 9d13435..0fde7d2 100644 --- a/modules/help/help.test +++ b/modules/help/help.test @@ -8,77 +8,44 @@ class HelpTestCase extends DrupalWebTestCase { function getInfo() { return array( 'name' => t('Help functionality'), - 'description' => t('Verify help display and user access to help based on persmissions.'), + 'description' => t('Verify help display and user access to help based on permissions.'), 'group' => t('Help'), ); } /** - * Enable modules and create users with specific permissions. - */ - function setUp() { - parent::setUp(); - - // Loading these (and other?) modules will result in failures? -// $this->drupalModuleEnable('blog'); -// $this->drupalModuleEnable('poll'); - $this->getModuleList(); - - // Create users. - $this->big_user = $this->drupalCreateUser(array('access administration pages')); // 'administer blocks', 'administer site configuration', - $this->any_user = $this->drupalCreateUser(array()); - } - - /** * Login users, create dblog events, and test dblog functionality through the admin and user interfaces. */ function testHelp() { // Login the admin user. - $this->drupalLogin($this->big_user); - $this->verifyHelp(); - - // Login the regular user. - $this->drupalLogin($this->any_user); - $this->verifyHelp(403); - } - - /** - * Verify the logged in user has the desired access to the various help nodes and the nodes display help. - * - * @param integer $response HTTP response code. - */ - private function verifyHelp($response = 200) { - $crumb = '›'; - - foreach ($this->modules as $module => $name) { - // View module help node. - $this->drupalGet('admin/help/' . $module); - $this->assertResponse($response); - if ($response == 200) { - // NOTE: The asserts fail on blog and poll because the get returns the 'admin/help' node instead of the indicated node??? -// if ($module == 'blog' || $module == 'poll') { -// continue; -// } - $this->assertTitle($name . ' | Drupal', t('[' . $module . '] Title was displayed')); - $this->assertRaw('

' . t($name) . '

', t('[' . $module . '] Heading was displayed')); - $this->assertText(t('Home ' . $crumb . ' Administer ' . $crumb . ' Help'), t('[' . $module . '] Breadcrumbs were displayed')); - } + $account = $this->drupalCreateUser(array('access administration pages', 'access help', 'administer blocks')); + $this->drupalLogin($account); + $sections = parse_ini_file('modules/help/help/help.help', TRUE); + foreach ($sections as $section => $data) { + $links[url("admin/help/help/$section")] = $data['title']; } - } - /** - * Get list of enabled modules. - * - * @return array Enabled modules. - */ - private function getModuleList() { - $this->modules = array(); - $result = db_query("SELECT name, filename, info FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC"); - foreach ($result as $module) { - if (file_exists($module->filename)) { - $fullname = unserialize($module->info); - $this->modules[$module->name] = $fullname['name']; + foreach ($sections as $section => $data) { + $this->drupalGet("admin/help/help/$section"); + $this->assertTitle($data['title'] .' | Drupal', t('title matched')); + $help = file_get_contents("modules/help/help/$section.html"); + if (!empty($data['line break'])) { + $help = preg_replace('/^\n/', '', $help); + $help = filter_filter('process', 1, -1, $help); } - } + // There are various replaces done on the help text. We do not test + // these now, but skip text inside []. + $help_pieces = preg_split('/\[[^\]]+\]/', $help); + foreach ($help_pieces as $piece) { + $this->assertRaw($piece, t('help text found')); + } + // Check the links in the table of contents. + foreach ($links as $href => $title) { + $this->assertTrue($this->xpath('//a[text()="'. $title .'" and @href="'. $href .'"]'), t('Link @href, @title found', array('@href' => $href, '@title' => $title))); + } + } + $this->drupalGet('admin/build/block'); + $this->clickLink('More help'); + $this->assertLink('About Block', 0, t('About Block link found')); } -} +} \ No newline at end of file diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 82eef0a..b31d360 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -85,10 +85,8 @@ function system_admin_menu_block_page() { * Menu callback; prints a listing of admin tasks for each installed module. */ function system_admin_by_module() { - $modules = module_rebuild_cache(); $menu_items = array(); - $help_arg = module_exists('help') ? drupal_help_arg() : FALSE; foreach ($modules as $file) { $module = $file->name; @@ -101,8 +99,8 @@ function system_admin_by_module() { // Only display a section if there are any available tasks. if (count($admin_tasks)) { - // Check for help links. - if ($help_arg && module_invoke($module, 'help', "admin/help#$module", $help_arg)) { + // Check for help topics. + if (help_exists($module)) { $admin_tasks[100] = l(t('Get help'), "admin/help/$module"); } @@ -598,9 +596,6 @@ function system_modules($form_state = array()) { $modules = array(); $form['modules'] = array('#tree' => TRUE); - // Used when checking if module implements a help page. - $help_arg = module_exists('help') ? drupal_help_arg() : FALSE; - // Iterate through each of the modules. foreach ($files as $filename => $module) { $extra = array(); @@ -618,13 +613,11 @@ function system_modules($form_state = array()) { $extra['requires'][$requires] = t('@module (enabled)', array('@module' => $files[$requires]->info['name'])); } } - // Generate link for module's help page, if there is one. - if ($help_arg && $module->status && in_array($filename, module_implements('help'))) { - if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) { - // Module has a help page. - $extra['help'] = theme('more_help_link', url("admin/help/$filename")); - } - } + // Generate link for module's help topics, if there are any . + if (help_exists($filename)) { + // Module has help topics. + $extra['help'] = theme('help_link', $filename, NULL, t('Get help')); + } // Mark dependents disabled so the user cannot remove required modules. $dependents = array(); // If this module is required by other modules, list those, and then make it @@ -744,7 +737,7 @@ function _system_modules_build_row($info, $extra) { $form['description']['#markup'] .= theme('system_modules_incompatible', $status_long); } - // Show a "more help" link for modules that have them. + // Show a help link for modules that have them. if ($extra['help']) { $form['help'] = array( '#markup' => $extra['help'],