Last updated May 31, 2012. Created on May 31, 2012.
Edited by wildkatana. Log in to edit this page.

Project Browser Server (http://drupal.org/project/project_browser_server) allows you to create a repository and serve query results to clients running the Project Browser (http://drupal.org/project/project_browser) module.

There are two hooks that you need to implement. They are

/**
* Implementation of hook_project_browser_server_categories()
*
* The function should return an array like this:
*
* array(
*   'category_1' => "Category 1",
*   'category_2' => "Category 2",
*   'category_3' => "Category 3",
* );
*
* @param $type
*   The type of project to get the categories for, 'module' or 'theme'
* @return array
*   Returns an array of categories
*/
function pbs_example_project_browser_server_categories($type) {
  $categories = array();
 
  switch ($type) {
    case 'module':
      $categories = array(
        'admin' => "Administrative",
        'search' => "Search",
        'user_management' => "User Management",
      );
      break;
     
    case 'theme':
      $categories = array(
        'dark' => "Dark",
        'light' => "Light",
      );
  }

  return $categories;
}

The categories hook let's you define which categories should be exposed as filters to Project Browser. It simply takes the parameter of type (module or theme) and returns an associative array of categories keyed by whatever you use internally as the key. Simple enough.

/**
* Implementation of hook_project_browser_server_query()
*
* The function takes a $filters parameter that looks like this:
*
* array(
*   'version' => '7', // The Major Version of Drupal that is running on the Client
*   'text' => 'views', // The text that was entered as the search query, or '' if none
*   'categories' => array() // The categories that were selected, if any
*   'type' => 'module', // The type of project being searched
*   'page' => 3, // The zero-based page number
*   'requested' => 12, // How many results are requested per page
* )
*
* The function should return an array like this:
*
* array(
*   'total' = 5, // The total number of results found for the filters
*   'projects' => array( // An array of projects returned for this page request
*     'views' => array( // A project array keyed by the machine name
*       'type' => 'module', // The type of project this is. Can be 'module' or 'theme'
*       'title' => 'Views', // The title of the project
*       'name' => 'views', // The machine name of the project
*       'author' => 'merlinofchaos', // The author's name
*       'description' => "Long project description ...",
*       'image' => 'http://www.example.com/image.jpg', // Absolute url to the image, if any
*       'usage' => '542312', // How many Downloads the module has had
*       'project url' => 'http://www.drupal.org/projects/views', // Absolute url to the project page, if any
*       'project status url' => 'http://updates.drupal.org/release-history/views/7.x', // The absolute url of the update checker, formatted like how Drupal.org Update Status does it
*       'last updated' => '12342523', // UNIX Timestamp of when the project was last updated
*       'maintenance status' => 'Actively maintained', // Maintenance status
*       'development status' => 'Under active development', // Development status
*       'rating' => '9.6', // A rating on a scale of 1 to 10 of the project, if available
*       'dependencies' => array( // An array of the dependencies of this module
*         'ctools',
*       ),
*     ),
*   'name_2 => array( ... ),
*   ),
* );
*
* @param $filters
*   An associative array of the filters and their values
* @return array
*   Returns an array of results, formatted how Project Browser likes it
*/
function pbs_example_project_browser_server_query($filters) {
  $projects = pbs_example_static_projects();
 
  $results = array(
    'total' => count($projects),
    'projects' => array(),
  );
 
  // Filter out projects based on type
  if (isset($filters['type']) AND $type = $filters['type']) {
    foreach ($projects as $name => $project) {
      if ($type != $project['type']) {
        unset($projects[$name]);
      }
    }
  }
 
  // Filter out projects based on drupal version number
  if (isset($filters['version']) AND $version = $filters['version']) {
    foreach ($projects as $name => $project) {
      if ($version != $project['drupal version']) {
        unset($projects[$name]);
      }
    }
  }
 
  // Filter out projects based on categories number
  if (isset($filters['categories']) AND is_array($filters['categories']) AND !empty($filters['categories'])) {
    $filtered = array();
    foreach ($projects as $name => $project) {
      foreach ($project['categories'] as $category) {
        if (in_array($category, $filters['categories'])) {
          $filtered[$name] = $project;
        }
      }
    }
    $projects = $filtered;
  }

  // Filter out projects based on the text query
  if (isset($filters['text']) AND $text = $filters['text']) {
    foreach ($projects as $name => $project) {
      if (!stristr($project['title'], $text) AND !stristr($project['description'], $text)) {
        unset($projects[$name]);
      }
    }
  }
 
  $results['total'] = count($projects);
 
  // FIXME - Only send back the requested amount
  $start = $filters['page'] * $filters['requested'];
  $end = $start + $filters['requested'];
 
  $results['projects'] = $projects;
 
  // Log this for debugging purposes
  watchdog('pbs_example', 'Query filters: !filters Results: !results',
    array(
      '!filters' => '<pre>'. print_r($filters, TRUE) .'</pre>',
      '!results' => '<pre>'. print_r($results, TRUE) .'</pre>',
    )
  );
 
  return $results;
}

The server query hook is a bit more complex. Basically, it takes a filters parameter which is an associative array of the different filters that were used in the Project Browser module, and it expects an associative array of projects which match the filter criteria.

For this simple example, we are just using a bunch of static project data, but most real life situations will query a database for the results. The static data function is here, but you won't need to use any of this code for your implementation. It is best to just query a database.

// ======================================
// Static Data:
// ======================================

/**
* Populate some static data to use for the example module.
*
* In most cases, this information will be pulled from various database tables.
* This function only returns release info for Drupal 7 projects
*/
function pbs_example_static_projects() {
  $projects = array();
 
  $projects['views'] = array(
    'type' => 'module',
    'title' => 'Views',
    'name' => 'views',
    'drupal version' => 7,
    'author' => 'merlinofchaos',
    'description' => "The Views module provides a flexible method for Drupal site
      designers to control how lists and tables of content (nodes in Views 1, almost
      anything in Views 2) are presented. Traditionally, Drupal has hard-coded most of
      this, particularly in how taxonomy and tracker lists are formatted. ",
    'drupal_versions' => array(6, 7),
    'categories' => array('admin', 'search'),
    'image' => 'http://learnbythedrop.com/system/files/images/View-Edit_0.png',
    'usage' => '542312',
    'project url' => 'http://www.drupal.org/projects/views',
    'project status url' => 'http://updates.drupal.org/release-history/views/7.x',
    'last updated' => '12342523',
    'maintenance status' => 'Actively maintained',
    'development status' => 'Under active development',
    'rating' => '9.6',
    'dependencies' => array(
      'ctools',
    ),
  );
 
  $projects['ctools'] = array(
    'type' => 'module',
    'title' => 'Chaos Tool Suite',
    'name' => 'ctools',
    'drupal version' => 7,
    'author' => 'merlinofchaos',
    'description' => "This suite is primarily a set of APIs and tools to improve
      the developer experience. It also contains a module called the Page Manager
      whose job is to manage pages. In particular it manages panel pages, but as
      it grows it will be able to manage far more than just Panels.",
    'drupal_versions' => array(6, 7),
    'categories' => array(),
    'image' => '',
    'usage' => '4312',
    'project url' => 'http://www.drupal.org/projects/ctools',
    'project status url' => 'http://updates.drupal.org/release-history/ctools/7.x',
    'last updated' => '12354634',
    'maintenance status' => 'Actively maintained',
    'development status' => 'Under active development',
    'rating' => '7.6',
    'dependencies' => array(),
  );
 
  $projects['token'] = array(
    'type' => 'module',
    'title' => 'Token',
    'name' => 'token',
    'drupal version' => 7,
    'author' => 'eaton',
    'description' => "Tokens are small bits of text that can be placed into larger
      documents via simple placeholders, like %site-name or [user]. The Token module
      provides a central API for modules to use these tokens, and expose their own token values.",
    'categories' => array('admin'),
    'image' => 'http://drupal.org/files/images/token_08.thumbnail.png',
    'usage' => '4563',
    'project url' => 'http://www.drupal.org/projects/token',
    'project status url' => 'http://updates.drupal.org/release-history/token/7.x',
    'last updated' => '12357351',
    'maintenance status' => 'Actively maintained',
    'development status' => 'Under active development',
    'rating' => '8.1',
    'dependencies' => array(),
  );
 
  $projects['zen'] = array(
    'type' => 'theme',
    'title' => 'Zen',
    'name' => 'zen',
    'drupal version' => 7,
    'author' => 'johnAlbin',
    'description' => "Zen is the ultimate starting theme for Drupal. If you are
      building your own standards-compliant theme, you will find it much easier to
      start with Zen than to start with Garland or Bluemarine. This theme has fantastic
      online documentation and tons of code comments for both the PHP (template.php)
      and HTML (page.tpl.php, node.tpl.php).",
    'categories' => array('light', 'dark'),
    'image' => 'http://drupal.org/files/images/zen-logo.thumbnail.png',
    'usage' => '4563',
    'project url' => 'http://www.drupal.org/project/zen',
    'project status url' => 'http://updates.drupal.org/release-history/zen/7.x',
    'last updated' => '12343634',
    'maintenance status' => 'Actively maintained',
    'development status' => 'Under active development',
    'rating' => '7.1',
    'dependencies' => array(),
  );
 
  $projects['acquia_marina'] = array(
    'type' => 'theme',
    'title' => 'Acquia Marina',
    'name' => 'acquia_marina',
    'drupal version' => 7,
    'author' => 'stephthegeek',
    'description' => "The Fusion base theme and Skinr are required. Skinr for Drupal 7
      (dev release) is usable now but it is recommended that you proceed with caution
      and do some of your own testing.",
    'categories' => array('light'),
    'image' => 'http://drupal.org/files/images/acquia_marina.thumbnail.png',
    'usage' => '14563',
    'project url' => 'http://www.drupal.org/project/acquia_marina',
    'project status url' => 'http://updates.drupal.org/release-history/acquia_marina/7.x',
    'last updated' => '12346574',
    'maintenance status' => 'Actively maintained',
    'development status' => 'Under active development',
    'rating' => '7.8',
    'dependencies' => array(
      'fusion'
    ),
  );
 
  $projects['fusion'] = array(
    'type' => 'theme',
    'title' => 'Fusion',
    'name' => 'fusion',
    'drupal version' => 7,
    'author' => 'stephthegeek',
    'description' => "Fusion is a powerful base theme, with layout and style configuration
      options built in that you can control through Drupal's UI. It's based on a simplified
      960px or fluid 12/16-column grid. It's designed to be used with the Skinr module,
      with numerous useful block styles included.",
    'categories' => array('light'),
    'image' => 'http://drupal.org/files/images/fusion-powering-small-banner.thumbnail.png',
    'usage' => '14563',
    'project url' => 'http://www.drupal.org/project/fusion',
    'project status url' => 'http://updates.drupal.org/release-history/fusion/7.x',
    'last updated' => '12342643',
    'maintenance status' => 'Actively maintained',
    'development status' => 'Under active development',
    'rating' => '',
    'dependencies' => array(),
  );
 
  return $projects;
}

The important thing to pay attention to is the filters:

array(
   'version' => '7', // The Major Version of Drupal that is running on the Client
   'text' => 'views', // The text that was entered as the search query, or '' if none
   'categories' => array() // The categories that were selected, if any
   'type' => 'module', // The type of project being searched
   'page' => 3, // The zero-based page number
   'requested' => 12, // How many results are requested per page
)

And the output:

array(
   'total' = 5, // The total number of results found for the filters
   'projects' => array( // An array of projects returned for this page request
     'views' => array( // A project array keyed by the machine name
       'type' => 'module', // The type of project this is. Can be 'module' or 'theme'
       'title' => 'Views', // The title of the project
       'name' => 'views', // The machine name of the project
       'author' => 'merlinofchaos', // The author's name
       'description' => "Long project description ...",
       'image' => 'http://www.example.com/image.jpg', // Absolute url to the image, if any
       'usage' => '542312', // How many Downloads the module has had
       'project url' => 'http://www.drupal.org/projects/views', // Absolute url to the project page, if any
       'project status url' => 'http://updates.drupal.org/release-history/views/7.x', // The absolute url of the update checker, formatted like how Drupal.org Update Status does it
       'last updated' => '12342523', // UNIX Timestamp of when the project was last updated
       'maintenance status' => 'Actively maintained', // Maintenance status
       'development status' => 'Under active development', // Development status
       'rating' => '9.6', // A rating on a scale of 1 to 10 of the project, if available
       'dependencies' => array( // An array of the dependencies of this module
         'ctools',
       ),
     ),
   'name_2 => array( ... ),
   ),
);

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.