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 @@
+