'admin/content/taxonomy', 'admin_main'=>'admin/content/vocabindex', 'admin_paths'=>'admin/content/vocabindex/paths', 'admin_refresh'=>'admin/content/vocabindex/refresh', 'admin_settings'=>'admin/content/vocabindex/settings', ); return $paths[$path]; } function vocabindex_perm() { return array('manage vocabulary index pages', 'view vocabulary index pages'); } function vocabindex_menu($may_cache) { if($may_cache) { $items[]=array( 'path'=>_vocabindex_menu_paths('admin_main'), 'title'=>'Vocabulary index pages', 'description'=>'Create index pages for vocabularies.', 'access'=>user_access('manage vocabulary index pages'), 'callback'=>'vocabindex_page_admin_paths', ); $items[]=array( 'path'=>_vocabindex_menu_paths('admin_paths'), 'title'=>'Paths', 'type'=>MENU_DEFAULT_LOCAL_TASK, ); $items[]=array( 'path'=>_vocabindex_menu_paths('admin_settings'), 'title'=>'Settings', 'description'=>'General settings.', 'callback'=>'drupal_get_form', 'callback arguments'=>array('vocabindex_admin'), 'type'=>MENU_LOCAL_TASK, ); //Prevent the DB request if the module doesn't exist anymore. Without this prevention this code would cause an error right after uninstalling the module. if(module_exists('vocabindex')) { $result=db_query("SELECT vi.path, v.name, v.description FROM {vocabindex} vi LEFT JOIN {vocabulary} v ON vi.vid=v.vid"); while($row=db_fetch_object($result)) { //Menu callbacks for every vocabindex page $items[]=array( 'path'=>$row->path, 'title'=>$row->name, 'description'=>$row->description, 'access'=>user_access('view vocabulary index pages'), 'callback'=>'vocabindex_view_page', 'callback arguments'=>array($row->path), 'type'=>MENU_SUGGESTED_ITEM, ); } } } return $items; } /** *Admin general settings page */ function vocabindex_admin() { $form['vocabindex_terms_order']=array( '#type'=>'select', '#title'=>t('Terms order'), '#description'=>t('Select the order in which the terms should be displayed.'), '#default_value'=>variable_get('vocabindex_terms_order', 'weight'), '#options'=>array( 'weight'=>t('By weight'), 'name'=>t('By name'), ), ); $form['vocabindex_list_style']=array( '#type'=>'select', '#title'=>t('List style'), '#description'=>t('Select the display style for the terms lists.'), '#default_value'=>variable_get('vocabindex_list_style', 'threaded'), '#options'=>array( 'threaded'=>t('Threaded'), 'flat'=>t('Flat'), 'flat-toplevel'=>t('Flat (top-level terms only)'), ), ); $form['vocabindex_stylesheet']=array( '#type'=>'checkbox', '#title'=>t('Use default stylesheet'), '#description'=>t('Uncheck if you don\'t want to use the default Vocabindex stylesheet.'), '#default_value'=>variable_get('vocabindex_stylesheet', TRUE), ); return system_settings_form($form); } /** *Admin paths page */ function vocabindex_page_admin_paths() { $count=db_result(db_query("SELECT COUNT(*) FROM {vocabulary}")); if($count!=0) { $output=t('Paths may only contain alphanumeric characters and dashes. For best SEO results, use dashes as word separators. Paths will automatically be converted to lowercase.'); $output.=drupal_get_form('vocabindex_form_admin_paths'); } else { $output=t('There are no vocabularies to create index pages for. You can create vocabularies at the Categories page.', array('!link'=>url(_vocabindex_menu_paths('taxonomy')))); } return $output; } function vocabindex_form_admin_paths() { $result=db_query("SELECT vi.path, v.name, v.vid FROM {vocabulary} v LEFT JOIN {vocabindex} vi ON v.vid=vi.vid ORDER BY v.name ASC"); while($row=db_fetch_object($result)) { if($row->path) { $description=t('Currently located at /!relative_url', array('!url'=>url($row->path), '!relative_url'=>$row->path)); } else { $description=t('There is currently no index page set for this vocabulary.'); } $form['vocabindex_path_'.$row->vid]=array( '#type'=>'textfield', '#title'=>t('Path for %title index page', array('%title'=>$row->name)), '#default_value'=>$row->path, '#maxlength'=>'128', '#description'=>$description, ); } $form['submit']=array( '#type'=>'submit', '#value'=>t('Save') ); return $form; } function vocabindex_form_admin_paths_validate($form_id, $form_values) { foreach($form_values as $element=>$path) { if(strpos($element, 'vocabindex_path_')!==FALSE) { $vid=str_replace('vocabindex_path_', '', $element); if(!empty($path) && !preg_match('#[a-z0-9-]#i', $path)) { form_set_error($element, t('Paths may only contain alphanumeric characters and dashes.')); } else { $result=db_result(db_query("SELECT vid FROM {vocabindex} WHERE path = '%s'", $path)); $message=vocabindex_check_index($result->vid, $path); if($message=='used') { form_set_error($element, t('Path already exists.')); } } } } } function vocabindex_form_admin_paths_submit($form_id, $form_values) { foreach($form_values as $element=>$path) { if(strpos($element, 'vocabindex_path_')!==FALSE) { $vid=str_replace('vocabindex_path_', '', $element); $old_path=db_result(db_query("SELECT path FROM {vocabindex} WHERE vid = %d", $vid)); vocabindex_create_index($vid, $path); //Present the user with a confirmation message drupal_set_message(t('The paths have been updated.')); } } } /** *Check if the given path is already being used. Returns 'vocab' if the path for an index hasn't changed, 'unused' if it isn't used or 'used' when it is in use. */ function vocabindex_check_index($vid, $path) { //Check if path is already used for this vocabulary. If false, check if it's already being used by other items or nodes. If true, do nothing. $count=db_result(db_query("SELECT COUNT(*) FROM {vocabindex} WHERE vid = %d AND path = '%s'", $vid, $path)); if($count==0) { //Check for existing aliases and menu paths $item=menu_get_item(NULL, $path); if(drupal_lookup_path('source', $path)==TRUE || count($item)>0) { $message='used'; } else { $message='unused'; } } else { $message='vocab'; } return $message; } /** *Creates or deletes the paths for the index pages */ function vocabindex_create_index($vid, $path) { //Delete the old vocabindex path vocabindex_delete_index(NULL, $vid); if($vid && $path) { //Create the new path $vocab=taxonomy_get_vocabulary($vid); $description=$vocab->description; $title=$vocab->name; $path=strtolower($path); db_query("INSERT INTO {vocabindex} (vid, path) VALUES (%d, '%s')", $vid, $path); //Rebuild the menu menu_rebuild(); } } /** *Deletes index pages. */ function vocabindex_delete_index($path=NULL, $vid=NULL) { if(!$path && $vid) { db_query("DELETE FROM {vocabindex} WHERE vid = %d", $vid); } else { db_query("DELETE FROM {vocabindex} WHERE path = '%s'", $path); } } /** *Views a vocabulary index page */ function vocabindex_view_page($path) { $vid=db_result(db_query("SELECT vid FROM {vocabindex} WHERE path='%s'", $path)); $list_style=variable_get('vocabindex_list_style', 'threaded'); $sort_by=variable_get('vocabindex_terms_order', 'weight'); $list_tmp=taxonomy_get_tree($vid); //Don't try to render the list if there are no terms to display if(count($list_tmp)!=0) { //The eventual list. The different types of keys are used for sorting $list=array(); if($sort_by=='weight') { foreach($list_tmp as $term) { //Sort by weight. Use the name as well to prevent duplicate keys and to sort by name after the terms have been sorted by weight $list[$term->weight.$term->name]=$term; } } else if($sort_by=='name') { foreach($list_tmp as $term) { //Sort by name $list[$term->name]=$term; } } //Check list-style and call the right function if($list_style!='threaded') { $output=vocabindex_render_list_flat($list, $sort_by, $list_style); } else { $output=vocabindex_render_list_threaded($list, $sort_by); } //And render the top list and page $output=theme('vocabindex_list', $output, $list_style); $vocab=taxonomy_get_vocabulary($vid); $output=theme('vocabindex_page', $vocab->description, $output); } else { $output=t('There are no categories to display.'); } //Only add the stylesheet if the site administrator wants it if(variable_get('vocabindex_stylesheet', TRUE)) { drupal_add_css('modules/vocabindex/vocabindex-style.css', 'module', 'screen', FALSE); } return $output; } /** *Function to render a flat list */ function vocabindex_render_list_flat($list, $sort_by, $list_style) { ksort($list); if($list_style=='flat-toplevel') { //Filter $list so only terms with $term->parents[0]==0 will be kept $list_tmp=array(); foreach($list as $key => $term) { if($term->parents[0]==0) { $list_tmp[$key]=$term; } } $list=$list_tmp; } //Loop through all the terms from the list and render them $i=1; foreach($list as $term) { $zebra=($i%2==0?'even':'odd'); $url=url('taxonomy/term/'.$term->tid); $output.=theme('vocabindex_list_item', $url, $term->name, $term->description, $zebra); $i++; } return $output; } /** *Function to render a threaded list */ function vocabindex_render_list_threaded($list, $sort_by) { //A list of terms that are parents $parents=array(); //A list of the depths all terms are at. $depths=array(); //Maximum depth. $max_depth=0; //Set up all the arrays necessary for rendering the eventual list foreach($list as $term) { foreach($term->parents as $parent) { //Build up a list of terms that are parents $parents[$parent][]=$term->tid; //Set up an array with all the depths so we can render the list from the inside out later on $depths[$term->depth][]=$term; if($term->depth>$max_depth) { //And to know where to start rendering we need to know the maximum depth $max_depth=$term->depth; } } } //Render the list from the inside out $rendered_terms=array(); for($i=$max_depth; $i>=0; $i--) { //Loop through all the terms at this depth foreach($depths[$i] as $term) { //Check if term is a parent if($parents[$term->tid]) { //The term is a parent. Because we render inside out all the children have been rendered already and can simply be put together foreach($parents[$term->tid] as $child) { $children.=$rendered_terms[$child]; } $children=theme('vocabindex_list', $children); } //Render term $url=url('taxonomy/term/'.$term->tid); $rendered_terms[$term->tid]=theme('vocabindex_list_item', $url, $term->name, $term->description, NULL, $children); //And clear all children $children=NULL; } } //Put the top-level terms together foreach($depths[0] as $term) { $output.=$rendered_terms[$term->tid]; } return $output; } function theme_vocabindex_page($description, $list) { if($description) { $output='

'.$description.'

'; } $output.=$list; return $output; } function theme_vocabindex_list($list_items, $list_style='threaded') { return ""; } function theme_vocabindex_list_item($url, $name, $description, $zebra, $children=NULL) { if($description) { $description=''.$description.''; } return '
  • '.$name.$description.''.$children."
  • \n"; }