This is an implementation of a subset of the Doxygen documentation generator specification, tuned to produce output that best benefits the Drupal code base.
It is designed to assume the code it documents follows Drupal coding conventions, and supports the following Doxygen constructs:
The module was designed to produce the Drupal developer documentation available at !api_site.
Set up
Visit the !api_settings_page to configure the module. You must have the relevant Drupal code base on the same machine as the site hosting the API module. Follow the descriptions in the \'Branches to index\' section to set up the code base for indexing.
Indexing of PHP functions is also supported. If the site has internet access, then the default settings for the \'PHP Manual\' section should work fine. For local development environments that have a PHP manual installed, you can edit the paths to point to the appropriate locations.
The module indexes code branches during cron runs, so make sure the site has cron functionality set up properly.
', array('!api_site' => l('http://api.drupal.org', 'http://api.drupal.org', array('absolute' => TRUE)), '!api_settings_page' => l(t('API settings page'), 'admin/settings/api'))); case 'admin/settings/api/refresh': return t('Parse all indexed code files again, even if they have not been modified.'); } } /** * Returns a list of all defined branches. * * @param $_reset * If set to TRUE, the cached return value is reset. * * @return * Array of branch objects, in order by branch weight. */ function api_get_branches($_reset = FALSE) { static $branches; if (!isset($branches) || $_reset) { $result = db_query("SELECT branch_id, project, branch_name, title, type, data, status FROM {api_branch} ORDER BY weight"); $branches = array(); while ($branch = db_fetch_object($result)) { drupal_unpack($branch); $branches[$branch->branch_id] = $branch; } } return $branches; } /** * Returns the list of currently-used branch names across all projects. * * @param $_reset * If set to TRUE, the cached return value is reset. * * @return * Array of branch names in use. */ function api_get_branch_names($_reset = FALSE) { static $branch_names; if (!isset($branch_names) || $_reset) { $result = db_query("SELECT DISTINCT branch_name FROM {api_branch} WHERE status = 1"); $branch_names = array(); while ($branch = db_fetch_object($result)) { $branch_names[$branch->branch_name] = $branch->branch_name; } } return $branch_names; } /** * Implementation of hook_menu(). */ function api_menu() { $items = array(); $branches = api_get_branches(); if (count($branches)) { $default_branch = $branches[variable_get('api_default_branch', NULL)]; $projects = array(); // We need a default branch for each project. If a project has a branch_name // matching $default_branch, use that. Otherwise, use the max. This assumes // branch names like '5' and '6'. foreach ($branches as $branch) { if ($branch->status) { if (!isset($projects[$branch->project])) { $projects[$branch->project] = array( 'max branch' => $branch->branch_name, 'use branch' => NULL, ); } else { $projects[$branch->project]['max branch'] = max($projects[$branch->project]['max branch'], $branch->branch_name); } if ($branch->branch_name === $default_branch->branch_name) { $projects[$branch->project]['use branch'] = $branch->branch_name; } } } foreach (array_keys($projects) as $project) { if (is_null($projects[$project]['use branch'])) { $projects[$project]['use branch'] = $projects[$project]['max branch']; } } // Part 1: No object, Default branch $items['api/search'] = array( 'title' => 'API Search', 'page callback' => 'drupal_get_form', 'page arguments' => array('api_search_form', $default_branch), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); $items['apis'] = array( 'title' => 'API search', 'page callback' => 'api_search_redirect', 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); } // Admin $items['admin/settings/api'] = array( 'title' => 'API reference', 'description' => 'Configure branches for documentation.', 'access callback' => 'user_access', 'access arguments' => array('administer API reference'), 'page callback' => 'drupal_get_form', 'page arguments' => array('api_page_admin_form'), 'file' => 'api.admin.inc', ); $items['admin/settings/api/branches'] = array( 'title' => 'Branches', 'access callback' => 'user_access', 'access arguments' => array('administer API reference'), 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/settings/api/branches/list'] = array( 'title' => 'List', 'access callback' => 'user_access', 'access arguments' => array('administer API reference'), 'type' => MENU_DEFAULT_LOCAL_TASK ); $items['admin/settings/api/branches/new'] = array( 'title' => 'New branch', 'access callback' => 'user_access', 'access arguments' => array('administer API reference'), 'page callback' => 'api_admin_new_branch_page', 'file' => 'api.admin.inc', 'type' => MENU_LOCAL_TASK, ); $items['admin/settings/api/branches/new/%'] = array( 'title' => 'New branch', 'access callback' => 'user_access', 'access arguments' => array('administer API reference'), 'page callback' => 'drupal_get_form', 'page arguments' => array('api_branch_edit_form', 5), 'file' => 'api.admin.inc', 'type' => MENU_CALLBACK, ); $items['admin/settings/api/branches/%'] = array( 'title' => 'Edit branch', 'access callback' => 'user_access', 'access arguments' => array('administer API reference'), 'page callback' => 'drupal_get_form', 'page arguments' => array('api_branch_edit_form', 4), 'file' => 'api.admin.inc', 'type' => MENU_CALLBACK, ); $items['admin/settings/api/branches/%/delete'] = array( 'access callback' => 'user_access', 'access arguments' => array('administer API reference'), 'page callback' => 'drupal_get_form', 'page arguments' => array('api_branch_delete_form', 4), 'file' => 'api.admin.inc', 'type' => MENU_CALLBACK, ); if (module_exists('comment')) { $items['admin/settings/api/comments'] = array( 'title' => 'Comments', 'access arguments' => array('administer API reference'), 'page callback' => 'drupal_get_form', 'page arguments' => array('api_comments_settings_form'), 'file' => 'api.admin.inc', ); } // OpenSearch metadata callback. $items['api/opensearch/%'] = array( 'page callback' => 'api_opensearch', 'page arguments' => array(2), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); // OpenSearch suggestions callback. $items['api/suggest/%/%menu_tail'] = array( 'page callback' => 'api_suggest', 'page arguments' => array(2, 3), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); // Autocomplete callback. // This returns ALL possibilities for a branch name. $items['api/autocomplete/%'] = array( 'page callback' => 'api_autocomplete', 'page arguments' => array(2, TRUE), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); // Function dumps for IDEs and code editors. $items['api/function_dump/%'] = array( 'page callback' => 'api_page_function_dump', 'page arguments' => array(2), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); foreach (api_get_branch_names() as $branch) { $items['api/search/'. $branch .'/%menu_tail'] = array( 'title' => $branch, 'page callback' => 'api_search_listing', 'page arguments' => array($branch, 3), 'access arguments' => array('access API reference'), 'type' => MENU_LOCAL_TASK, ); } foreach ($branches as $branch) { if ($branch->status) { $is_default = ($branch->branch_name === $projects[$branch->project]['use branch']); // Main branch page if ($is_default) { $items['api/' . $branch->project] = array( 'title' => 'API reference', 'page callback' => 'api_page_branch', 'page arguments' => array($branch), 'access arguments' => array('access API reference'), 'type' => $branch->branch_id === $default_branch->branch_id ? MENU_NORMAL_ITEM : MENU_CALLBACK, ); } $items['api/' . $branch->project . '/' . $branch->branch_name] = array( 'title' => $branch->title, 'page callback' => 'api_page_branch', 'page arguments' => array($branch), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); // Listings if ($is_default) { $items['api/' . $branch->project . '/functions'] = array( 'title' => 'Functions', 'page callback' => 'api_page_listing', 'access arguments' => array('access API reference'), 'page arguments' => array($branch, 'function'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/functions/'. $branch->branch_name] = array( 'title' => $branch->title, 'page callback' => 'api_page_listing', 'page arguments' => array($branch, 'function'), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); if ($is_default) { $items['api/' . $branch->project . '/constants'] = array( 'title' => 'Constants', 'page callback' => 'api_page_listing', 'access arguments' => array('access API reference'), 'page arguments' => array($branch, 'constant'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/constants/'. $branch->branch_name] = array( 'title' => $branch->title, 'page callback' => 'api_page_listing', 'page arguments' => array($branch, 'constant'), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); if ($is_default) { $items['api/' . $branch->project . '/globals'] = array( 'title' => 'Globals', 'page callback' => 'api_page_listing', 'access arguments' => array('access API reference'), 'page arguments' => array($branch, 'global'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/globals/'. $branch->branch_name] = array( 'title' => $branch->title, 'page callback' => 'api_page_listing', 'page arguments' => array($branch, 'global'), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); if ($is_default) { $items['api/' . $branch->project . '/files'] = array( 'title' => 'Files', 'page callback' => 'api_page_listing', 'access arguments' => array('access API reference'), 'page arguments' => array($branch, 'file'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/files/'. $branch->branch_name] = array( 'title' => $branch->title, 'page callback' => 'api_page_listing', 'page arguments' => array($branch, 'file'), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); if ($is_default) { $items['api/' . $branch->project . '/classes'] = array( 'title' => 'Classes', 'page callback' => 'api_page_listing', 'access arguments' => array('access API reference'), 'page arguments' => array($branch, 'class'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/classes/' . $branch->branch_name] = array( 'title' => $branch->title, 'page callback' => 'api_page_listing', 'page arguments' => array($branch, 'class'), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); if ($is_default) { $items['api/' . $branch->project . '/groups'] = array( 'title' => 'Topics', 'page callback' => 'api_page_listing', 'access arguments' => array('access API reference'), 'page arguments' => array($branch, 'group'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/groups/'. $branch->branch_name] = array( 'title' => $branch->title, 'page callback' => 'api_page_listing', 'page arguments' => array($branch, 'group'), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); // Functions if ($is_default) { $items['api/' . $branch->project . '/%api_filename/function/%api_object'] = array( 'title' => 'Function', 'load arguments' => array($branch, 'function', 2), 'page callback' => 'api_page_function', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/%api_filename/function/%api_object/'. $branch->branch_name] = array( 'title' => $branch->title, 'load arguments' => array($branch, 'function', 2), 'page callback' => 'api_page_function', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); // Constants if ($is_default) { $items['api/' . $branch->project . '/%api_filename/constant/%api_object'] = array( 'title' => 'Constant', 'load arguments' => array($branch, 'constant', 2), 'page callback' => 'api_page_constant', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/%api_filename/constant/%api_object/'. $branch->branch_name] = array( 'title' => $branch->title, 'load arguments' => array($branch, 'constant', 2), 'page callback' => 'api_page_constant', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); // Globals if ($is_default) { $items['api/' . $branch->project . '/%api_filename/global/%api_object'] = array( 'title' => 'Global', 'load arguments' => array($branch, 'global', 2), 'page callback' => 'api_page_global', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/%api_filename/global/%api_object/'. $branch->branch_name] = array( 'title' => $branch->title, 'load arguments' => array($branch, 'global', 2), 'page callback' => 'api_page_global', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); // Properties if ($is_default) { $items['api/' . $branch->project . '/%api_filename/property/%api_object'] = array( 'title' => 'Property', 'load arguments' => array($branch, 'property', 2), 'page callback' => 'api_page_property', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/%api_filename/property/%api_object/'. $branch->branch_name] = array( 'title' => $branch->title, 'load arguments' => array($branch, 'property', 2), 'page callback' => 'api_page_property', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); // Classes if ($is_default) { $items['api/' . $branch->project . '/%api_filename/class/%api_object'] = array( 'title' => 'Class', 'load arguments' => array($branch, 'class', 2), 'page callback' => 'api_page_class', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/%api_filename/class/%api_object/'. $branch->branch_name] = array( 'title' => $branch->title, 'load arguments' => array($branch, 'class', 2), 'page callback' => 'api_page_class', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); // Interfaces if ($is_default) { $items['api/' . $branch->project . '/%api_filename/interface/%api_object'] = array( 'title' => 'Class', 'load arguments' => array($branch, 'interface', 2), 'page callback' => 'api_page_class', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/%api_filename/interface/%api_object/'. $branch->branch_name] = array( 'title' => $branch->title, 'load arguments' => array($branch, 'interface', 2), 'page callback' => 'api_page_class', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); // Files if ($is_default) { $items['api/' . $branch->project . '/%api_filename'] = array( 'title' => 'File', 'load arguments' => array($branch), 'page callback' => 'api_page_file', 'page arguments' => array($branch, 2), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/%api_filename/'. $branch->branch_name] = array( 'title' => $branch->title, 'load arguments' => array($branch), 'page callback' => 'api_page_file', 'page arguments' => array($branch, 2), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); $items['api/' . $branch->project . '/%api_filename/'. $branch->branch_name .'/documentation'] = array( 'title' => 'View documentation', 'load arguments' => array($branch), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['api/' . $branch->project . '/%api_filename/'. $branch->branch_name .'/source'] = array( 'title' => 'View source', 'load arguments' => array($branch), 'page callback' => 'api_page_file_source', 'page arguments' => array($branch, 2), 'access arguments' => array('access API reference'), 'type' => MENU_LOCAL_TASK, ); // Groups if ($is_default) { $items['api/' . $branch->project . '/%api_filename/group/%api_object'] = array( 'title' => 'Topic', 'load arguments' => array($branch, 'group', 2), 'page callback' => 'api_page_group', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => MENU_CALLBACK, ); } $items['api/' . $branch->project . '/%api_filename/group/%api_object/'. $branch->branch_name] = array( 'title' => $branch->title, 'load arguments' => array($branch, 'group', 2), 'page callback' => 'api_page_group', 'page arguments' => array($branch, 4), 'access arguments' => array('access API reference'), 'type' => $is_default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, ); } } // Redirect 1.0 file links. $items['api/file/%menu_tail'] = array( 'page callback' => 'api_file_redirect', 'page arguments' => array(2), 'access callback' => TRUE, 'file' => 'legacy.inc', ); // Redirect 1.1 links. // Objects $items['api/function/%api_legacy_1_1_object'] = $items['api/function/%api_legacy_1_1_object/%'] = array( 'load arguments' => array('function', 3), 'page callback' => 'drupal_goto', 'page arguments' => array(2), 'access callback' => TRUE, ); $items['api/constant/%api_legacy_1_1_object'] = $items['api/constant/%api_legacy_1_1_object/%'] = array( 'load arguments' => array('constant', 3), 'page callback' => 'drupal_goto', 'page arguments' => array(2), 'access callback' => TRUE, ); $items['api/global/%api_legacy_1_1_object'] = $items['api/global/%api_legacy_1_1_object/%'] = array( 'load arguments' => array('global', 3), 'page callback' => 'drupal_goto', 'page arguments' => array(2), 'access callback' => TRUE, ); $items['api/topic/%api_legacy_1_1_object'] = $items['api/topic/%api_legacy_1_1_object/%'] = array( 'load arguments' => array('topic', 3), 'page callback' => 'drupal_goto', 'page arguments' => array(2), 'access callback' => TRUE, ); // Default listings $items['api/functions'] = array( 'page callback' => 'drupal_goto', 'page arguments' => array('api/' . $default_branch->project . '/functions'), 'access callback' => TRUE, ); $items['api/files'] = array( 'page callback' => 'drupal_goto', 'page arguments' => array('api/' . $default_branch->project . '/files'), 'access callback' => TRUE, ); $items['api/constants'] = array( 'page callback' => 'drupal_goto', 'page arguments' => array('api/' . $default_branch->project . '/constants'), 'access callback' => TRUE, ); $items['api/globals'] = array( 'page callback' => 'drupal_goto', 'page arguments' => array('api/' . $default_branch->project . '/globals'), 'access callback' => TRUE, ); $items['api/topics'] = array( 'page callback' => 'drupal_goto', 'page arguments' => array('api/' . $default_branch->project . '/topics'), 'access callback' => TRUE, ); $items['api'] = array( 'page callback' => 'drupal_goto', 'page arguments' => array('api/' . $default_branch->project), 'access callback' => TRUE, ); // Branch listings $items['api/functions/%api_legacy_1_1_listing'] = array( 'load arguments' => array('functions'), 'page callback' => 'drupal_goto', 'page arguments' => array(2), 'access callback' => TRUE, ); $items['api/files/%api_legacy_1_1_listing'] = array( 'load arguments' => array('files'), 'page callback' => 'drupal_goto', 'page arguments' => array(2), 'access callback' => TRUE, ); $items['api/constants/%api_legacy_1_1_listing'] = array( 'load arguments' => array('constants'), 'page callback' => 'drupal_goto', 'page arguments' => array(2), 'access callback' => TRUE, ); $items['api/globals/%api_legacy_1_1_listing'] = array( 'load arguments' => array('globals'), 'page callback' => 'drupal_goto', 'page arguments' => array(2), 'access callback' => TRUE, ); $items['api/topics/%api_legacy_1_1_listing'] = array( 'load arguments' => array('topics'), 'page callback' => 'drupal_goto', 'page arguments' => array(2), 'access callback' => TRUE, ); $items['api/%api_legacy_1_1_listing'] = array( 'page callback' => 'drupal_goto', 'page arguments' => array(1), 'access callback' => TRUE, ); return $items; } /** * Find object for old URLs and return link. */ function api_legacy_1_1_object_load($object_name, $object_type, $branch_name) { return api_url(api_object_load($object_name, api_get_branch_by_name($branch_name), $object_type)); } function api_legacy_1_1_listing_load($branch_name, $type = NULL) { $branch = api_get_branch_by_name($branch_name); if (isset($type)) { return 'api/' . $branch->project . '/' . $type . '/' . $branch->branch_name; } else { return 'api/' . $branch->project . '/' . $branch->branch_name; } } /** * Useful for old URLs which do not include project. */ function api_get_branch_by_name($branch_name) { $branches = api_get_branches(); if (empty($branch_name)) { return $branches[variable_get('api_default_branch', NULL)]; } else { $default_project = $branches[variable_get('api_default_branch', NULL)]->project; foreach ($branches as $branch) { if ($branch->project === $default_project && $branch->branch_name === $branch_name) { return $branch; } } } } /** * Menu object load callback for %api_filename in menu paths. * * Loads information about an API file. */ function api_filename_load($file_name, $branch) { return api_object_load(str_replace('--', '/', $file_name), $branch, 'file'); } /** * Menu object load callback for %api_object in menu paths. * * Loads an object from its name, type, and branch. * * @param $object_name_or_did * The string object name or int did to load. * @param $branch * Branch object. * @param $object_type * A string type, or array of strings. class, interface, function, etc. */ function api_object_load($object_name_or_did, $branch, $object_type, $file_name = NULL) { static $cache; if (!is_array($object_type)) { $object_type = array($object_type); } $key = $object_name_or_did .':'. implode('-', $object_type) .':'. $branch->branch_id; if (!isset($cache[$key])) { // Prepare the query $tables = array('{api_documentation} ad', 'LEFT JOIN {api_overrides} ao ON ao.did = ad.did'); $fields = array('ad.did', 'ad.branch_id', 'ad.object_name', 'ad.object_type', 'ad.title', 'ad.file_name', 'ad.summary', 'ad.documentation', 'ad.code', 'ad.start_line', 'ad.see', 'ad.class_did', 'ad.var', 'ad.throws', 'ao.overrides_did', 'ao.root_did', 'ad.member_name', 'ao.documented_did'); $where = "WHERE ad.object_type IN (" . db_placeholders($object_type, 'text') . ") AND ad.branch_id = %d"; $arguments = $object_type; $arguments[] = $branch->branch_id; if (is_int($object_name_or_did)) { $where .= " AND ad.did = %d"; $arguments[] = $object_name_or_did; } else { $where .= " AND ad.object_name = '%s'"; $arguments[] = $object_name_or_did; } if (!is_null($file_name)) { $file = api_filename_load($file_name, $branch); $where .= " AND ad.file_name = '%s'"; $arguments[] = $file->file_name; } if (in_array('function', $object_type)) { $tables[] = 'LEFT JOIN {api_function} af ON af.did = ad.did'; $fields[] = 'af.*'; } elseif (in_array('file', $object_type)) { $tables[] = 'LEFT JOIN {api_file} af ON af.did = ad.did'; $fields[] = 'af.*'; } // Now build it $cache[$key] = db_fetch_object(db_query_range('SELECT '. implode(', ', $fields) .' FROM '. implode(' ', $tables) .' '. $where, $arguments, 0, 1)); // Grab documentation from documented parent. if (!empty($cache[$key]->documented_did) && $cache[$key]->documented_did !== $cache[$key]->did) { $documented_object = api_object_load((int) $cache[$key]->documented_did, $branch, $object_type); foreach (array('documentation', 'parameters', 'return_value', 'see', 'throws', 'var') as $member) { $cache[$key]->$member = $documented_object->$member; } } } return $cache[$key]; } /** * Implementation of hook_perm(). */ function api_perm() { return array('access API reference', 'administer API reference'); } /** * Implementation of hook_theme(). */ function api_theme() { return array( 'api_branch_table' => array( 'arguments' => array('element' => NULL), ), 'api_expandable' => array( 'arguments' => array( 'prompt' => NULL, 'content' => NULL, 'class' => NULL, ), 'template' => 'templates/api-expandable', ), 'api_defined' => array( 'arguments' => array( 'branch' => NULL, 'object' => NULL, ), 'template' => 'templates/api-defined', ), 'api_related_topics' => array( 'arguments' => array( 'topics' => array(), ), 'template' => 'templates/api-related-topics', ), 'api_functions' => array( 'arguments' => array( 'functions' => array(), ), 'template' => 'templates/api-functions', ), 'api_function_page' => array( 'arguments' => array( 'branch' => NULL, 'function' => NULL, 'signatures' => NULL, 'documentation' => NULL, 'parameters' => NULL, 'return' => NULL, 'related_topics' => NULL, 'call' => NULL, 'code' => NULL, 'see' => NULL, 'throws' => NULL, ), 'template' => 'templates/api-function-page', ), 'api_constant_page' => array( 'arguments' => array( 'branch' => NULL, 'constant' => NULL, 'documentation' => NULL, 'code' => NULL, 'related_topics' => NULL, 'see' => NULL, ), 'template' => 'templates/api-constant-page', ), 'api_global_page' => array( 'arguments' => array( 'branch' => NULL, 'global' => NULL, 'documentation' => NULL, 'code' => NULL, 'related_topics' => NULL, 'see' => NULL, ), 'template' => 'templates/api-global-page', ), 'api_property_page' => array( 'arguments' => array( 'branch' => NULL, 'property' => NULL, 'documentation' => NULL, 'code' => NULL, 'related_topics' => NULL, 'see' => NULL, 'var' => NULL, ), 'template' => 'templates/api-property-page', ), 'api_class_page' => array( 'arguments' => array( 'branch' => NULL, 'class' => NULL, 'documentation' => NULL, 'implements' => NULL, 'hierarchy' => NULL, 'objects' => NULL, 'code' => NULL, 'related_topics' => NULL, 'see' => NULL, ), 'template' => 'templates/api-class-page', ), 'api_file_page' => array( 'arguments' => array( 'file' => NULL, 'documentation' => NULL, 'objects' => NULL, 'see' => NULL, 'related_topics' => NULL, ), 'template' => 'templates/api-file-page' ), 'api_group_page' => array( 'arguments' => array( 'branch' => NULL, 'group' => NULL, 'documentation' => NULL, 'objects' => NULL, 'see' => NULL, ), 'template' => 'templates/api-group-page' ), 'api_branch_default_page' => array( 'arguments' => array( 'branch' => NULL, ), 'template' => 'templates/api-branch-default-page' ), ); } /** * Theme preprocess function for api-function-page.tpl.php. */ function api_preprocess_api_function_page(&$variables) { $variables['defined'] = theme('api_defined', $variables['branch'], $variables['function']); } /** * Theme preprocess function for api-constant-page.tpl.php. */ function api_preprocess_api_constant_page(&$variables) { $variables['defined'] = theme('api_defined', $variables['branch'], $variables['constant']); } /** * Theme preprocess function for api-global-page.tpl.php. */ function api_preprocess_api_global_page(&$variables) { $variables['defined'] = theme('api_defined', $variables['branch'], $variables['global']); } /** * Theme preprocess function for api-global-page.tpl.php. */ function api_preprocess_api_class_page(&$variables) { $variables['defined'] = theme('api_defined', $variables['branch'], $variables['class']); } /** * Theme preprocess function for api-group-page.tpl.php. */ function api_preprocess_api_group_page(&$variables) { $variables['defined'] = theme('api_defined', $variables['branch'], $variables['group']); } /** * Theme preprocess function for api-property-page.tpl.php. */ function api_preprocess_api_property_page(&$variables) { $variables['defined'] = theme('api_defined', $variables['branch'], $variables['property']); } /** * Implementation of hook_init(). * * Adds CSS and JavaScript for the search auto-complete. Adds OpenSearch * autodiscovery links. Redirects nodes of type 'api' to the correct URL. */ function api_init() { drupal_add_css(drupal_get_path('module', 'api') . '/jquery-autocomplete/jquery.autocomplete.css'); drupal_add_js(drupal_get_path('module', 'api') . '/jquery-autocomplete/jquery.autocomplete.js'); drupal_add_css(drupal_get_path('module', 'api') . '/api.css'); drupal_add_js(drupal_get_path('module', 'api') . '/api.js'); $branch = api_get_active_branch(); drupal_add_js(array('apiAutoCompletePath' => base_path() . variable_get('api_autocomplete_path_' . $branch->branch_name, 'api/autocomplete/' . $branch->branch_name)), 'setting'); // Add OpenSearch autodiscovery links. foreach (api_get_branch_names() as $branch_name) { $title = t('Drupal API @branch', array('@branch' => $branch_name)); $url = url('api/opensearch/'. $branch_name, array('absolute' => TRUE)); drupal_set_html_head(''); } // If we happen to be on an API node page, redirect. if (($node = menu_get_object()) && $node->type == 'api') { $documentation = db_fetch_object(db_query('SELECT branch_id, object_type, file_name, object_name FROM {api_documentation} WHERE did = %d', $node->nid)); drupal_goto(api_url($documentation)); } } /** * Implementation of hook_db_rewrite_sql(). * * Excludes nodes of type 'api' from node queries. */ function api_db_rewrite_sql($query, $primary_table, $primary_field) { if ($primary_field == 'nid' && $primary_table == 'n') { return array('where' => "n.type <> 'api'"); } } /** * Implementation of hook_block(). */ function api_block($op, $delta = NULL, $edit = array()) { switch ($op) { case 'list': return array( 'api-search' => array( 'info' => t('API search'), 'cache' => BLOCK_CACHE_PER_PAGE, ), 'navigation' => array( 'info' => t('API navigation'), 'cache' => BLOCK_CACHE_PER_PAGE, ), ); case 'view': $branch = api_get_active_branch(); switch ($delta) { case 'api-search': if (user_access('access API reference') && !empty($branch)) { return array( 'subject' => t('Search @branch', array('@branch' => $branch->branch_name)), 'content' => drupal_get_form('api_search_form', $branch), ); } return; case 'navigation': 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); return array( 'subject' => t('API Navigation'), 'content' => theme('item_list', $links), ); } return; } } } /** * Implementation of hook_filter(). */ function api_filter($op, $delta = 0, $format = -1, $text = '') { switch ($op) { case 'list': return array(0 => t('API filter')); case 'description': return t('Add links to API objects, like theme() or theme.inc.'); case 'process': return api_filter_documentation($text, api_get_active_branch()); default: return $text; } } /** * Constructs a link to an API object page. * * @param $object * An API object with object_type, object_name, and file_name properties. * @param $file * TRUE links to the object’s containing file, FALSE links to the object * itself. * * @return * A URL string. */ function api_url($object, $file = FALSE) { $branches = api_get_branches(); if ($file) { return 'api/' . $branches[$object->branch_id]->project . '/' . str_replace('/', '--', $object->file_name) . '/' . $branches[$object->branch_id]->branch_name; } elseif ($object->object_type === 'file') { return 'api/' . $branches[$object->branch_id]->project . '/' . str_replace('/', '--', $object->object_name) . '/' . $branches[$object->branch_id]->branch_name; } else { return 'api/' . $branches[$object->branch_id]->project . '/' . str_replace('/', '--', $object->file_name) . '/' . $object->object_type . '/' . $object->object_name . '/' . $branches[$object->branch_id]->branch_name; } } /** * Shows a link for theme('api_expandable'). */ function api_show_l($text) { return l($text, $_REQUEST['q'], array('attributes' => array('class' => 'show-content'))); } /** * Hides a link for theme('api_expandable'). */ function api_hide_l($text) { return l($text, $_REQUEST['q'], array('attributes' => array('class' => 'hide-content'))); } /** * Saves an API branch. * * @param $branch * A branch object, with branch_name, title, and directories variables. * @param $old_branch_name * To replace a branch, provide the old branch name. */ function api_save_branch($branch) { $branch->data = serialize($branch->data); if (empty($branch->branch_id)) { drupal_write_record('api_branch', $branch); if (is_null(variable_get('api_default_branch', NULL))) { variable_set('api_default_branch', $branch->branch_id); } } else { drupal_write_record('api_branch', $branch, 'branch_id'); } // Reweight all branches. api_get_branch_names(TRUE); $branches = api_get_branches(TRUE); usort($branches, 'api_branch_sort'); $weight = 0; foreach ($branches as $branch) { $branch->weight = $weight; $weight += 1; drupal_write_record('api_branch', $branch, 'branch_id'); } menu_rebuild(); } /** * Sort callback for sorting branches. */ function api_branch_sort($a, $b) { return version_compare($a->branch_name, $b->branch_name); } /** * Returns the currently active branch. */ function api_get_active_branch() { static $branch; if (!isset($branch)) { $item = menu_get_item(); $branches = api_get_branches(); $branch_names = api_get_branch_names(); $default_branch = variable_get('api_default_branch', NULL); if (isset($item['page_arguments'][0]->branch_name)) { $branch = $item['page_arguments'][0]; } elseif (strpos($item['path'], 'api/search') === 0 && isset($branch_names[$item['page_arguments'][0]])) { // Search page, use the default project if possible foreach ($branches as $possible_branch) { if ($possible_branch->project === $branches[$default_branch]->project && $possible_branch->branch_name === $item['page_arguments'][0]) { $branch = $possible_branch; } } } if (!isset($branch)) { if (!is_null($default_branch)) { $branch = $branches[$default_branch]; } else { $branch = NULL; } } } return $branch; } /** * Form builder for API search form. * * @param $branch * Branch to build the search form for. * * @see api_search_form_submit() */ function api_search_form($form_state, $branch) { $form = array( '#token' => FALSE, ); $form['#branch'] = $branch; $form['search'] = array( '#title' => t('Function, file, or topic'), '#type' => 'textfield', '#default_value' => '', '#required' => TRUE, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Search'), ); return $form; } /** * Form submission handler for api_search_form(). */ function api_search_form_submit($form, &$form_state) { $form_state['redirect'] = 'api/search/'. $form['#branch']->branch_name .'/'. $form_state['values']['search']; } /** * Page callback for path 'apis'. */ function api_search_redirect() { $args = func_get_args(); if (count($args) === 0 && strpos($_GET['q'], 'apis/') !== 0) { // Handling 404. $tail = $_REQUEST['q']; } else { $tail = implode('/', $args); } $branches = api_get_branches(); $_REQUEST['destination'] = 'api/search/'. $branches[variable_get('api_default_branch', NULL)]->branch_name .'/'. $tail; drupal_goto(); } /** * Page callback for path api/search/[branch] -- performs a search. */ function api_search_listing($branch_name) { $search_text = func_get_args(); array_shift($search_text); $search_text = implode('/', $search_text); drupal_set_title(t('Search for %search', array('%search' => $search_text))); $count = db_result(db_query("SELECT count(*) FROM {api_branch} b INNER JOIN {api_documentation} ad ON b.branch_id = ad.branch_id LEFT JOIN {api_overrides} ao ON ao.did = ad.did WHERE b.branch_name = '%s' AND ad.title = '%s' AND (ao.did IS NULL OR ao.root_did = ao.did)", $branch_name, $search_text)); if ($count == 1) { // Exact match. $item = db_fetch_object(db_query("SELECT b.branch_name, b.project, ad.* FROM {api_branch} b INNER JOIN {api_documentation} ad ON b.branch_id = ad.branch_id LEFT JOIN {api_overrides} ao ON ao.did = ad.did WHERE b.branch_name = '%s' AND ad.title = '%s' AND (ao.did IS NULL OR ao.root_did = ao.did)", $branch_name, $search_text)); $branches = api_get_branches(); drupal_goto(api_url($item)); } else { // Wildcard search. $result = pager_query("SELECT b.branch_id, b.branch_name, b.project, ad.* FROM {api_branch} b INNER JOIN {api_documentation} ad ON b.branch_id = ad.branch_id LEFT JOIN {api_overrides} ao ON ao.did = ad.did WHERE b.branch_name = '%s' AND ad.title LIKE '%%%s%%' AND (ao.did IS NULL OR ao.root_did = ao.did) ORDER BY ad.object_type, ad.title", 50, 0, NULL, $branch_name, str_replace(array('_', '%'), array('\_', '\%'), $search_text)); return api_render_listing(api_query_list($result), t('No search results found.'), TRUE, TRUE) . theme('pager', NULL, 50, 0); } } /** * Prepares a listing of documentation objects for a branch. * * @param $branch_name * Name of the branch to list. * @param $page * TRUE if this will be embedded in a page, and FALSE if it is an AHAH * callback. * * @return * JavaScript listing of all the objects in the branch. */ function api_autocomplete($branch_name, $page = TRUE) { $result = db_query("SELECT ad.title, ad.object_type FROM {api_documentation} ad INNER JOIN {api_branch} b ON ad.branch_id = b.branch_id LEFT JOIN {api_overrides} ao ON ao.did = ad.did WHERE b.branch_name = '%s' AND ad.object_type <> 'mainpage' AND (ao.did IS NULL OR ao.root_did = ao.did) ORDER BY LENGTH(ad.title)", $branch_name); $objects = array(); while ($object = db_fetch_object($result)) { $objects[] = $object->title; } if ($page) { drupal_json($objects); } else { return drupal_to_js($objects); } } /** * Page callback for path api/opensearch/[branch] - OpenSearch plugin. * * @param $branch_name * Name of the branch to search. * * @see https://developer.mozilla.org/en/Creating_OpenSearch_plugins_for_Firefox */ function api_opensearch($branch_name) { if (!db_result(db_query("SELECT 1 FROM {api_branch} WHERE branch_name = '%s'", $branch_name))) { return drupal_not_found(); } drupal_set_header('Content-Type: text/xml; charset=utf-8'); $short_name = t('Drupal API @branch', array('@branch' => $branch_name)); $description = t('Drupal @branch API documentation', array('@branch' => $branch_name)); if ($image = theme_get_setting('favicon')) { // Get rid of base_path that theme_get_setting() added. $image = substr($image, strlen(base_path())); } else { // Fall back on default favicon if the theme didn't provide one. $image = 'misc/favicon.ico'; } $image = url($image, array('absolute' => TRUE)); $search_url = url('api/search/'. $branch_name, array('absolute' => TRUE)) .'/{searchTerms}'; $suggest_url = url('api/suggest/'. $branch_name, array('absolute' => TRUE)) .'/{searchTerms}'; $search_form_url = url('api', array('absolute' => TRUE)); $self_url = url($_GET['q'], array('absolute' => TRUE)); print <<' . t('Other projects:') . ' ' . theme('links', $links) . '
'; } } /** * Renders an overview of documentation objects in a table. * * @param $list * Members list : array(type => array(title => object)). * @param $empty_message * An optional string to display instead of an empty table. * @param $show_headings * If you expect only one object type, you might not want the provided * heading. * @param $link_file * Boolean : toggles the display of the file link column. * * @return * Rendered HTML for the listing. */ function api_render_listing($list, $empty_message = NULL, $show_headings = TRUE, $link_file = FALSE) { if (count($list) === 0) { return is_null($empty_message) ? '' : ''. $empty_message .'
'; } $branches = api_get_branches(); $header = array(t('Name')); if ($link_file) { $header[] = t('Location'); } $header[] = t('Description'); $tables = array(); foreach ($list as $type => $objects) { $rows = array(); foreach ($objects as $object) { $row = array(l($object->title, api_url($object))); if ($link_file) { $row[] = '' . api_file_link($object) . ''; } $summary = api_link_documentation($object->summary, $branches[$object->branch_id]); if (!empty($object->overrides_did)) { $overrides = api_object_load((int) $object->overrides_did, $branches[$object->branch_id], array('function', 'property', 'constant')); $summary .= ' ' . t('Overrides !link', array('!link' => l($overrides->title, api_url($overrides)))) . ''; } $row[] = $summary; $rows[] = $row; } $tables[$type] = theme('table', $header, $rows); } $headings = array( 'function' => t('Functions & methods'), 'property' => t('Properties'), 'group' => t('Groups'), 'global' => t('Globals'), 'constant' => t('Constants'), 'file' => t('Files'), 'interface' => t('Interfaces'), 'class' => t('Classes'), ); $output = ''; foreach ($tables as $key => $table) { if ($show_headings) { $output .= '