POST TEASER" MENU **** **** NOTE: PLEASE DO NOT EMAIL JONATHAN LEIGHTON FOR SUPPORT. Instead email for support. **** Post Teaser -- A teaser plugin for WordPress Copyright (C) Jonathan Leighton (j@jonathanleighton.com) Copyright (C) WeyHan Ng (han@sandboxblogger.com) 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 WordPress; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ class post_teaser { var $version = "4.1.2"; var $debug = false; var $meta = ""; /*** Default option values ***/ var $default_options = array(); /*** Anything that doesn't really need to be configurable ***/ var $static_options = array ( 'blocks' => 'p|li|dt|dd|address|form|pre|tr' ); /*** Called once before any teasering. Puts the options into properties. ***/ function post_teaser() { load_plugin_textdomain('post-teaser'); $this->default_options['full_template'] = __('

%title% full Post'); $this->default_options['more_template'] = __('

%title% full Post(%word_image_count%, estimated %reading_time% reading time)', 'post-teaser'); $this->default_options['teaser_template'] = __('

%title% full Post'); $this->default_options['target'] = '80'; $this->default_options['word_mins'] = __(' mins', 'post-teaser'); $this->default_options['word_secs'] = __(' secs', 'post-teaser'); $this->default_options['time_separator'] = ':'; $this->default_options['zero_counts'] = '0'; $this->default_options['count_separator'] = ', '; $this->default_options['more_link'] = '0'; $this->default_options['block_stats'] = '1'; $this->default_options['block_stats_before'] = '0'; $this->default_options['omit_fullpost'] = '0'; $this->default_options['use_disable_filter'] = '0'; $this->default_options['home_control'] = '0'; $this->default_options['home_disable'] = '0'; $this->default_options['home_showall'] = '1'; $this->default_options['disable_cat'] = ''; $this->default_options['disable_cat_always'] = '0'; $this->default_options['disable_inverse'] = '0'; $this->default_options['disable_tag'] = ''; $this->default_options['disable_tag_always'] = '0'; $this->default_options['use_showall_filter'] = '0'; $this->default_options['showall_cat'] = ''; $this->default_options['showall_cat_always'] = '0'; $this->default_options['showall_inverse'] = '0'; $this->default_options['showall_tag'] = ''; $this->default_options['showall_tag_always'] = '0'; $options = get_option('post_teaser'); if (!$options) { add_option('post_teaser', $this->default_options); $options = $this->default_options; } foreach ($options as $option_name => $option_value) $this->{$option_name} = $option_value; foreach ($this->static_options as $option_name => $option_value) $this->{$option_name} = $option_value; if (!$this->full_template) { $this->full_template = $this->default_options['full_template']; } if (!$this->more_template) { $this->more_template = $this->default_options['more_template']; } if (!$this->teaser_template) { $this->teaser_template = $this->default_options['teaser_template']; } if (strstr($this->full_template, '%reading_time%') || strstr($this->more_template, '%reading_time%') || strstr($this->teaser_template, '%reading_time%')) $this->doing_reading_time = true; else $this->doing_reading_time = false; if (strstr($this->full_template, '%word_image_count%') || strstr($this->more_template, '%word_image_count%') || strstr($this->teaser_template, '%word_image_count%')) $this->doing_counts = true; else $this->doing_counts = false; } /*** For editing the plugin's configuration in the admin menu ***/ function init_option_page() { if (isset($_POST['post_teaser_submit']) && $_POST['post_teaser_submit']) { $uri_arg_pos = strpos($_SERVER["REQUEST_URI"], "&"); if ($uri_arg_pos !== false) $request_uri = substr($_SERVER["REQUEST_URI"], 0, $uri_arg_pos); else $request_uri = $_SERVER["REQUEST_URI"]; if (isset($_POST['submit_update'])) { $new_options = array ( 'full_template' => trim(stripslashes($_POST['full_template'])), 'more_template' => trim(stripslashes($_POST['more_template'])), 'teaser_template' => trim(stripslashes($_POST['teaser_template'])), 'target' => (int) $_POST['target'], 'word_mins' => $_POST['word_mins'], 'word_secs' => $_POST['word_secs'], 'time_separator' => $_POST['time_separator'], 'zero_counts' => $_POST['zero_counts'], 'count_separator' => $_POST['count_separator'], 'more_link' => $_POST['more_link'], 'block_stats' => $_POST['block_stats'], 'block_stats_before' => $_POST['block_stats_before'], 'omit_fullpost' => $_POST['omit_fullpost'], 'use_disable_filter' => $_POST['use_disable_filter'], 'home_control' => $_POST['home_control'], 'home_control_opt' => $_POST['home_control_opt'], 'disable_cat' => $_POST['disable_cat'], 'disable_cat_always' => $_POST['disable_cat_always'], 'disable_inverse' => $_POST['disable_inverse'], 'disable_tag' => $_POST['disable_tag'], 'disable_tag_always' => $_POST['disable_tag_always'], 'use_showall_filter' => $_POST['use_showall_filter'], 'showall_cat' => $_POST['showall_cat'], 'showall_cat_always' => $_POST['showall_cat_always'], 'showall_inverse' => $_POST['showall_inverse'], 'showall_tag' => $_POST['showall_tag'], 'showall_tag_always' => $_POST['showall_tag_always'] ); $new_options['count_separator'] = str_replace('&', '&', $new_options['count_separator']); /** Store all checked box as '1' or' 0' for checked or unchecked **/ $new_options['zero_counts'] = $new_options['zero_counts'] ? '1' : '0'; $new_options['more_link'] = $new_options['more_link'] ? '1' : '0'; $new_options['block_stats'] = $new_options['block_stats'] ? '1' : '0'; $new_options['block_stats_before'] = $new_options['block_stats_before'] ? '1' : '0'; $new_options['omit_fullpost'] = $new_options['omit_fullpost'] ? '1' : '0'; $new_options['home_control'] = $new_options['home_control'] ? '1' : '0'; $new_options['use_disable_filter'] = $new_options['use_disable_filter'] ? '1' : '0'; $new_options['disable_cat_always'] = $new_options['disable_cat_always'] ? '1' : '0'; $new_options['disable_inverse'] = $new_options['disable_inverse'] ? '1' : '0'; $new_options['disable_tag_always'] = $new_options['disable_tag_always'] ? '1' : '0'; $new_options['use_showall_filter'] = $new_options['use_showall_filter'] ? '1' : '0'; $new_options['showall_cat_always'] = $new_options['showall_cat_always'] ? '1' : '0'; $new_options['showall_inverse'] = $new_options['showall_inverse'] ? '1' : '0'; $new_options['showall_tag_always'] = $new_options['showall_tag_always'] ? '1' : '0'; /** normalized space for all free text list field before saving options**/ $cat_str = $new_options['disable_cat']; if ($cat_str) { $new_cat_str = ''; foreach(explode(",", $cat_str) as $cat) { $new_cat_str = $new_cat_str . "," . trim($cat); } $new_options['disable_cat'] = trim($new_cat_str, ","); } switch ($new_options['home_control_opt']) { case "home_disable": $new_options['home_showall'] = '0'; $new_options['home_disable'] = '1'; break; case "home_showall": $new_options['home_showall'] = '1'; $new_options['home_disable'] = '0'; break; default: $new_options['home_disable'] = '0'; $new_options['home_showall'] = '1'; } $tag_str = $new_options['disable_tag']; if ($tag_str) { $new_tag_str = ''; foreach(explode(",", $tag_str) as $tag) { $new_tag_str = $new_tag_str . "," . trim($tag); } $new_options['disable_tag'] = trim($new_tag_str, ","); } $cat_str = $new_options['showall_cat']; if ($cat_str) { $new_cat_str = ''; foreach(explode(",", $cat_str) as $cat) { $new_cat_str = $new_cat_str . "," . trim($cat); } $new_options['showall_cat'] = trim($new_cat_str, ","); } $tag_str = $new_options['showall_tag']; if ($tag_str) { $new_tag_str = ''; foreach(explode(",", $tag_str) as $tag) { $new_tag_str = $new_tag_str . "," . trim($tag); } $new_options['showall_tag'] = trim($new_tag_str, ","); } update_option('post_teaser', $new_options); header('Location: ' . $request_uri . '&saved=true'); } elseif (isset($_POST['submit_reset'])) { update_option('post_teaser', $this->default_options); header('Location: ' . $request_uri . '&saved=true'); } die; } $post_teaser_page = add_theme_page(__('Post Teaser', 'post-teaser'), __('Post Teaser', 'post-teaser'), 'manage_options', __FILE__, array(&$this, 'option_page')); add_action("admin_print_scripts-$post_teaser_page", array(&$this, 'option_page_head')); } function option_page_head() { $plugindir = get_option('siteurl').'/wp-content/plugins/'.dirname(plugin_basename(__FILE__)); echo "\n"; ?>


viewing the latest documentation or the FAQ.', 'post-teaser') ?>

" method="post" name="post_teaser_form" id="post_teaser_form">

target . '" />'); ?>

