|
|
@ -0,0 +1,306 @@ |
|
|
|
<?php |
|
|
|
/* |
|
|
|
Plugin Name: Extended Table of Contents (with nextpage support) |
|
|
|
Plugin URI: http://www.happybooking.de/wordpress/plugins/extended-toc |
|
|
|
Description: This plugin automatically generates and inserts a table of contents (ToC) to your pages and posts, based on tags h1-h6. Whenever the plugin discovers more than a certain amount of headings (default: 3) the ToC is inserted at the top of the page. This plugin also can handle posts that are divided into pages by the nextpage-wordpress-tag. Any feedback or suggestions are welcome. |
|
|
|
Version: 0.6.4 |
|
|
|
Author: HappyBooking UG // Daniel Boldura
|
|
|
|
Author URI: http://www.happybooking.de/ |
|
|
|
|
|
|
|
|
|
|
|
/* Copyright 2013 HappyBooking UG // Daniel Boldura (email: info at happybooking.de or daniel at boldura.de)
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify |
|
|
|
it under the terms of the GNU General Public License as published by |
|
|
|
the Free Software Foundation; either version 2 of the License, or |
|
|
|
(at your option) any later version. |
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, |
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
GNU General Public License for more details. |
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License |
|
|
|
along with this program; if not, write to the Free Software |
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
|
|
*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* Planed features and todos: |
|
|
|
* |
|
|
|
* 1. Collision detection for anchors |
|
|
|
* 2. Header hierarchie |
|
|
|
* 3. Support markups for show or hide the ToC on single pages/posts |
|
|
|
* 4. Config the ToC within a markup e.g. [extoc start=5 headers=1,2,3 title="My table of contents"] oder [extoc start=5 headers=1,2,3 notitle] |
|
|
|
*/ |
|
|
|
|
|
|
|
define( 'EXTENDED_TOC_VERSION', '0.6.4' ); |
|
|
|
define( 'EXTENDED_TOC_ID', 'extended_toc' ); |
|
|
|
define( 'EXTENDED_TOC_NAME', 'Extended-ToC' ); |
|
|
|
|
|
|
|
if( !class_exists('ExToC') ) { |
|
|
|
class ExToC |
|
|
|
{ |
|
|
|
private $path; |
|
|
|
private $content = ""; |
|
|
|
private $fullcontent = ""; |
|
|
|
private $pages = array(); |
|
|
|
private $ID = 0; |
|
|
|
private $counter = 1; |
|
|
|
private $totalHeadings = 0; |
|
|
|
|
|
|
|
public function __construct() { |
|
|
|
$this->path = plugins_url( '', __FILE__ ); |
|
|
|
$this->exclude_post_types = array( 'attachment', 'revision', 'nav_menu_item', 'safecss' ); |
|
|
|
|
|
|
|
// get options
|
|
|
|
$defaults = array( // default options
|
|
|
|
'heading_text' => 'Contents', |
|
|
|
'start' => 3, |
|
|
|
'show_heading_text' => true, |
|
|
|
'auto_insert_post_types' => array('page', 'post'), |
|
|
|
'heading_levels' => array('1', '2', '3', '4', '5', '6'), |
|
|
|
); |
|
|
|
$options = get_option( EXTENDED_TOC_ID, $defaults ); |
|
|
|
$this->options = wp_parse_args( $options, $defaults ); |
|
|
|
|
|
|
|
add_action( 'plugins_loaded', array(&$this, 'plugins_loaded') ); |
|
|
|
|
|
|
|
if( is_admin() ) { |
|
|
|
add_action('admin_init', array(&$this, 'admin_init')); |
|
|
|
add_action('admin_menu', array(&$this, 'admin_menu')); |
|
|
|
} |
|
|
|
else { |
|
|
|
/** Add the content filter and enqueue css **/ |
|
|
|
add_filter( 'the_content', array(&$this, 'the_content'), 10 ); |
|
|
|
add_action( 'wp_enqueue_scripts', array(&$this, 'wp_enqueue_scripts') ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public function __destruct() { |
|
|
|
} |
|
|
|
|
|
|
|
public function admin_init() { |
|
|
|
wp_register_style( EXTENDED_TOC_ID, $this->path . '/admin-style.css', array(), EXTENDED_TOC_VERSION ); |
|
|
|
wp_enqueue_style(EXTENDED_TOC_ID); |
|
|
|
} |
|
|
|
|
|
|
|
public function admin_menu() { |
|
|
|
// Create menu tab
|
|
|
|
$page = add_submenu_page( 'plugins.php', EXTENDED_TOC_NAME, EXTENDED_TOC_NAME, 'manage_options', EXTENDED_TOC_ID, array(&$this, 'admin_options') ); |
|
|
|
} |
|
|
|
|
|
|
|
private function save_admin_options() |
|
|
|
{ |
|
|
|
global $post_id; |
|
|
|
|
|
|
|
// security check
|
|
|
|
if ( !wp_verify_nonce( @$_POST[EXTENDED_TOC_ID], plugin_basename(__FILE__) ) ) |
|
|
|
return false; |
|
|
|
|
|
|
|
// require an administrator level to save
|
|
|
|
if ( !current_user_can( 'manage_options', $post_id ) ) |
|
|
|
return false; |
|
|
|
|
|
|
|
$this->options = array_merge( |
|
|
|
$this->options, |
|
|
|
array( |
|
|
|
'heading_text' => stripslashes( trim($_POST['heading_text']) ), |
|
|
|
'auto_insert_post_types' => @(array)$_POST['auto_insert_post_types'], |
|
|
|
) |
|
|
|
); |
|
|
|
|
|
|
|
// update_option will return false if no changes were made
|
|
|
|
update_option( EXTENDED_TOC_ID, $this->options ); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
public function admin_options() { |
|
|
|
if( isset($_GET['update']) ) { |
|
|
|
if( $this->save_admin_options() ) |
|
|
|
$msg = '<div id="message" class="updated fade"><p>' . __('Options saved.', EXTENDED_TOC_ID) . '</p></div>'; |
|
|
|
else |
|
|
|
$msg = '<div id="message" class="error fade"><p>' . __('Save failed.', EXTENDED_TOC_ID) . '</p></div>'; |
|
|
|
} |
|
|
|
?>
|
|
|
|
<div class="wrap"> |
|
|
|
<div id="icon-plugins" class="icon32"> |
|
|
|
<br /> |
|
|
|
</div> |
|
|
|
|
|
|
|
<h2><?=__("Extended Table of Contents", EXTENDED_TOC_ID)?></h2>
|
|
|
|
|
|
|
|
<?=$msg?>
|
|
|
|
|
|
|
|
<form method="post" action="<?php echo htmlentities('?page=' . $_GET['page'] . '&update'); ?>"> |
|
|
|
<?php wp_nonce_field( plugin_basename(__FILE__), EXTENDED_TOC_ID ); ?>
|
|
|
|
|
|
|
|
<div class="form_container"> |
|
|
|
<table class="form-table"> |
|
|
|
<tbody> |
|
|
|
<tr> |
|
|
|
<th><label for="heading_text"><?=__('Heading text', EXTENDED_TOC_ID); ?></label></th>
|
|
|
|
<td><input id="heading_text" type="text" class="regular-text" name="heading_text" value="<?=$this->options['heading_text']?>" /></td> |
|
|
|
</tr> |
|
|
|
|
|
|
|
<tr> |
|
|
|
<th><?=__('Add table of contents to following content types', EXTENDED_TOC_ID); ?></th>
|
|
|
|
<td> |
|
|
|
<?php foreach( get_post_types() as $post_type ): ?>
|
|
|
|
<?php if( !in_array($post_type, $this->exclude_post_types) ): ?>
|
|
|
|
<input type="checkbox" value="<?=$post_type?>" id="auto_insert_post_types_<?=$post_type?>" name="auto_insert_post_types[]"<?=in_array($post_type, $this->options['auto_insert_post_types'])?' checked="checked"':''?> />
|
|
|
|
<label for="auto_insert_post_types_<?=$post_type?>"><?=$post_type?></label><br />
|
|
|
|
<?php endif; ?>
|
|
|
|
<?php endforeach; ?>
|
|
|
|
</td> |
|
|
|
</tr> |
|
|
|
</tbody> |
|
|
|
</table> |
|
|
|
</div> |
|
|
|
|
|
|
|
<p class="submit"><input class="button-primary" type="submit" value="<?=__("Save Options", EXTENDED_TOC_ID)?>" name="submit" /></p> |
|
|
|
</form> |
|
|
|
</div> |
|
|
|
<?php |
|
|
|
} |
|
|
|
|
|
|
|
public function wp_enqueue_scripts() { |
|
|
|
wp_register_style(EXTENDED_TOC_ID, $this->path . '/style.css', array(), POWER_TOC_VERSION); |
|
|
|
wp_enqueue_style(EXTENDED_TOC_ID); |
|
|
|
} |
|
|
|
|
|
|
|
public function plugins_loaded() { |
|
|
|
load_plugin_textdomain( EXTENDED_TOC_ID, false, dirname(plugin_basename(__FILE__)) . '/locale/' ); |
|
|
|
} |
|
|
|
|
|
|
|
public function the_content($content) { |
|
|
|
global $post; |
|
|
|
|
|
|
|
if ( is_feed() ) |
|
|
|
return $content; |
|
|
|
|
|
|
|
if( !in_array(get_post_type($post), $this->options['auto_insert_post_types'])|| is_search() || is_archive() || is_front_page() ) |
|
|
|
return $content; |
|
|
|
|
|
|
|
/** Extract the content, and extract the part content if <!--nextpage--> was used **/ |
|
|
|
$this->content = $content; |
|
|
|
$this->extract_full_post_content(); |
|
|
|
|
|
|
|
$toc_content = "<div id=\"toc-np-container\">"; |
|
|
|
|
|
|
|
if( $this->options['show_heading_text'] == true ) |
|
|
|
$toc_content .= "<p id=\"toc-np-title\">" . $this->options["heading_text"] . "</p>"; |
|
|
|
|
|
|
|
$toc_content .= "<ul class=\"no-bullets\">"; |
|
|
|
$toc_content .= $this->extract_toc(); |
|
|
|
$toc_content .= "</ul></div>"; |
|
|
|
|
|
|
|
if( $this->totalHeadings >= $this->options['start'] ) |
|
|
|
return $toc_content . $this->content; |
|
|
|
else |
|
|
|
return $this->content; |
|
|
|
} |
|
|
|
|
|
|
|
/** Extract the full unshortened content from the post **/ |
|
|
|
private function extract_full_post_content() { |
|
|
|
global $post; |
|
|
|
$this->fullcontent = $post->post_content; |
|
|
|
$this->ID = $post->ID; |
|
|
|
} |
|
|
|
|
|
|
|
private function extract_toc() { |
|
|
|
/** check within the full content how many pages exists */ |
|
|
|
$this->extract_pages(); |
|
|
|
|
|
|
|
$headers = ""; |
|
|
|
|
|
|
|
/** Extract headings from every pages */ |
|
|
|
for( $pagenum = 1; $pagenum <= count($this->pages); $pagenum++ ) { |
|
|
|
$headers .= $this->exctract_headings($pagenum); |
|
|
|
} |
|
|
|
|
|
|
|
return $headers; |
|
|
|
} |
|
|
|
|
|
|
|
private function extract_pages() { |
|
|
|
/** Split the content by "nextpage"-tags if some exists */ |
|
|
|
$this->pages = preg_split("/<!--nextpage-->/msuU", $this->fullcontent); |
|
|
|
} |
|
|
|
|
|
|
|
private function exctract_headings($pagenum) { |
|
|
|
/** find all header tags within the page **/ |
|
|
|
preg_match_all('/(<h([1-6]{1})[^>]*>).*<\/h\2>/msuU', $this->pages[$pagenum-1], $matches, PREG_SET_ORDER); |
|
|
|
|
|
|
|
/** Check the headings that are desired */ |
|
|
|
if ( count($this->options['heading_levels']) != 6 ) { |
|
|
|
$new_matches = array(); |
|
|
|
for ($i = 0; $i < count($matches); $i++) { |
|
|
|
if ( in_array($matches[$i][2], $this->options['heading_levels']) ) |
|
|
|
$new_matches[] = $matches[$i]; |
|
|
|
} |
|
|
|
$matches = $new_matches; |
|
|
|
} |
|
|
|
|
|
|
|
$items = ""; |
|
|
|
|
|
|
|
for( $i = 0; $i < count($matches); $i++ ) { |
|
|
|
/** get anchor and add to find and replace arrays **/ |
|
|
|
$anchor = $this->url_encode_anchor($matches[$i][0]); |
|
|
|
$find[] = $matches[$i][0]; |
|
|
|
$this->content = str_replace( |
|
|
|
array( |
|
|
|
$matches[$i][1], // start of heading
|
|
|
|
'</h' . $matches[$i][2] . '>' // end of heading
|
|
|
|
), |
|
|
|
array( |
|
|
|
$matches[$i][1] . '<span id="' . $anchor . '">', |
|
|
|
'</span></h' . $matches[$i][2] . '>' |
|
|
|
), |
|
|
|
$this->content |
|
|
|
); |
|
|
|
|
|
|
|
/** build html */ |
|
|
|
$items .= '<li>'; |
|
|
|
$items .= '<a href="?p='.$this->ID.($pagenum>1?'&page='.$pagenum:'').'#' . $anchor . '">'; |
|
|
|
$items .= "<span class=\"toc-np-number\">" . $this->counter++ . "</span> "; |
|
|
|
$items .= strip_tags($matches[$i][0]) . '</a>'; |
|
|
|
$items .= '</li>'; |
|
|
|
|
|
|
|
$this->totalHeadings++; |
|
|
|
} |
|
|
|
|
|
|
|
return $items; |
|
|
|
} |
|
|
|
|
|
|
|
private function url_encode_anchor($anchor) |
|
|
|
{ |
|
|
|
$return = false; |
|
|
|
|
|
|
|
if(!empty($anchor) ) { |
|
|
|
/** Remove tags */ |
|
|
|
$return = trim( strip_tags($anchor) ); |
|
|
|
|
|
|
|
/** remove & */ |
|
|
|
$return = str_replace( '&', '', $return ); |
|
|
|
|
|
|
|
/** remove all unknown chars **/ |
|
|
|
$return = preg_replace("/[^0-9a-zA-Z \-_]+/", "", $return); |
|
|
|
|
|
|
|
/** Remove backspace etc */ |
|
|
|
$return = preg_replace("/[\s]+/", "-", $return); |
|
|
|
|
|
|
|
/** If we now start or end with a - or _ remove it */ |
|
|
|
$return = preg_replace("/^[-_]/", "", $return); |
|
|
|
$return = preg_replace("/[-_]$/", "", $return); |
|
|
|
} |
|
|
|
|
|
|
|
return $return; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** Initialise the class */ |
|
|
|
$tocPlugin = new ExToC(); |
|
|
|
|
|
|
|
?>
|