diff --git a/api.module b/api.module index 705db0a..9c77331 100644 --- a/api.module +++ b/api.module @@ -1267,12 +1267,25 @@ function api_block($op, $delta = NULL, $edit = array()) { if (user_access('access API reference') && !empty($branch)) { $links = array(); $links[] = l($branch->title, 'api/' . $branch->project . '/' . $branch->branch_name); - $links[] = l(t('Constants'), 'api/' . $branch->project . '/constants/' . $branch->branch_name); - $links[] = l(t('Classes'), 'api/' . $branch->project . '/classes/' . $branch->branch_name); - $links[] = l(t('Files'), 'api/' . $branch->project . '/files/' . $branch->branch_name); - $links[] = l(t('Functions'), 'api/' . $branch->project . '/functions/' . $branch->branch_name); - $links[] = l(t('Globals'), 'api/' . $branch->project . '/globals/' . $branch->branch_name); - $links[] = l(t('Topics'), 'api/' . $branch->project . '/groups/' . $branch->branch_name); + $counts = api_listing_counts($branch); + if ($counts['constants'] > 0) { + $links[] = l(t('Constants'), 'api/' . $branch->project . '/constants/' . $branch->branch_name); + } + if ($counts['classes'] > 0) { + $links[] = l(t('Classes'), 'api/' . $branch->project . '/classes/' . $branch->branch_name); + } + if ($counts['files'] > 0) { + $links[] = l(t('Files'), 'api/' . $branch->project . '/files/' . $branch->branch_name); + } + if ($counts['functions'] > 0) { + $links[] = l(t('Functions'), 'api/' . $branch->project . '/functions/' . $branch->branch_name); + } + if ($counts['globals'] > 0) { + $links[] = l(t('Globals'), 'api/' . $branch->project . '/globals/' . $branch->branch_name); + } + if ($counts['groups'] > 0) { + $links[] = l(t('Topics'), 'api/' . $branch->project . '/groups/' . $branch->branch_name); + } return array( 'subject' => t('API Navigation'), @@ -1285,6 +1298,61 @@ function api_block($op, $delta = NULL, $edit = array()) { } /** + * Returns an array of the counts of each type of listing for a branch. + * + * @param $branch + * Object representing the branch to count. + * + * @return + * Associative array where the keys are the type of listing ('functions', + * 'classes', etc.) and the values are the count of how many there are in + * that listing for the given branch. + */ +function api_listing_counts($branch) { + + static $cached_counts = array(); + + // Check the cache. + $key = $branch->branch_name . $branch->branch_id; + if (isset($cached_counts[$key])) { + return $cached_counts[$key]; + } + + $return = array( + 'groups' => 0, + 'classes' => 0, + 'functions' => 0, + 'constants' => 0, + 'globals' => 0, + 'files' => 0, + ); + + // These queries mirror what is done in api_page_listing(). + $result = db_query("SELECT COUNT(*) as num FROM {api_documentation} WHERE branch_id = %d AND object_type = 'group' GROUP BY branch_id", $branch->branch_id); + while ($obj = db_fetch_object($result)) { + $return['groups'] = $obj->num; + break; + } + + $result = db_query("SELECT COUNT(*) as num FROM {api_documentation} WHERE branch_id = %d AND ( object_type = 'class' OR 'object_type' = 'interface') AND class_did = 0 GROUP BY branch_id", $branch->branch_id); + while ($obj = db_fetch_object($result)) { + $return['classes'] = $obj->num; + break; + } + + foreach (array('function', 'constant', 'global', 'file') as $type) { + $result = db_query("SELECT COUNT(*) as num FROM {api_documentation} WHERE branch_id = %d AND object_type = '%s' AND class_did = 0 GROUP BY branch_id", $branch->branch_id, $type); + while ($obj = db_fetch_object($result)) { + $return[$type . 's'] = $obj->num; + break; + } + } + + $cached_counts[$key] = $return; + return $return; +} + +/** * Implementation of hook_filter(). */ function api_filter($op, $delta = 0, $format = -1, $text = '') { @@ -1712,10 +1780,19 @@ function api_switch_project($current_branch, $url = '') { $links = array(); foreach (api_get_branches() as $branch) { if ($branch->status && $branch->project !== $current_branch->project && !isset($links[$branch->project])) { - $links[$branch->project] = array( - 'title' => $branch->project_title, - 'href' => 'api/' . $branch->project . $url, - ); + // Only make a link to this project if there are items of this type, or + // if the URL suffix is blank (indicating the project home page). + $has_items = empty($url); + if (!$has_items) { + $counts = api_listing_counts($branch); + $has_items = ($counts[substr($url,1)] > 0); + } + if ($has_items) { + $links[$branch->project] = array( + 'title' => $branch->project_title, + 'href' => 'api/' . $branch->project . $url, + ); + } } } if (count($links) > 0) { diff --git a/templates/api-branch-default-page.tpl.php b/templates/api-branch-default-page.tpl.php index 6e551e9..09ae55d 100644 --- a/templates/api-branch-default-page.tpl.php +++ b/templates/api-branch-default-page.tpl.php @@ -14,14 +14,40 @@ * - $branch->excluded_directories: The local excluded directories. */ ?> - + 0) { +?>

project . '/groups/' . $branch->branch_name); ?>

+ 0) { +?>

project . '/files/' . $branch->branch_name); ?>

+ 0) { +?>

project . '/globals/' . $branch->branch_name); ?>

+ 0) { +?>

project . '/constants/' . $branch->branch_name); ?>

