'Clone a book', 'page callback' => 'book_copy_copy_book', 'page arguments' => array(2), 'access arguments' => array('copy books'), 'type' => MENU_CALLBACK, ); $items['book_copy/copy/%node/%node'] = array( 'title' => 'Clone a book', 'page callback' => 'book_copy_copy_book', 'page arguments' => array(2, 3), 'access arguments' => array('copy books'), 'type' => MENU_CALLBACK, ); $items['book_copy/history/%node'] = array( 'title' => 'Show a Books history', 'page callback' => 'book_copy_show_history', 'page arguments' => array(2), 'access arguments' => array('view book history'), 'type' => MENU_CALLBACK, ); return $items; } /** * Implementatin of hook_link() */ function book_copy_link($type, $node = NULL, $teaser = FALSE) { global $user; $links = array(); if ($type == 'node' && isset($node->book) && !$teaser) { if (user_access('view book history')) { $links['book_copy_history'] = array( 'title' => t('Show Book History'), 'href' => 'book_copy/history/'. $node->book['bid'], ); } if (user_access('copy books')) { $links['book_copy_copy'] = array( 'title' => t('Derive a Copy'), 'href' => 'book_copy/copy/'. $node->book['bid'], ); if ($node->book['bid'] != $node->nid) { $links['book_copy_subtree'] = array( 'title' => t('Copy subtree'), 'href' => 'book_copy/copy/'. $node->book['bid'] .'/'. $node->nid, ); } } } return $links; } /** * This function is responsible for cloning an entire book * This will also maintain book hieararchy in the new book. * * if */ function book_copy_copy_book($book, $subtree = 0) { $newbid = 0; if (isset($book->book)) { $bid = $book->book['bid']; $book = node_load(array('nid' => $bid)); if ($subtree !== 0) { // only grab those nodes matching our sub-tree $mlid = db_fetch_array(db_query("SELECT ml.mlid FROM {menu_links} ml LEFT JOIN {book} b ON b.mlid = ml.mlid WHERE b.nid = %d", $subtree->nid)); $where = 'AND (ml.plid = %d OR ml.p1 = %d OR ml.p2 = %d OR ml.p3 = %d OR ml.p4 = %d OR ml.p5 = %d OR ml.p6 = %d OR ml.p7 = %d OR ml.p8 = %d OR ml.p9 = %d)'; $args = array_fill(0, 10, $mlid['mlid']); array_unshift($args, $bid); } else { $where = ''; $args = array($bid); } module_load_include('inc', 'clone', 'clone.pages'); $result = db_query("SELECT ml.menu_name, ml.mlid, ml.plid, ml.link_path, ml.router_path, ml.link_title, ml.module, b.nid FROM {menu_links} ml LEFT JOIN {book} b ON b.mlid = ml.mlid WHERE b.bid = %d ". $where ." ORDER BY ml.depth", $args); if ($result) { $mlinks = array(); $nidmap = array(); $mlidmap = array(); while ($row = db_fetch_array($result)) { $plid = $row['plid']; $mlid = $row['mlid']; // copy each node keeping reference to new nid via old nid (this copies the old book structure) $nidmap[$row['nid']] = clone_node_save($row['nid']); // clone_node_save() returns the nid of the new node. $node = node_load(array('nid' => $nidmap[$row['nid']])); $mlidmap[$mlid] = $node->book['mlid']; if ($newbid == 0) { $newbid = $nidmap[$row['nid']]; $message = $row['link_title']; // The function signature is: hook_book_copy_alter(&$node, $oldbid, $newbid); drupal_alter("book_copy", $node, $bid, $newbid); } if ($subtree != 0 || $node->nid != $newbid) { // we need to set the bid for this node; $node->book['bid'] = $newbid; node_save($node); } // fix lower level nested links if (isset($mlidmap[$plid])) { $node->book['plid'] = $mlidmap[$plid]; menu_link_save($node->book); } } // add book copy reference and node copy history foreach ($nidmap as $snid => $nid) { db_query('INSERT INTO {book_copy_history} (nid, bid, sbid, snid, copied) VALUES (%d, %d, %d, %d, %d)', $nid, $newbid, $bid, $snid, time()); } $book = node_load(array('nid' => $newbid)); $book->bookcopydata = array(); $book->bookcopydata['message'] = t('Successfully cloned "%message", now viewing copy.', array('%message' => $message)); if (_book_outline_access($book)) { $book->bookcopydata['url'] = 'node/'. $newbid .'/outline'; } else { $book->bookcopydata['url'] = 'node/'. $newbid; } // The function signature is: hook_book_copy_goto_alter(&$data); drupal_alter("book_copy_goto", $book); drupal_set_message($book->bookcopydata['message']); drupal_goto($book->bookcopydata['url']); // requires user has 'administer book outline' or can access personal books } } } /** * Implementation of hook_clone_node_alter */ function book_copy_clone_node_alter(&$node, $original_node, $method) { if ($method == 'save-edit') { // remove "Clone of" text $node->title = str_replace('Clone of ', '', $node->title); if (isset($node->book)) { if ($node->book['plid'] == 0) { unset($node->book['nid']); $node->book['bid'] = 'new'; } } } } function book_copy_show_history($book) { drupal_set_title(t('Book History: @title', array('@title' => $book->title))); $ssbid = _book_copy_initial_source($book->book['bid']); $tree = _book_copy_get_book_history($ssbid); $output = _book_copy_render_tree($tree, $book->book['bid']); return $output; } // nid => copied // \=> children function _book_copy_render_tree($tree, $bid) { foreach ($tree as $nid => $info) { if ($nid == $bid) { $class = 'class="messages"'; } else { $class = ''; } $output .= '