%title%
%plain_title%
HTML (to go in the title attribute of a link, for instance)', 'post-teaser') ?>
%permalink%
URI of the post', 'post-teaser') ?>
%reading_time%
%word_image_count%

" ?>

54

debug) $this->debug_message .= $message . "\n"; } /*** Replaces placeholders with the relevant values ***/ function placeholders($template) { $template = str_replace('%title%', the_title('', '', false), $template); $template = str_replace('%plain_title%', strip_tags(str_replace('"', '"', the_title('', '', false))), $template); $template = str_replace('%permalink%', get_permalink(), $template); $template = str_replace('%word_image_count%', $this->word_image_count, $template); $template = str_replace('%reading_time%', $this->reading_time, $template); return $template; } /*** Counts words. PHP's str_word_count() only works for alphabetic characters ***/ function word_count($text) { $text = strip_tags($text); $text = preg_split("/\s+/", $text); $count = count($text); return $count; } /*** Where all the conditions we don't want to tease are checked here ***/ function is_disabled() { global $post, $cookiehash, $action, $sem_home_page; if (is_singular()) return true; elseif (is_feed()) return true; elseif ((!empty($post->post_password)) && ($_COOKIE['wp-postpass_' . $cookiehash] != $post->post_password)) return true; elseif ($action == 'editpost' || $action == 'edit') return true; elseif ($this->meta == 'disable') return true; elseif (isset($sem_home_page) && is_home() && function_exists('sem_static_front')) // Semilogic static front page return true; elseif ($this->home_control && is_home()) { // Must be last in chain of elseif because of nested if if ($this->home_disable) return true; else return false; // Skip the rest of the test if it is homepage and showall is selected } if ($this->use_disable_filter) { if ($this->disable_cat && (($this->disable_cat_always == '1' && in_category(array($this->disable_cat))) || is_category(array($this->disable_cat)) ) ) { if ($this->disable_inverse) return false; else return true; } elseif ($this->disable_tag && (($this->disable_tag_always == '1' && has_tag(array($this->disable_tag))) || is_tag(array($this->disable_tag)) ) ) { if ($this->disable_inverse) return false; else return true; } } if ($this->use_disable_filter && $this->disable_inverse) return true; return false; } /*** Where all the conditions for showall are checked here ***/ function is_showall() { if ($this->meta == 'showall') return true; elseif ($this->home_control && $this->home_showall && is_home()) return true; if ($this->use_showall_filter) { if ($this->showall_cat && (($this->showall_cat_always == '1' && in_category(array($this->showall_cat))) || is_category(array($this->showall_cat)) ) ) if ($this->showall_inverse) return false; else return true; elseif ($this->showall_tag && (($this->showall_tag_always == '1' && has_tag(array($this->showall_tag))) || is_tag(array($this->showall_tag)) ) ) if ($this->showall_inverse) return false; else return true; } if ($this->use_showall_filter && $this->showall_inverse) return true; return false; } /*** The real business happens here. Every post is run through this method ***/ function process($content) { global $post, $pages, $page, $multipage; $matches = null; $matches2 = null; $auto_close = array(); $this->debug_message = ''; $this->meta = get_post_meta($post->ID, 'teaser', true); /*** Checks for when we don't want to teaser stuff ***/ if ($this->is_disabled()) return $content; $this->debug('Starting teaser (got through the checks), version number is ' . $this->version); $showall = $this->is_showall(); $more = false; /*** Deal with and (this is very hackish) ***/ $plain_content = $pages[$page-1]; if (strstr($plain_content, '')) { $this->debug('A "more" tag has been detected... it will be replaced with the teaser text.'); $matches[0] = preg_replace('!.+?!', '', $content); $matches[0] = preg_replace('!

\s+

!', '', $matches[0]); $matches[0] = preg_replace('!
\s*

!', '

', $matches[0]); // Get rid of any closing tags at the end of the content as there have // been some nesting validation issues (due to WP clashing) and they will be // automatically closed in order anyway (see below) $matches[0] = preg_replace('!(\s*)+\s*$!', '', $matches[0]); $i = 0; $content = $plain_content; $more = true; } if ($multipage) { $this->debug('A "nextpage" tag has been detected.'); if ($more) $content_temp = $matches[0]; else $content_temp = $content; $content = ''; foreach ($pages as $item) $content .= $item; } if (($more || $multipage) && ($this->doing_reading_time || $this->doing_counts)) { remove_filter('the_content', array(&$this, 'process'), 20); $content = apply_filters('the_content', $content); add_filter('the_content', array(&$this, 'process'), 20); $content = str_replace(']]>', ']]>', $content); } /*** Reading time ***/ if ($this->doing_reading_time) { $this->debug('Start of reading time calculation'); $average = $this->word_count($content) / 250 * 60; $min = (int) ($average / 60); $sec = round(fmod($average, 60)); if ($sec < 10) $sec = '0' . $sec; elseif ($sec == 60) { // Fix seconds round to 60 issue $min = $min + 1; $sec = 0; } if ($min == 0) $this->reading_time = ($min * 60 + $sec) . ' ' . $this->word_secs; else $this->reading_time = $min . $this->time_separator . $sec . ' ' . $this->word_mins; $this->debug("End of reading time calculation. Result = {$this->reading_time}, sec = $sec, min = $min"); } /*** Word/image count ***/ if ($this->doing_counts) { $this->debug('Start of word/image count calculation'); $word_count = $this->word_count($content); if (!$this->zero_counts && $word_count == 0) $word_count = ''; else $word_count .= ($word_count == 1) ? __(' word', 'post-teaser') : __(' words', 'post-teaser'); $temp = preg_replace('/]*class=\'wp-smiley\'[^>]*>/i', '', $content); $image_count = preg_match_all('/]*>/i', $temp, $matches2); if (!$this->zero_counts && $image_count == 0) $image_count = ""; else $image_count .= ($image_count == 1) ? __(' image', 'post-teaser') : __(' images', 'post-teaser'); $this->word_image_count = $word_count; if ($word_count && $image_count) $this->word_image_count .= $this->count_separator; $this->word_image_count .= $image_count; $this->debug("End of word/image count calculation. Result = {$this->word_image_count}, words = $word_count, images = $image_count"); } if ($multipage) $content = $content_temp; /*** This is how it works: * Split posts into "blocks" * Find the first block which would produce a cumulative word count greater than the target * Decrement this value if the block before it is closer to the target * Decide whether to make a "teaser" of the post based on whether that chosen block is the last one. Exceptions: * If it has a tag, it's teasered regardless * If the post has a "teaser" custom field with a value of "showall", it will be returned in full regardless (and is over-ridden) * If the post is teasered: * Put all (X)HTML starting tags into an array * From this array, get the actual element names, and exclude self-closing tags (like
). What's left goes into $auto_close * Unset each element as a closing tag is found for it * The elements left over must have been chopped in half -- give them a closing tag ***/ $i = 0; $block_count = 0; if (!$more && !$showall) { preg_match_all("!.*?<({$this->blocks})[^>]*>.+?!si", $content, $matches); $matches = $matches[0]; $block_count = count($matches); $this->debug("Number of blocks: $block_count"); $current_word_count = 0; $block_word_count = array(); for ($i = 0; $current_word_count < $this->target && $i < $block_count; /* (increment is conditional, see below) */) { $block_word_count[$i] = $this->word_count($matches[$i]); $current_word_count += $block_word_count[$i]; if ($current_word_count < $this->target) $i++; } $this->debug("Finished looping through on block #$i (starts at zero). Cumulative word count was $current_word_count. Target was {$this->target}."); if ($current_word_count >= $this->target && $i > 0) { // No need if it will definitely not be teasered $this_block_distance = $current_word_count - $this->target; $last_block_distance = $this->target - ($current_word_count - $block_word_count[$i]); $this->debug("Current block is $this_block_distance words from target, previous block is $last_block_distance words from target."); if ($this_block_distance > $last_block_distance) { $i--; $this->debug("Decremented to $i"); } } } if (($i + 1 < $block_count || $more) && !$showall) { $this->debug('Post will be teasered'); $fullpost = false; for ($j = 0; $j <= $i; $j++) { preg_match_all('!<(?:[a-zA-Z1-9]+)[^>]*>!i', $matches[$j], $matches2); $matches2 = $matches2[0]; foreach ($matches2 as $id => $element) { if (preg_match('!^<([a-zA-Z1-9]+)[^>]*/>$!i', $element)) { unset($matches2[$id]); continue; } $element = preg_replace('!^<([a-zA-Z1-9]+)[^>]*>$!i', "$1", $element); $auto_close[] = $element; } } $content = ''; for ($j = 0; $j <= $i; $j++) { $temp = $matches[$j]; foreach ($auto_close as $id => $element) { $pos = strpos(" " . $temp, ""); // Space at front because 0 == false if ($pos) { $temp = substr_replace($temp, '', $pos, strlen("")); // Makes sure a closing tag is not counted more than once unset($auto_close[$id]); } } if (!$this->debug) $content .= $matches[$j]; else $content .= "\n\n" . $matches[$j] . "\n\n"; } $auto_close = array_reverse($auto_close); foreach ($auto_close as $element) $content .= ""; if ($this->more_link) { $lastp = strrpos($content, "

"); if ($lastp) { $more_template =& $this->more_template; $teaser_str = " " . $this->placeholders($more_template) . ''; $content = substr_replace($content, $teaser_str, $lastp, 0); } } $template =& $this->teaser_template; } else { $this->debug('Post is being returned in full'); $fullpost = true; $template =& $this->full_template; } if ($this->block_stats && !($this->omit_fullpost && $fullpost)) { if ($this->block_stats_before) $content = "\n\n
" . $this->placeholders($template) . '
' . $content; else $content .= "\n\n
" . $this->placeholders($template) . '
'; } $this->debug('The End.'); if ($this->debug) $content .= "\n\n\n\n"; return $content; } /*** A fix for invalid code from wpautop() (http://trac.wordpress.org/ticket/1099). ***/ /* Disabled because the bug above have been fixed. Will consider removal at a later date function autopfix($pee) { $pee = preg_replace('!(<(?:div|address|form)[^>]*>)([^<]+)

!', "$1

$2

", $pee); $pee = preg_replace('!

([^<]+)\s*?(]*>)!', "

$1

$2", $pee); return $pee; } */ function replace_excerpt($excerpt) { return the_content(); } } $post_teaser = new post_teaser(); //add_filter('the_content', array(&$post_teaser, 'autopfix')); // Disabled because the associated bug have been fixed add_filter('the_content', array(&$post_teaser, 'process'), 20); add_filter('the_excerpt', array(&$post_teaser, 'replace_excerpt'), 20); // Because Post Teaser does the same thing, better add_action('admin_menu', array(&$post_teaser, 'init_option_page')); ?>