+ 0) { +?>

project . '/functions/' . $branch->branch_name); ?>

+ 0) { +?>

project . '/classes/' . $branch->branch_name); ?>

+

diff --git a/tests/api_web_test.test b/tests/api_web_test.test index cb6f43c..2631e69 100644 --- a/tests/api_web_test.test +++ b/tests/api_web_test.test @@ -729,3 +729,182 @@ class ApiAdminPagesTestCase extends ApiWebPagesBaseTest { return db_result(db_query('SELECT COUNT(*) from {api_file} WHERE modified < 100')); } } + +/** + * Provides a base class for testing multiple projects for the API module. + */ +class ApiMultipleProjectsBaseTest extends ApiWebPagesBaseTest { + + protected $branch_info_2; + + /** + * Overrides ApiWebPagesBaseTest::setUp(). + * + * Sets up two branches in two different projects using the admin interface. + */ + public function setUp() { + // Save the default branch for teardown. + $this->default_branch = variable_get('api_default_branch', NULL); + variable_del('api_default_branch'); + DrupalWebTestCase::setUp('job_queue', 'grammar_parser', 'api', 'ctools'); + + // Set up a super-user. + $this->super_user = $this->drupalCreateUser(array( + 'access API reference', + 'administer API reference', + 'access content', + 'access administration pages', + 'administer blocks', + )); + $this->drupalLogin($this->super_user); + + // Create a "file" branch with the sample code, from the admin interface. + $this->branch_info = array( + 'project' => 'test', + 'branch_name' => '6', + 'project_title' => 'Project 6', + 'title' => 'Testing 6', + 'status' => 1, + 'data[directories]' => drupal_get_path('module', 'api') .'/tests/sample', + ); + + $this->drupalPost('admin/settings/api/branches/new/files', + $this->branch_info, + t('Save branch') + ); + + // Find the ID that was assigned to the function branch. + $branches = api_get_branches(TRUE); + $this_id = 0; + foreach ($branches as $branch) { + if ($branch->title == $this->branch_info['title']) { + $this_id = $branch->branch_id; + break; + } + } + + // Make this the default branch. + $this->drupalPost('admin/settings/api/branches/list', + array( + 'default_branch' => $this_id, + ), + t('Save changes') + ); + + // Create a second "file" branch in a different project, but with the same + // branch name. + $this->branch_info_2 = array( + 'project' => 'test2', + 'branch_name' => '6', + 'project_title' => 'Project 2', + 'title' => 'Testing 6', + 'status' => 1, + 'data[directories]' => drupal_get_path('module', 'api') .'/tests/sample2', + ); + + $this->drupalPost('admin/settings/api/branches/new/files', + $this->branch_info_2, + t('Save branch') + ); + + // Parse the code. + api_get_branches(TRUE); + + include_once drupal_get_path('module', 'api') .'/parser.inc'; + api_update_all_branches(); + while (job_queue_dequeue()) { } + api_shutdown(); + + api_get_branches(TRUE); + } +} + +/** + * Tests that links to empty listing pages are not created. + */ +class ApiEmptyPageLinksTestCase extends ApiMultipleProjectsBaseTest { + + public static function getInfo() { + return array( + 'name' => t('Empty listing page links'), + 'description' => t('Tests that links to empty listing pages are not made.'), + 'group' => 'API Module', + ); + } + + /** + * Tests that links to empty listing pages are not present. + */ + function testEmptyPageLinks() { + // Visit the branch home page for the first project. + $this->drupalGet('api/' . $this->branch_info['project']); + // Verify that all the listing page links are there. + $this->assertLink('Files', 0, 'Files link is present'); + $this->assertLink('Functions', 0, 'Functions link is present'); + $this->assertLink('Classes and Interfaces', 0, 'Classes link is present'); + $this->assertLink('Constants', 0, 'Constants link is present'); + $this->assertLink('Globals', 0, 'Globals link is present'); + $this->assertLink('Topics', 0, 'Topics link is present'); + // Verify link to the other project is present. + $this->assertLink($this->branch_info_2['project_title'], 0, 'Link to other project is present'); + + // Visit the branch home page for the second project. + $this->drupalGet('api/' . $this->branch_info_2['project']); + // Verify that the correct listing page links are there. + $this->assertLink('Files', 0, 'Files link is present'); + $this->assertLink('Functions', 0, 'Functions link is present'); + // Verify that the listing pages that would be empty are not present. + $this->assertNoLink('Classes and Interfaces', 'Classes link is not present'); + $this->assertNoLink('Constants', 'Constants link is not present'); + $this->assertNoLink('Globals', 'Globals link is not present'); + $this->assertNoLink('Topics', 'Topics link is not present'); + // Verify link to the other project is present. + $this->assertLink($this->branch_info['project_title'], 0, 'Link to other project is present'); + + // Visit the Functions listing pages and verify they link to the other + // projects. + $this->drupalGet('api/' . $this->branch_info['project'] . '/functions'); + $this->assertLink($this->branch_info_2['project_title'], 0, 'Link to other project is present'); + $this->drupalGet('api/' . $this->branch_info_2['project'] . '/functions'); + $this->assertLink($this->branch_info['project_title'], 0, 'Link to other project is present'); + + // Visit the Classes and Constants listing pages for the first project and + // verify there is no link to the other project. + $this->drupalGet('api/' . $this->branch_info['project'] . '/classes'); + $this->assertNoLink($this->branch_info_2['project_title'], 'Link to other project is not present'); + $this->drupalGet('api/' . $this->branch_info['project'] . '/constants'); + $this->assertNoLink($this->branch_info_2['project_title'], 'Link to other project is not present'); + + // Turn on the API navigation block. Visit a function page in both + // projects, and verify the right links are showing in the API + // navigation block. + + $this->drupalPost('admin/build/block/list', array( + 'api_navigation[region]' => 'left' + ), + t('Save blocks') + ); + + // Project/branch with all possible types of items. + $this->drupalGet('api/' . $this->branch_info['project'] . '/sample.php/function/sample_function'); + $this->assertText('API Navigation', 'Block title is present'); + $this->assertLink($this->branch_info['title'], 0, 'Branch link is present'); + $this->assertLink('Files', 0, 'Files link is present'); + $this->assertLink('Functions', 0, 'Functions link is present'); + $this->assertLink('Classes', 0, 'Classes link is present'); + $this->assertLink('Constants', 0, 'Constants link is present'); + $this->assertLink('Globals', 0, 'Globals link is present'); + $this->assertLink('Topics', 0, 'Topics link is present'); + + // Project/branch with only files/functions. + $this->drupalGet('api/' . $this->branch_info_2['project'] . '/sample2.php/function/second_sample_function'); + $this->assertText('API Navigation', 'Block title is present'); + $this->assertLink($this->branch_info_2['title'], 0, 'Branch link is present'); + $this->assertLink('Files', 0, 'Files link is present'); + $this->assertLink('Functions', 0, 'Functions link is present'); + $this->assertNoLink('Classes', 'Classes link is not present'); + $this->assertNoLink('Constants', 'Constants link is not present'); + $this->assertNoLink('Globals', 'Globals link is not present'); + $this->assertNoLink('Topics', 'Topics link is not present'); + } +} diff --git a/tests/sample2/sample2.php b/tests/sample2/sample2.php new file mode 100644 index 0000000..928847b --- /dev/null +++ b/tests/sample2/sample2.php @@ -0,0 +1,30 @@ +