View source
<?php
function toc_node_form_node_type_form_alter(&$form, $form_state) {
$type = $form['#node_type'];
$form['toc_node'] = array(
'#type' => 'fieldset',
'#title' => t('Table of contents'),
'#description' => t('Settings for adding TOC to nodes of this content type.'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#group' => 'additional_settings',
'#attached' => array(
'js' => array(
drupal_get_path('module', 'menu') . '/menu.admin.js',
),
),
);
$form['toc_node']['toc_node_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Enable table of contents'),
'#default_value' => variable_get('toc_node_enabled_' . $type->type, 0),
);
$form['toc_node']['toc_node_back_to_top_links'] = array(
'#type' => 'select',
'#title' => t('Back to top links'),
'#default_value' => variable_get('toc_node_back_to_top_links_' . $type->type, 0),
'#empty_option' => t('- Select -'),
'#options' => array(
t('Disabled - always'),
t('Enabled - always'),
t('Enabled - per node option'),
t('Disabled - per node option'),
),
);
$form['toc_node']['toc_node_level'] = array(
'#type' => 'select',
'#title' => t('Level'),
'#description' => t('Up to what heading level to include.'),
'#default_value' => variable_get('toc_node_level_' . $type->type, 'h2'),
'#empty_option' => t('- Select -'),
'#options' => array(
2 => 'h2',
3 => 'h3',
4 => 'h4',
5 => 'h5',
6 => 'h6',
7 => 'h7',
),
);
$form['toc_node']['toc_node_styles'] = array(
'#type' => 'checkboxes',
'#title' => t('Styles available'),
'#default_value' => variable_get('toc_node_styles_' . $type->type, array()),
'#options' => array(
'none' => t('No TOC'),
'bullets' => t('Bullets'),
'numbers' => t('Numbered'),
),
);
$form['toc_node']['toc_node_style_default'] = array(
'#type' => 'select',
'#title' => t('Display default'),
'#default_value' => variable_get('toc_node_style_default_' . $type->type, 'bullets'),
'#empty_option' => t('- Select -'),
'#options' => array(
'none' => t('No TOC'),
'bullets' => t('Bullets'),
'numbers' => t('Numbered'),
),
);
}
function toc_node_form_node_form_alter(&$form, &$form_state, $form_id) {
$type = $form['type']['#value'];
$toc_enabled = variable_get('toc_node_enabled_' . $type, 0);
if (empty($toc_enabled)) {
return;
}
$options_all = array(
'none' => t('No TOC'),
'bullets' => t('Bullets'),
'numbers' => t('Numbered'),
);
$options_enabled = variable_get('toc_node_styles_' . $type, array());
$options = array_intersect_key($options_all, array_flip($options_enabled));
$style = variable_get('toc_node_style_default_' . $type, 'bullets');
$back_to_top_links_defaults = variable_get('toc_node_back_to_top_links_' . $type, 0);
$level = variable_get('toc_node_level_' . $type, 'h2');
if (isset($form['nid']['#value'])) {
$node_settings = db_query('SELECT * FROM {toc_node} WHERE nid = :nid', array(
':nid' => $form['nid']['#value'],
))
->fetchAssoc();
if (!empty($node_settings) && isset($options[$node_settings['style']])) {
$style = $node_settings['style'];
$back_to_top_links = $node_settings['back_links'];
$level = $node_settings['level'];
}
}
$form['toc_node'] = array(
'#type' => 'fieldset',
'#title' => t('Table of contents'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#group' => 'additional_settings',
);
$form['toc_node']['toc_node_style'] = array(
'#type' => 'select',
'#title' => t('TOC Display'),
'#default_value' => $style,
'#empty_option' => t('- Select -'),
'#options' => $options,
);
if (!isset($back_to_top_links) && ($back_to_top_links_defaults == 1 || $back_to_top_links_defaults == 2)) {
$back_to_top_links = 1;
}
elseif (!isset($back_to_top_links) && ($back_to_top_links_defaults == 0 || $back_to_top_links_defaults == 3)) {
$back_to_top_links = 0;
}
$form['toc_node']['toc_node_back_to_top_links'] = array(
'#type' => 'checkbox',
'#title' => t('Back to top links'),
'#default_value' => $back_to_top_links,
);
if ($back_to_top_links_defaults < 2) {
$form['toc_node']['toc_node_back_to_top_links']['#access'] = FALSE;
}
$form['toc_node']['toc_node_level'] = array(
'#type' => 'select',
'#title' => t('Level'),
'#description' => t('Up to what heading level to include.'),
'#default_value' => $level,
'#empty_option' => t('- Select -'),
'#options' => array(
2 => 'h2',
3 => 'h3',
4 => 'h4',
5 => 'h5',
6 => 'h6',
7 => 'h7',
),
);
}
function toc_node_node_insert($node) {
$toc_enabled = variable_get('toc_node_enabled_' . $node->type, 0);
if (empty($toc_enabled)) {
return;
}
db_insert('toc_node')
->fields(array(
'nid' => $node->nid,
'style' => $node->toc_node_style,
'level' => $node->toc_node_level,
'back_links' => $node->toc_node_back_to_top_links,
))
->execute();
}
function toc_node_node_update($node) {
$toc_enabled = variable_get('toc_node_enabled_' . $node->type, 0);
if (empty($toc_enabled)) {
return;
}
if (!isset($node->toc_node_style)) {
$node->toc_node_style = variable_get('toc_node_style_default_' . $node->type, 'bullets');
}
if (!isset($node->toc_node_level)) {
$node->toc_node_level = variable_get('toc_node_level_' . $node->type, 'h2');
}
if (!isset($node->toc_node_back_to_top_links)) {
$node->toc_node_back_to_top_links = variable_get('toc_node_back_to_top_links_' . $node->type, 0);
}
db_merge('toc_node')
->key(array(
'nid' => $node->nid,
))
->fields(array(
'nid' => $node->nid,
'style' => $node->toc_node_style,
'level' => $node->toc_node_level,
'back_links' => $node->toc_node_back_to_top_links,
))
->execute();
}
function toc_node_node_delete($node) {
db_delete('toc_node')
->condition('nid', $node->nid)
->execute();
}
function toc_node_node_load($nodes, $types) {
$skip = TRUE;
foreach ($types as $type) {
$toc_enabled = variable_get('toc_node_enabled_' . $type, 0);
if (!empty($toc_enabled)) {
$skip = FALSE;
}
}
if ($skip) {
return;
}
$result = db_query('SELECT * FROM {toc_node} WHERE nid IN(:nids)', array(
':nids' => array_keys($nodes),
));
foreach ($result as $record) {
$nodes[$record->nid]->toc_node_style = $record->style;
$nodes[$record->nid]->toc_node_level = $record->level;
$nodes[$record->nid]->toc_node_back_to_top_links = $record->back_links;
}
}
function toc_node_theme() {
$theme = array(
'toc_node_move_to_top_link' => array(
'variables' => array(
'content' => NULL,
),
),
'toc_node_links' => array(
'variables' => array(
'links' => NULL,
),
'template' => 'templates/toc_node_links',
),
);
return $theme;
}
function theme_toc_node_move_to_top_link($variables) {
$content = $variables['content'];
$path_alias = drupal_get_path_alias($_GET['q']);
$link = '<div class="toc-top-links">';
$link .= l(t('Move to top'), $path_alias, array(
'fragment' => 'table-of-contents',
));
$link .= '</div>';
$output = str_ireplace('</h2>', '</h2>' . $link, $content);
return $output;
}
function toc_node_block_info() {
$return = array();
$return['toc_node'] = array(
'info' => t('Table Of Contents'),
'cache' => DRUPAL_CACHE_PER_PAGE,
);
return $return;
}
function toc_node_block_view($delta = '') {
$block = array();
switch ($delta) {
case 'toc_node':
if (arg(0) != 'node' || !is_numeric(arg(1))) {
return;
}
$nid = arg(1);
$node = node_load($nid);
$toc_enabled = variable_get('toc_node_enabled_' . $node->type, 0);
if (empty($toc_enabled)) {
return;
}
if (!isset($node->toc_node_style) || $node->toc_node_style == 'none') {
return;
}
$node_rendered = node_view($node);
$node_content = drupal_render($node_rendered);
$content = toc_node_generate($node_content, $node->toc_node_style, $node->toc_node_level, $node->toc_node_back_to_top_links, 'toc_list');
if ($content != $node_content) {
$block['subject'] = t('Contents');
$block['content'] = $content;
}
break;
}
return $block;
}
function toc_node_generate($content, $style, $heading_levels, $back_to_top_links, $return = 'all') {
static $toc_list_outputed = FALSE;
$links = array();
$anchor_count = 0;
$dom_document = new DOMDocument('1.0', 'utf-8');
@$dom_document
->loadHTML('<?xml encoding="UTF-8"><div id="toc-node">' . $content . '</div>');
for ($level = 2; $level <= $heading_levels; $level++) {
toc_node_add_anchors($dom_document, $anchor_count, $links, $level);
}
if (empty($links)) {
return $content;
}
$title = NULL;
$toc = toc_node_links($dom_document, $links, $style);
toc_node_remove_toc_tags($dom_document, $heading_levels);
$content_updated = $dom_document
->saveHTML();
$content_fragments = explode('<div id="toc-node">', $content_updated);
$output = str_replace('</div></body></html>', '', $content_fragments[1]);
if ($return == 'toc_list') {
$toc_list_outputed = TRUE;
return $toc;
}
if (!$toc_list_outputed) {
$output = $toc . $output;
}
if (!empty($back_to_top_links)) {
$output = theme('toc_node_move_to_top_link', array(
'content' => $output,
));
}
return $output;
}
function toc_node_add_anchors(&$dom_document, &$anchor_count, &$links, $heading_level) {
$headers = $dom_document
->getElementsByTagName('h' . $heading_level);
foreach ($headers as $header) {
$anchor_count++;
$label = trim($header->nodeValue);
$links['toc-' . $anchor_count]['label'] = $label;
$links['toc-' . $anchor_count]['level'] = $heading_level;
$classes = $header
->getAttribute('class');
$header
->setAttribute('class', $classes . ' toc-headings');
$anchor_div = $dom_document
->createElement('div');
$anchor_div
->setAttribute('class', 'toc-item-anchor');
$target = $dom_document
->createElement('a');
$target
->setAttribute('name', 'toc-' . $anchor_count);
$anchor_div
->appendChild($target);
$header->parentNode
->insertBefore($anchor_div, $header);
$toc_tag = $dom_document
->createElement('toc');
$toc_tag
->setAttribute('class', 'toc-' . $anchor_count);
$header
->appendChild($toc_tag);
}
}
function toc_node_links($dom_document, $links_data, $style) {
$path = drupal_get_path_alias($_GET['q']);
$toc_number = array();
$toc_tags = $dom_document
->getElementsByTagName('toc');
foreach ($toc_tags as $toc_tag) {
$toc_id = $toc_tag->attributes
->getNamedItem('class')->value;
$link = $links_data[$toc_id];
$toc_level = $link['level'] - 1;
$toc_level_count = count($toc_number);
for ($i = $link['level']; $i <= $toc_level_count; $i++) {
unset($toc_number[$i]);
}
if (empty($toc_number[$toc_level])) {
$toc_number[$toc_level] = 1;
}
else {
$toc_number[$toc_level] = $toc_number[$toc_level] + 1;
}
$link_number = implode('.', $toc_number);
$data = '';
if ($style == 'numbers') {
$data .= '<span class="toc-number">' . $link_number . '</span> ';
}
$data .= '<span class="toc-text">' . $link['label'] . '</span>';
$links[] = array(
'data' => l($data, $path, array(
'fragment' => $toc_id,
'html' => TRUE,
)),
'class' => array(
'toc-node-level-' . $toc_level,
),
);
}
if (empty($links)) {
return '';
}
$item_list = theme('item_list', array(
'items' => $links,
'type' => 'ul',
'attributes' => array(
'class' => 'toc-node-' . $style,
),
));
return theme('toc_node_links', array(
'links' => $item_list,
));
}
function toc_node_remove_toc_tags(&$dom_document, $heading_levels) {
for ($level = 2; $level <= $heading_levels; $level++) {
$headers = $dom_document
->getElementsByTagName('h' . $level);
foreach ($headers as $header) {
$toc = $header
->getElementsByTagName('toc');
$header
->removeChild($toc
->item(0));
}
}
}
function toc_node_node_view_alter(&$build) {
$toc_enabled = variable_get('toc_node_enabled_' . $build['#bundle'], 0);
$node = $build['#node'];
if (empty($toc_enabled) || empty($node->toc_node_style) || $node->toc_node_style == 'none') {
return;
}
if (arg(0) != 'node' || !is_numeric(arg(1)) || arg(1) != $node->nid) {
return;
}
$build['#post_render'][] = 'toc_node_node_post_render';
}
function toc_node_node_post_render($content, $element) {
$node = $element['#node'];
$content = toc_node_generate($content, $node->toc_node_style, $node->toc_node_level, $node->toc_node_back_to_top_links);
return $content;
}