url('ajax/trees')); drupal_add_js(array('trees' => $settings), "setting"); } } /** * Implementation of hook_views_api() */ function trees_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'trees'), ); } /** * Implementation of hook_theme() */ function trees_theme() { return array( 'trees_admin_table' => array( 'arguments' => array('form' => NULL), ), 'trees_leaves' => array( 'arguments' => array('leaves' => array()), 'file' => 'theme.inc', ), 'trees_parents' => array( 'arguments' => array('parents' => array()), 'file' => 'theme.inc', ), 'trees_leave' => array( 'arguments' => array('leave' => array()), 'file' => 'theme.inc', ), ); } /** * Implementation of hook_perm(). */ function trees_perm() { return array('add content to trees', 'administer trees outlines', 'create new trees', 'view outline tab', 'create new trees on node'); } /** * Implementation of hook_menu(). */ function trees_menu() { $items['admin/content/trees'] = array( 'title' => 'trees', 'description' => "Manage your site's trees outlines.", 'page callback' => 'trees_admin_overview', 'access arguments' => array('administer trees outlines'), 'file' => 'trees.admin.inc', ); $items['admin/content/trees/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/content/trees/settings'] = array( 'title' => 'Settings', 'page callback' => 'drupal_get_form', 'page arguments' => array('trees_admin_settings'), 'access arguments' => array('administer site configuration'), 'type' => MENU_LOCAL_TASK, 'weight' => 8, 'file' => 'trees.admin.inc', ); $items['admin/content/trees/%node'] = array( 'title' => 'Re-order trees pages and change titles', 'page callback' => 'drupal_get_form', 'page arguments' => array('trees_admin_edit', 3), 'access callback' => '_trees_outline_access', 'access arguments' => array(3), 'type' => MENU_CALLBACK, 'file' => 'trees.admin.inc', ); $items['trees'] = array( 'title' => 'trees', 'page callback' => 'trees_render', 'access arguments' => array('access content'), 'type' => MENU_SUGGESTED_ITEM, 'file' => 'trees.pages.inc', ); $items['node/%node/outline'] = array( 'title' => 'Outline', 'page callback' => 'trees_outline', 'page arguments' => array(1), 'access callback' => 'trees_access_outline', 'access arguments' => array(1), 'type' => MENU_LOCAL_TASK, 'weight' => 2, 'file' => 'trees.pages.inc', ); $items['node/%node/outline/remove/%'] = array( 'title' => 'Remove from outline', 'page callback' => 'drupal_get_form', 'page arguments' => array('trees_remove_form', 1, 4), 'access callback' => '_trees_outline_remove_access', 'access arguments' => array(1), 'type' => MENU_CALLBACK, 'file' => 'trees.pages.inc', ); $items['ajax/trees'] = array( 'page callback' => 'trees_ajax_update', 'file' => 'trees_blocks.inc', 'access callback' => TRUE ); return $items; } /** * Access callback for outline */ function trees_access_outline ($node) { return (trees_type_is_allowed($node->type) && user_access('view outline tab')); } /** * Menu item access callback - determine if the outline tab is accessible. */ function _trees_outline_access($node) { return user_access('administer trees outlines') && node_access('view', $node); } /** * Menu item access callback - determine if the user can remove nodes from the outline. */ function _trees_outline_remove_access($node) { return isset($node->trees) && ($node->trees['bid'] != $node->nid) && _trees_outline_access($node); } /** * Returns an array of all trees. * * This list may be used for generating a list of all the trees, or for building * the options for a form select. */ function trees_get_trees() { static $all_trees; if (!isset($all_trees)) { $all_trees = array(); $result = db_query("SELECT bid, nid FROM {trees}"); $nids = array(); while ($trees = db_fetch_array($result)) { $nids[] = $trees['nid']; } if ($nids) { $result2 = db_query(db_rewrite_sql("SELECT n.type, n.title, b.*, ml.* FROM {trees} b INNER JOIN {node} n on b.nid = n.nid INNER JOIN {menu_links} ml ON b.mlid = ml.mlid WHERE n.nid IN (". implode(',', $nids) .") AND n.status = 1 AND plid = 0 ORDER BY ml.weight, ml.link_title")); while ($link = db_fetch_array($result2)) { $link['href'] = $link['link_path']; $link['options'] = unserialize($link['options']); $all_trees[$link['nid']] = $link; } } } return $all_trees; } /** * Build the parent selection form element for the node form or outline tab * * This function is also called when generating a new set of options during the * AJAX callback, so an array is returned that can be used to replace an existing * form element. */ //new function _trees_parent_select($trees_link) { if (variable_get('menu_override_parent_selector', FALSE)) { return array(); } // Offer a message or a drop-down to choose a different parent page. $form = array( '#type' => 'hidden', '#value' => -1, '#prefix' => '
'. t('%title is part of a trees outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', array('%title' => $node->title)) .'
', '#weight' => -10, ); } } /** * Return an array with default values for a trees link. */ function _trees_link_defaults($nid) { return array('original_bid' => 0, 'menu_name' => '', 'nid' => $nid, 'bid' => 0, 'router_path' => 'node/%', 'plid' => 0, 'mlid' => 0, 'has_children' => 0, 'weight' => 0, 'module' => 'trees', 'options' => array()); } /** * A recursive helper function for trees_toc(). */ function _trees_toc_recurse($trees, $indent, &$toc, $exclude, $depth_limit) { foreach ($trees as $data) { if ($data['link']['depth'] > $depth_limit) { // Don't iterate through any links on this level. break; } if (!in_array($data['link']['mlid'], $exclude)) { $toc[$data['link']['mlid']] = $indent .' '. truncate_utf8($data['link']['title'], 30, TRUE, TRUE); if ($data['below']) { _trees_toc_recurse($data['below'], $indent .'--', $toc, $exclude, $depth_limit); } } } } /** * Returns an array of trees pages in table of contents order. * * @param $bid * The ID of the trees whose pages are to be listed. * @param $exclude * Optional array of mlid values. Any link whose mlid is in this array * will be excluded (along with its children). * @param $depth_limit * Any link deeper than this value will be excluded (along with its children). * @return * An array of mlid, title pairs for use as options for selecting a trees page. */ function trees_toc($bid, $exclude = array(), $depth_limit) { $trees = menu_tree_all_data(trees_menu_name($bid)); $toc = array(); _trees_toc_recurse($trees, '', $toc, $exclude, $depth_limit); return $toc; } /** * Determine if a given node type is in the list of types allowed for trees. */ function trees_type_is_allowed($type) { return in_array($type, variable_get('trees_allowed_types', array('trees'))); } /** * Implementation of hook_node_type(). * * Update trees module's persistent variables if the machine-readable name of a * node type is changed. */ function trees_node_type($op, $type) { switch ($op) { case 'update': if (!empty($type->old_type) && $type->old_type != $type->type) { // Update the list of node types that are allowed to be added to trees. $allowed_types = variable_get('trees_allowed_types', array('trees')); $key = array_search($type->old_type, $allowed_types); if ($key !== FALSE) { $allowed_types[$type->type] = $allowed_types[$key] ? $type->type : 0; unset($allowed_types[$key]); variable_set('trees_allowed_types', $allowed_types); } // Update the setting for the "Add child page" link. if (variable_get('trees_child_type', 'trees') == $type->old_type) { variable_set('trees_child_type', $type->type); } } break; } } /** * Implementation of hook_help(). */ function trees_help($path, $arg) { switch ($path) { case 'admin/help#trees': $output = ''. t('The trees module is suited for creating structured, multi-page hypertexts such as site resource guides, manuals, and Frequently Asked Questions (FAQs). It permits a document to have chapters, sections, subsections, etc. Authors with suitable permissions can add pages to a collaborative trees, placing them into the existing document by adding them to a table of contents menu.') .'
'; $output .= ''. t('Pages in the trees hierarchy have navigation elements at the bottom of the page for moving through the text. These links lead to the previous and next pages in the trees, and to the level above the current page in the trees\'s structure. More comprehensive navigation may be provided by enabling the trees navigation block on the blocks administration page.', array('@admin-block' => url('admin/build/block'))) .'
'; $output .= ''. t('Users can select the printer-friendly version link visible at the bottom of a trees page to generate a printer-friendly display of the page and all of its subsections. ') .'
'; $output .= ''. t("Users with the administer trees outlines permission can add a post of any content type to a trees, by selecting the appropriate trees while editing the post or by using the interface available on the post's outline tab.") .'
'; $output .= ''. t('Administrators can view a list of all trees on the trees administration page. The Outline page for each trees allows section titles to be edited or rearranged.', array('@admin-node-trees' => url('admin/content/trees'))) .'
'; $output .= ''. t('For more information, see the online handtrees entry for trees module.', array('@trees' => 'http://drupal.org/handtrees/modules/trees/')) .'
'; return $output; case 'admin/content/trees': return ''. t('The trees module offers a means to organize a collection of related posts, collectively known as a trees. When viewed, these posts automatically display links to adjacent trees pages, providing a simple navigation system for creating and reviewing structured content.') .'
'; case 'node/%/outline': return ''. t('The outline feature allows you to include posts in the trees hierarchy, as well as move them within the hierarchy or to reorder an entire trees.', array('@trees' => url('trees'), '@trees-admin' => url('admin/content/trees'))) .'
'; } } /** * Like menu_link_load(), but adds additional data from the {trees} table. * * Do not call when loading a node, since this function may call node_load(). */ function trees_link_load($mlid) { if ($item = db_fetch_array(db_query("SELECT * FROM {menu_links} ml INNER JOIN {trees} b ON b.mlid = ml.mlid LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $mlid))) { _menu_link_translate($item); return $item; } return FALSE; } /** * Get the data representing a subtrees of the trees hierarchy. * * The root of the subtrees will be the link passed as a parameter, so the * returned trees will contain this item and all its descendents in the menu trees. * * @param $item * A fully loaded menu link. * @return * An subtrees of menu links in an array, in the order they should be rendered. */ function trees_menu_subtrees_data($item) { static $trees = array(); // Generate a cache ID (cid) specific for this $menu_name and $item. $cid = 'links:'. $item['menu_name'] .':subtrees-cid:'. $item['mlid']; if (!isset($trees[$cid])) { $cache = cache_get($cid, 'cache_menu'); if ($cache && isset($cache->data)) { // If the cache entry exists, it will just be the cid for the actual data. // This avoids duplication of large amounts of data. $cache = cache_get($cache->data, 'cache_menu'); if ($cache && isset($cache->data)) { $data = $cache->data; } } // If the subtrees data was not in the cache, $data will be NULL. if (!isset($data)) { $match = array("menu_name = '%s'"); $args = array($item['menu_name']); $i = 1; while ($i <= MENU_MAX_DEPTH && $item["p$i"]) { $match[] = "p$i = %d"; $args[] = $item["p$i"]; $i++; } $sql = " SELECT b.*, m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, ml.* FROM {menu_links} ml INNER JOIN {menu_router} m ON m.path = ml.router_path INNER JOIN {trees} b ON ml.mlid = b.mlid WHERE ". implode(' AND ', $match) ." ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC"; $data['trees'] = menu_tree_data(db_query($sql, $args), array(), $item['depth']); $data['node_links'] = array(); menu_tree_collect_node_links($data['trees'], $data['node_links']); // Compute the real cid for trees subtrees data. $trees_cid = 'links:'. $item['menu_name'] .':subtrees-data:'. md5(serialize($data)); // Cache the data, if it is not already in the cache. if (!cache_get($trees_cid, 'cache_menu')) { cache_set($trees_cid, $data, 'cache_menu'); } // Cache the cid of the (shared) data using the menu and item-specific cid. cache_set($cid, $trees_cid, 'cache_menu'); } // Check access for the current user to each item in the trees. menu_tree_check_access($data['trees'], $data['node_links']); $trees[$cid] = $data['trees']; } return $trees[$cid]; } //New functions /* * Helper function get bids of all trees */ function _trees_get_treesbids() { $result = db_query("SELECT DISTINCT bid FROM {trees}"); while ($bid = db_fetch_object($result)) { $bids[] = $bid->bid; } return $bids; } /** * Helper function * Get's the name of the root node of a trees * * @bid the id of the trees * @return the name of the trees or false if no match is found */ function _trees_get_root_name($bid) { $result = db_result(db_query("SELECT link_path FROM {menu_links} WHERE menu_name = '%s' AND plid = 0", 'trees-toc-'.$bid)); if ($result) { $path = explode('/', $result); $nid = $path[1]; return db_result(db_query("SELECT title FROM {node} WHERE nid = %d", $nid)); } return false; } /* * Validation function will check if an outline is selected, other wise do not save in the database * When an outline is allready existing delete it at the outline, it is not part of this function */ function trees_validate_outline($form, &$form_state) { foreach ($form_state['values']['trees'] as $bid => $trees) { if ($trees['plid'] == 0) { $form_state['values']['trees'][$bid]['bid'] = 0; } } } /* * Helper function, looks if a node is allready the root of another trees * * @param bid the trees id * @return true/false */ function trees_check_root() { $result = db_result(db_query("SELECT m.mlid FROM menu_links m INNER JOIN trees t ON t.mlid = m.mlid WHERE m.plid = %d AND m.link_path = '%s' AND m.module = '%s'", 0, arg(0).'/'.arg(1), 'trees')); return ($result) ? true : false; } /** * Implementation of hook_block(). */ function trees_block($op = 'list', $delta = 0, $edit = array()) { switch ($op) { case 'list': $blocks = array(); $trees = trees_get_trees(); foreach($trees as $tree) { $blocks[$tree['bid'] .'_siblings'] = array( 'info' => t('@title - Current siblings', array('@title' => $tree['title'])), 'cache' => BLOCK_CACHE_PER_PAGE | BLOCK_CACHE_PER_USER, ); $blocks[$tree['bid'] .'_parents'] = array( 'info' => t('@title - Current parents', array('@title' => $tree['title'])), 'cache' => BLOCK_CACHE_PER_PAGE | BLOCK_CACHE_PER_USER, ); $blocks[$tree['bid'] .'_children'] = array( 'info' => t('@title - Current children', array('@title' => $tree['title'])), 'cache' => BLOCK_CACHE_PER_PAGE | BLOCK_CACHE_PER_USER, ); } return $blocks; break; case 'view': if(arg(0) !== 'node' || !is_numeric(arg(1))) { return; } $node = node_load(arg(1)); include_once(dirname(__FILE__). '/trees_blocks.inc'); list($bid, $delta) = explode('_', $delta, 2); switch($delta) { case 'parents': return get_node_parents_by_trees($bid, $node); break; case 'children': $page = 0; if(isset($_GET['block']) && $_GET['block'] == 'children') { $page = $_GET['page']; } $block = get_node_children_by_trees($bid, $node, $page); if(is_array($block) && !empty($block['content'])) { $block['content'] = '