'update.php')), 'error'); } } /** * Implementation of hook_menu(). */ function vocabindex_menu() { if (vocabindex_update_status() == FALSE) { return; } $items[_vocabindex_menu_paths('admin_main')] = array( 'title' => 'Vocabulary Index', 'description' => 'Create index pages for vocabularies.', 'access arguments' => array('administer vocabulary index'), 'page callback' => 'vocabindex_admin_vi', 'page arguments' => array((string) VOCABINDEX_VI_PAGE), 'file' => 'vocabindex.admin.inc', ); $items[_vocabindex_menu_paths('admin_pages')] = array( 'title' => 'Pages', 'access arguments' => array('administer vocabulary index'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'file' => 'vocabindex.admin.inc', 'weight' => 0, ); $items[_vocabindex_menu_paths('admin_blocks')] = array( 'title' => 'Blocks', 'description' => 'Create index blocks for vocabularies.', 'access arguments' => array('administer vocabulary index'), 'page callback' => 'vocabindex_admin_vi', 'page arguments' => array((string) VOCABINDEX_VI_BLOCK), 'type' => MENU_LOCAL_TASK, 'file' => 'vocabindex.admin.inc', 'weight' => 1, ); $items[_vocabindex_menu_paths('admin_settings')] = array( 'title' => 'Settings', 'description' => 'General settings.', 'access arguments' => array('administer vocabulary index'), 'page callback' => 'drupal_get_form', 'page arguments' => array('vocabindex_admin'), 'type' => MENU_LOCAL_TASK, 'file' => 'vocabindex.admin.inc', 'weight' => 2, ); $vis = vocabindex_vi_load(VOCABINDEX_VI_PAGE); foreach ($vis as $vi) { $item = vocabindex_menu_base() + array( 'title' => $vi->name, 'description' => check_plain($vi->description), // We need to cast out the constant to a string, because the integer will // otherwise be replaced with the corresponding part of the menu path. 'page arguments' => array($vi->vid, (string) VOCABINDEX_VOC), ); $items[$vi->path] = $item; // The menu item for alphabetical VIs using the letter wildcard. if ($vi->view == VOCABINDEX_VIEW_ALPHABETICAL) { $arg = (int) substr_count($vi->path, '/') + 1; $path = $vi->path . '/%vocabindex_letter'; $items[$path] = $item; $items[$path]['page arguments'][2] = $arg; } } return $items; } /** * Implementation of hook_menu_alter(). * * Used for creating the term index callbacks. */ function vocabindex_menu_alter(&$items) { if (vocabindex_update_status() == FALSE) { return; } $vis = vocabindex_vi_load(VOCABINDEX_VI_PAGE); foreach ($vis as $vi) { // Check if this VI is a flat, browseable index. if ($vi->view == VOCABINDEX_VIEW_FLAT) { $tree = taxonomy_get_tree($vi->vid); $children = vocabindex_get_children($tree); for ($i = 0, $len_i = count($tree); $i < $len_i; $i++) { $term = $tree[$i]; // Check if the current term is a parent. if (isset($children[$term->tid])) { $items['taxonomy/term/' . $term->tid] = vocabindex_menu_base() + array( 'title' => $term->name, 'description' => check_plain($term->description), // We need to cast our integer constant to a string, because the // integer will otherwise be replaced with the corresponding part // of the menu path. 'page arguments' => array($term->tid, (string) VOCABINDEX_TERM), 'file path' => drupal_get_path('module', 'vocabindex'), ); } } } } } /** * Implementation of hook_block(). */ function vocabindex_block($op, $delta = 0) { if ($op == 'list') { $blocks = array(); $vis = vocabindex_vi_load(VOCABINDEX_VI_BLOCK); foreach ($vis as $vi) { $blocks[$vi->vid] = array('info' => t('@vocabulary (Vocabulary Index)', array('@vocabulary' => $vi->name))); } return $blocks; } elseif ($op == 'view') { module_load_include('inc', 'vocabindex', 'vocabindex.view'); $vi = vocabindex_vi_load(VOCABINDEX_VI_BLOCK, $delta); return array('subject' => check_plain($vi->name), 'content' => vocabindex_view_block($vi->vid)); } } /** * Implementation of hook_theme(). */ function vocabindex_theme($existing, $type, $theme, $path) { $base = array('path' => drupal_get_path('module', 'vocabindex') . '/theme'); $functions['vocabindex_admin_vi_form'] = $base + array( 'template' => 'vocabindex_admin_vi_form', 'arguments' => array( 'form' => array(), ), ); $functions['vocabindex_page'] = $base + array( 'template' => 'vocabindex_page', 'arguments' => array( 'description' => NULL, 'list' => NULL, 'pager' => NULL, 'pager_alpha' => NULL, ), ); $functions['vocabindex_list'] = $base + array( 'template' => 'vocabindex_list', 'arguments' => array( 'list_items' => NULL, 'view' => NULL, ), ); $functions['vocabindex_list_item'] = $base + array( 'template' => 'vocabindex_list_item', 'preprocess functions' => array( 'vocabindex_preprocess_list_item' ), 'arguments' => array( 'url' => NULL, 'name' => NULL, 'description' => NULL, 'zebra' => NULL, 'parent' => FALSE, 'children' => NULL, 'node_count' => 0, 'position' => NULL, ), ); $functions['vocabindex_pager'] = $base + array( 'template' => 'vocabindex_pager_alphabetical', 'arguments' => array( 'letters' => NULL, ), ); $functions['vocabindex_pager_letter'] = $base + array( 'template' => 'vocabindex_pager_letter', 'arguments' => array( 'letter' => NULL, 'url' => NULL, 'active' => NULL, ), ); $functions['vocabindex_pager_separator'] = $base + array( 'template' => 'vocabindex_pager_separator', ); return $functions; } /** * Implementation of hook_taxonomy(). */ function vocabindex_taxonomy($op, $type, $array) { $vid = $array['vid']; if ($type == 'vocabulary') { if ($op == 'delete') { vocabindex_vi_delete(VOCABINDEX_VI_VID, $vid); } elseif ($op == 'insert') { vocabindex_vi_create($vid); } menu_rebuild(); } } /** * Implementation of hook_help(). */ function vocabindex_help($path, $arg) { switch ($path) { // General help. case 'admin/help#vocabindex': // Module description. $output = '
' . t('Vocabulary Index provides several ways to list all terms inside a specified vocabulary. For each vocabulary you may define a page and/or a block that will display its terms in the way you think suits best. This way may be a tree, a flat, browsable index, or an index where you can filter terms by letter. Next to that you can choose to display node counts for each term. Term descriptions will be displayed along with the term names if provided.', array('!url_pages' => url(_vocabindex_menu_paths('admin_pages')), '!url_blocks' => url(_vocabindex_menu_paths('admin_blocks')))) . '
'; // Dynamic Help. // Check permissions. $count = db_result(db_query("SELECT COUNT(perm) FROM {permission} WHERE perm LIKE '%view vocabulary index%'")); $permissions = (bool) $count; // Check index pages. $index_pages = (bool) count(vocabindex_vi_load(VOCABINDEX_VI_PAGE, 0, TRUE)); // Check index blocks. $index_blocks = (bool) count(vocabindex_vi_load(VOCABINDEX_VI_BLOCK, 0, TRUE)); $output .= dynamic_help(array( array( 'message' => t('Set up permissions.', array('!permissions' => url('admin/user/permissions', array('fragment' => 'module-vocabindex')))), 'executed' => $permissions, ), array( 'message' => t('Create index pages.', array('!index_pages' => url(_vocabindex_menu_paths('admin_pages')))), 'executed' => $index_pages, ), array( 'message' => t('Create index blocks.', array('!index_blocks' => url(_vocabindex_menu_paths('admin_blocks')))), 'executed' => $index_blocks, ), ), 'Vocabulary Index'); // Reference to handbook page. $output .= '' . t('For more extensive information on configuring Vocabulary Index or on development, please visit the handbooks.', array('!handbook_page' => 'http://drupal.org/node/297075')) . '
'; // Character transliteration. $output .= '' . t('Character transliteration groups terms by the modern Latin equivalent of their first letter for alphabetical views. This way terms like Ångström and Ampère will both be grouped under the letter A, for instance. If you would like to modify the default transliteration file, copy the file to i18n-ascii-custom.txt and use this file instead to override the values from i18n-ascii.txt or to add new values.', array('!default_file' => $path . '/i18n-ascii.txt')) . '
'; // Pathauto. $output .= '' . t('Pathauto and Vocabulary Index can work together to provide good-looking paths when browsing taxonomy. Let Pathauto create term aliases that neatly fit the paths of your index pages.', array('!pathauto_project_page' => 'http://drupal.org/project/pathauto')) . '
'; break; // Per-page help. case _vocabindex_menu_paths('admin_main'): case _vocabindex_menu_paths('admin_pages'): $output = '' . t('Index pages are pages dedicated to list the terms of a vocabulary. They are accessible by a path of their own, and they are enabled as soon as you enter one.') . '
'; break; case _vocabindex_menu_paths('admin_blocks'): $output = '' . t('Enabled blocks can be configured at the blocks settings page. Flat index blocks are similar to browsable pages, they only differ in that they are not browsable and therefore don\'t display nested terms.', array('!blocks' => url(_vocabindex_menu_paths('blocks')))) . '
'; } return $output; } /** * Define all menu paths in one central place. * * Useful because we are referring to menu paths from several places in this * module. If we want to change them we can easily do so here. * * @param $path * Type: string; The name of the site section to get the path for. * * @return * Type: string; The requested path. */ function _vocabindex_menu_paths($section) { static $paths = array( 'taxonomy' => 'admin/content/taxonomy', 'blocks' => 'admin/build/block', 'admin_main' => 'admin/build/vocabindex', 'admin_pages' => 'admin/build/vocabindex/pages', 'admin_blocks' => 'admin/build/vocabindex/blocks', 'admin_settings' => 'admin/build/vocabindex/settings', ); return $paths[$section]; } /** * Returns the basics for a VI menu item. */ function vocabindex_menu_base() { return array( // NOTE: Access arguments should be replaced by Taxonomy Access integration. -Xano. 'access arguments' => array('view vocabulary index'), 'page callback' => 'vocabindex_view_page', 'type' => MENU_CALLBACK, 'file' => 'vocabindex.view.inc', ); } /** * Check if the latest DB updates for this module have been performed. */ function vocabindex_update_status() { if (!is_null(variable_get('vocabindex_schema_update', NULL))) { return variable_get('vocabindex_schema_update', NULL); } elseif (module_exists('vocabindex')) { require_once('./includes/install.inc'); $current = drupal_get_installed_schema_version('vocabindex'); // $available should have the value of the latest DB update available in // this version of Vocabulary Index. Manually declaring this value improves // performance, because vocabindes.install doesn't need to be called at // every menu rebuild. $available = 6200; $uptodate = $current < $available ? FALSE : TRUE; variable_set('vocabindex_schema_update', $uptodate); return $uptodate; } } /** * Template preprocessor for list items. * * We use this blank function to prevent template_preprocess from being * executed and setting the wrong $zebra value. */ function vocabindex_preprocess_list_item(&$variables) { } /** * Menu path wildcard for alphabetical VIs. * * @param $letter * Type: string; The wildcard's value. * * @return * Type: boolean; Whether the wildcard's value is a letter or not. */ function vocabindex_letter_load($letter) { return preg_match('#^[a-z0-9]$#', vocabindex_transliterate($letter)) == 0 ? FALSE : $letter; } /** * Delete index pages. * * @param $vid * Type: integer; The VID of the VIs to delete. 0 (zero) to delete all VIs. */ function vocabindex_vi_delete($vid = 0) { if ($vid == 0) { // Delete all VIs db_query("DELETE FROM {vocabindex}"); } else { // Delete all VIs matching a VID db_query("DELETE FROM {vocabindex} WHERE vid = %d", $vid); } } /** * Create two VIs (Page and Block) for a certain vocabulary. * * This function must be called when the module is being installed or when a new * vocabulary has been created. Note that created VIs are not yet enabled. * * @param $vid * Type: integer; The VID of the vocabulary to create the VIs for. */ function vocabindex_vi_create($vid) { db_query("INSERT INTO {vocabindex} (vid, type) VALUES (%d, %d), (%d, %d)", $vid, VOCABINDEX_VI_PAGE, $vid, VOCABINDEX_VI_BLOCK); } /** * Load the vocabindex settings for a given index. * * The first time this function is called it will get vocabularies and matching * VI data from the database and put them in a static array. * * @param $type * Type: constant; The result you want to get. Possible values: * - VOCABINDEX_VI_PAGE (Select all index pages). * - VOCABINDEX_VI_BLOCK (Select all index blocks). * @param $vid * Type: integer; The VID of the vocabulary index to get the settings for. * 0 (zero) to select all VIs. * @param $enabled * Type: boolean; Whether to return all VIs or just the enabled ones. * * @return * Type: array/object; An array containing all requested VI objects or just * one VI object. */ function vocabindex_vi_load($type, $vid = 0, $enabled = TRUE) { static $vis = array(); if (empty($vis)) { $result = db_query(db_rewrite_sql("SELECT vi.*, v.name, v.description FROM {vocabindex} vi LEFT JOIN {vocabulary} v ON vi.vid = v.vid ORDER BY v.name, v.weight ASC", 'v', 'vid')); while ($vi = db_fetch_object($result)) { // Only when displaying flat VIs the depth should be 2 (one to display, the // other to determine if terms are parents). $vi->depth = $vi->view == VOCABINDEX_VIEW_FLAT ? 2 : NULL; $vis[$vi->type][$vi->vid] = $vi; } } // Check for the requested VIDs. if ($vid != 0) { $selection = array($vis[$type][$vid]); } else { $selection = $vis[$type]; } // Select all VIs or just the enabled ones depending on $enabled. if ($enabled) { $buffer = array(); foreach ($selection as $vi) { if ($vi->enabled) { $buffer[] = $vi; } } $return = $buffer; } else { $return = array_values($selection); } return $vid == 0 ? $return : $return[0]; } /** * Create an array listing all the terms that are parents. * * @param $tree * Type: array; The result of taxonomy_get_tree(). * * @return * Type: array; */ function vocabindex_get_children($tree) { $children = array(); // Loop through all the terms. for ($i = 0, $len_i = count($tree); $i < $len_i; $i++) { $term = $tree[$i]; // Loop through the term's parents. for ($j = 0, $len_j = count($term->parents); $j < $len_j; $j++) { $children[$term->parents[$j]][] = $term->tid; } } // As terms can have multiple parents and we don't want terms to show up in // places where they shouldn't show up we're going to prune the array. foreach ($children as $tid => $child) { $children[$tid] = array_unique($child); } return $children; } /** * Transliterate a letter to the modern Latin alphabet. * * @param $letter * Type: string; The letter to transliterate. * * @return * Type: string; The first letter of the transliterated string if a * translation is available. */ function vocabindex_transliterate($letter) { static $transliterations = array(); if (empty($transliterations)) { $path = drupal_get_path('module', 'vocabindex'); $transliterations = parse_ini_file($path .'/i18n-ascii.txt'); // Check for custom transliteration file if (file_exists($path . '/i18n-ascii-custom.txt')) { $custom_transliterations = parse_ini_file($path .'/i18n-ascii-custom.txt'); $transliterations = array_merge($transliterations, $custom_transliterations); } } // Only return the first letter of the transliterated result. return substr(strtr($letter, $transliterations), 0, 1); }