course_book.module in Course 8.3
File
modules/course_book/course_book.moduleView source
<?php
/**
* Course book admin form.
*/
function course_book_admin($form, &$form_state) {
$form = array();
$form['book_intro']['#markup'] = t("Course books allow you to use Drupal Books as course objects.");
$form['course_book_default_node_type'] = array(
'#title' => t('Default node type.'),
'#type' => 'select',
'#options' => node_type_get_names(),
'#default_value' => variable_get('course_book_default_node_type', 'book'),
'#description' => t("Sets the default node type for book objects. This can be overridden whilst assigning a book to a course outline."),
);
return system_settings_form($form);
}
/**
* Implements hook_menu().
*/
function course_book_menu() {
$items = array();
$items['admin/course/book'] = array(
'access arguments' => array(
'administer courses',
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'course_book_admin',
),
'type' => MENU_LOCAL_TASK,
'title' => 'Book',
);
return $items;
}
/**
* Overrides a course outline list item.
*
* @param array $item
* A course outline list item. The structure mirrors an array element from
* the $items param from theme_item_list().
* @param CourseObject $courseObject
* The instantiated course object that has an outline item to be overridden.
* @param string $type
* The type of override to perform. Can be:
* - all_pages: Displays a nested item list of book pages, with all items
* fully expanded.
* - active_tree: Displays the active menu tree, mirroring the core book
* outline on book pages. Additionally, the number of pages are appended to
* the course outline item title, to indicate there are pages when not a
* page within the active tree.
* - count: Displays the book title, with the number of pages in the book.
*/
function course_book_override_outline_list_item(&$item, Drupal\course\Entity\CourseObject $courseObject, $type) {
if ($courseObject
->getComponent() == 'book') {
if ($bid = $courseObject
->getInstanceId()) {
// Alter the book outline item differently, depending on configured type.
switch ($type) {
case 'all_pages':
// If users do not have access to take the book course object, display
// only titles instaed of links.
$links = $courseObject
->access('take');
// Get the top level item in the tree (the book item).
$book_items = course_book_items($bid, $links);
$book_tree = reset($book_items);
if ($book_tree['children']) {
// Add a fully expanded list of children below the existing course
// book outline item.
$item['children'] = $book_tree['children'];
}
break;
case 'active_tree':
$book_node = \Drupal\node\Entity\Node::load($bid);
if (!empty($book_node->book)) {
/* @var $book_manager Drupal\book\BookManager */
$book_manager = \Drupal::service('book.manager');
$tree = $book_manager
->bookTreeAllData($bid);
// There should only be one element at the top level.
$data = array_shift($tree);
if ($data['below']) {
$output = $book_manager
->bookTreeOutput($data['below']);
// Append to the existing book outline item's data output, since we
// don't have an array but already rendered active menu tree output.
$item['book_children']['#markup'] = drupal_render($output);
$item['book_children']['#weight'] = 100;
}
// Note we do not break here purposefully, to additionally append the
// number of pages to the course outline item title. We do this
// because when not a page within the active tree, no children items
// display, so without this there is no indicator they are there.
}
case 'count':
$count = course_book_count($bid);
// @kludge replace outline object title with an appended version.
$subject = $item['#markup'] ?? '';
$pattern = $courseObject
->getTitle();
if ($count > 1) {
$replacement = t('@title (@count pages)', array(
'@title' => $pattern,
'@count' => $count,
));
// Replace only the first instance of the title (in case the same
// string also exists elsewhere in the data output.
//$replaced = str_replace($pattern, $replacement, $subject);
$replaced = preg_replace('/' . preg_quote($pattern, '/') . '/', $replacement, $subject, 1);
$item['#markup'] = $replaced;
}
break;
}
}
}
}
/**
* Counts the number of book pages that are accessible to the current user.
*
* @param int $bid
* A book ID.
* @param array $exclude
* (Optional) An array of node IDs. Any link whose ID is in this array
* will be excluded (along with its children).
*
* @return int
* The number of accessible pages in a book.
*/
function course_book_count($bid, $exclude = array()) {
/* @var $book_manager Drupal\book\BookManager */
$book_manager = \Drupal::service('book.manager');
$toc = $book_manager
->getTableOfContents($bid, \Drupal::config('course_book.settings')
->get('depth', 100), $exclude);
$count = count($toc);
return $count;
}
/**
* Gets a nested list of book items.
*
* @param int $bid
* A book ID.
* @param bool $links
* Whether or not to render item links.
* @param array $exclude
* (Optional) An array of menu link IDs. Any link whose mlid is in this array
* will be excluded (along with its children).
* @param int $depth_limit
* Any link deeper than this value will be excluded (along with its children).
*
* @return array
* A nested array of book items, suitable for theme_item_list().
*/
function course_book_items($bid, $links = TRUE, $exclude = array(), $depth_limit = MENU_MAX_DEPTH) {
$tree = menu_tree_all_data(book_menu_name($bid));
$items = course_book_items_recurse($tree, $links, $exclude, $depth_limit);
return $items;
}
/**
* A recursive helper function for course_book_items().
*/
function course_book_items_recurse($tree, $links, $exclude, $depth_limit) {
$items = array();
foreach ($tree as $data) {
if ($data['link']['depth'] > $depth_limit) {
// Don't iterate through any links on this level.
break;
}
if (!in_array($data['link']['nid'], $exclude)) {
$link = array();
// Build the current nested item - either a link or text.
$text = truncate_utf8($data['link']['title'], 30, TRUE, TRUE);
if ($links) {
$path = $data['link']['href'];
$link['data'] = l($text, $path);
}
else {
$link['data'] = $text;
}
// Get children, if any.
if ($data['below']) {
$link['children'] = course_book_items_recurse($data['below'], $links, $exclude, $depth_limit);
}
$items[] = $link;
}
}
return array_values($items);
}
/**
* Implements hook_node_insert().
*
* Resave the book course object associated with this book page.
*
* We need to do this so that proper hooks get run to set content access.
*/
function course_book_node_insert($node) {
if (!empty($node->book['bid']) && $node->book['bid'] != $node
->id()) {
if ($courseObject = course_get_course_object('book', $node->book['bid'])) {
$courseObject
->save();
}
}
}
Functions
Name | Description |
---|---|
course_book_admin | Course book admin form. |
course_book_count | Counts the number of book pages that are accessible to the current user. |
course_book_items | Gets a nested list of book items. |
course_book_items_recurse | A recursive helper function for course_book_items(). |
course_book_menu | Implements hook_menu(). |
course_book_node_insert | Implements hook_node_insert(). |
course_book_override_outline_list_item | Overrides a course outline list item. |