PluginProbe
Advanced Excerpt / 4.0
Advanced Excerpt v4.0
trunk 0.2.2 3.0 3.1 4.0 4.1 4.1.1 4.2 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.2.9 4.3.0 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7 All 30 releases
advanced-excerpt / advanced-excerpt.php

advanced-excerpt.php in Advanced Excerpt 4.0, at advanced-excerpt.php

554 lines 20.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Advanced Excerpt
4 Plugin URI: http://sparepencil.com/code/advanced-excerpt/
5 Description: Several improvements over WP's default excerpt. The size of the excerpt can be limited using character or word count, and HTML markup is not removed.
6 Version: 4.0
7 Author: Bas van Doren
8 Author URI: http://sparepencil.com/
9
10 Copyright 2007 Bas van Doren
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 if (!class_exists('AdvancedExcerpt')):
27 class AdvancedExcerpt
28 {
29 // Plugin configuration
30 var $name;
31 var $text_domain;
32 var $mb;
33
34 var $default_options;
35 var $custom_options;
36
37 // Tricky variable
38 var $skip_next_call;
39
40 // Reference arrays
41 // Basic HTML tags (determines which tags are in the checklist)
42 var $options_basic_tags = array
43 (
44 'a', 'abbr', 'acronym', 'b', 'big',
45 'blockquote', 'br', 'center', 'cite', 'code', 'dd', 'del', 'div', 'dl', 'dt',
46 'em', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'ins',
47 'li', 'ol', 'p', 'pre', 'q', 's', 'small', 'span', 'strike', 'strong', 'sub',
48 'sup', 'table', 'td', 'th', 'tr', 'u', 'ul'
49 );
50
51 // HTML tags allowed in <body>
52 // <style> is <head>-only, but usage is often non-standard, so it's included here
53 var $options_body_tags = array(
54 'a', 'abbr', 'acronym', 'address', 'applet',
55 'area', 'b', 'bdo', 'big', 'blockquote', 'br', 'button', 'caption', 'center',
56 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl',
57 'dt', 'em', 'fieldset', 'font', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3',
58 'h4', 'h5', 'h6', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'isindex', 'kbd',
59 'label', 'legend', 'li', 'map', 'menu', 'noframes', 'noscript', 'object',
60 'ol', 'optgroup', 'option', 'p', 'param', 'pre', 'q', 's', 'samp', 'script',
61 'select', 'small', 'span', 'strike', 'strong', 'style', 'sub', 'sup', 'table',
62 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'tt', 'u', 'ul',
63 'var'
64 );
65
66 // (not used) HTML tags which may have content that should not be considered actual text
67 // TODO: Implement a way to remove tag + content if tag is not allowed (low priority)
68 var $non_text_tags = array(
69 'applet', 'noframes', 'noscript', 'object', 'select', 'script', 'style'
70 );
71
72
73 function AdvancedExcerpt()
74 {
75 $this->name = strtolower(get_class($this));
76 $this->text_domain = $this->name;
77 $this->skip_next_call = false;
78 $this->charset = get_bloginfo('charset');
79
80 $this->load_options();
81
82 // Carefully support multibyte languages
83 if (extension_loaded('mbstring') && function_exists('mb_list_encodings'))
84 $this->mb = in_array($this->charset, mb_list_encodings());
85
86 load_plugin_textdomain($this->text_domain, PLUGINDIR . '/advanced-excerpt/');
87
88 // __FILE__ doesn't seem to work
89 $file = ABSPATH . PLUGINDIR . '/advanced-excerpt/advanced-excerpt.php';
90 register_activation_hook($file, array(
91 &$this,
92 'install'
93 ));
94
95 //register_deactivation_hook($file, array(&$this, 'uninstall'));
96
97 add_action('admin_menu', array(
98 &$this,
99 'add_pages'
100 ));
101
102 // Replace the default filter (see /wp-includes/default-filters.php)
103 remove_filter('get_the_excerpt', 'wp_trim_excerpt');
104 add_filter('get_the_excerpt', array(
105 &$this,
106 'filter'
107 ));
108 }
109
110 // Deprecated: php-4 support
111 function __construct()
112 {
113 self::AdvancedExcerpt();
114 }
115
116 function filter($text, $options = null)
117 {
118 // Merge options
119 if (is_array($options))
120 $r = array_merge($this->default_options, $options);
121 else
122 $r = $this->default_options;
123
124 extract($r, EXTR_SKIP);
125
126 // Only make the excerpt if it does not exist or 'No Custom Excerpt' is set to true
127 if ('' == $text || $no_custom)
128 {
129 // Get the full content and filter it
130 $text = get_the_content('');
131 if (1 == $no_shortcode)
132 $text = strip_shortcodes($text);
133 $text = apply_filters('the_content', $text);
134
135 // From the default wp_trim_excerpt():
136 // Some kind of precaution against malformed CDATA in RSS feeds I suppose
137 $text = str_replace(']]>', ']]&gt;', $text);
138
139 // Strip HTML if allow-all is not set
140 if (!in_array('_all', $allowed_tags))
141 {
142 if (count($allowed_tags) > 0)
143 $tag_string = '<' . implode('><', $allowed_tags) . '>';
144 else
145 $tag_string = '';
146 $text = strip_tags($text, $tag_string);
147 }
148
149 $tokens = array();
150 $out = '';
151 $w = 0;
152 // Divide the string into tokens; HTML tags, or words, followed by any whitespace
153 // (<[^>]+>|[^<>\s]+\s*)
154 preg_match_all('/(<[^>]+>|[^<>\s]+)\s*/u', $text, $tokens);
155 foreach($tokens[0] as $t)
156 { // Parse each token
157 if($w >= $length && !$finish_sentence)
158 { // Limit reached
159 break;
160 }
161 if($t[0] != '<')
162 { // Token is not a tag
163 if($w >= $length && $finish_sentence && preg_match('/[\?\.\!]\s*$/uS', $t) == 1)
164 { // Limit reached, continue until ? . or ! occur at the end
165 $out .= trim($t);
166 break;
167 }
168 if(1 == $use_words)
169 { // Count words
170 $w++;
171 }
172 else
173 { // Count/trim characters
174 $chars = trim($t); // Remove surrounding space
175 $c = $this->strlen($chars);
176 if($c + $w > $length && !$finish_sentence)
177 { // Token is too long
178 $c = ($finish_word) ? $c : $length - $w; // Keep token to finish word
179 $t = $this->substr($t, 0, $c);
180 }
181 $w += $c;
182 }
183 }
184 // Append what's left of the token
185 $out .= $t;
186 }
187
188 $text = trim(force_balance_tags($out));
189
190 // New filter in WP2.9, seems unnecessary for now
191 //$ellipsis = apply_filters('excerpt_more', $ellipsis);
192
193 // Read more
194 if ($add_link)
195 {
196 $ellipsis = $ellipsis . sprintf(' <a href="%s" class="read_more">%s</a>', get_permalink(), $read_more);
197 }
198
199 // Adding the ellipsis
200 $pos = strpos($text, '</p>', max(0, strlen($text) - 7));
201 if ($pos !== false)
202 {
203 // Stay inside the last paragraph (if it's in the last 6 characters)
204 $text = substr_replace($text, $ellipsis, $pos, 0);
205 }
206 else
207 {
208 // If <p> is an allowed tag,
209 // wrap the ellipsis for consistency with excerpt markup
210 if (in_array('_all', $allowed_tags) || in_array('p', $allowed_tags))
211 $ellipsis = '<p>' . $ellipsis . '</p>';
212
213 $text = $text . $ellipsis;
214 }
215 }
216
217 return $text;
218 }
219
220 function install()
221 {
222 add_option($this->name . '_length', 40);
223 add_option($this->name . '_use_words', 1);
224 add_option($this->name . '_no_custom', 0);
225 add_option($this->name . '_no_shortcode', 1);
226 add_option($this->name . '_finish_word', 0);
227 add_option($this->name . '_finish_sentence', 0);
228 add_option($this->name . '_ellipsis', '&hellip;');
229 add_option($this->name . '_read_more', 'Read the rest');
230 add_option($this->name . '_add_link', 0);
231 add_option($this->name . '_allowed_tags', $this->options_basic_tags);
232
233 //$this->load_options();
234 }
235
236 function uninstall()
237 {
238 // Nothing to do (note: deactivation hook is also disabled)
239 }
240
241 function load_options()
242 {
243 $this->default_options = array(
244 'length' => get_option($this->name . '_length'),
245 'use_words' => get_option($this->name . '_use_words'),
246 'no_custom' => get_option($this->name . '_no_custom'),
247 'no_shortcode' => get_option($this->name . '_no_shortcode'),
248 'finish_word' => get_option($this->name . '_finish_word'),
249 'finish_sentence' => get_option($this->name . '_finish_sentence'),
250 'ellipsis' => get_option($this->name . '_ellipsis'),
251 'read_more' => get_option($this->name . '_read_more'),
252 'add_link' => get_option($this->name . '_add_link'),
253 'allowed_tags' => (array) get_option($this->name . '_allowed_tags')
254 );
255 }
256
257
258 function update_options()
259 {
260 $length = (int) $_POST[$this->name . '_length'];
261 $use_words = ('on' == $_POST[$this->name . '_use_words']) ? 1 : 0;
262 $no_custom = ('on' == $_POST[$this->name . '_no_custom']) ? 1 : 0;
263 $no_shortcode = ('on' == $_POST[$this->name . '_no_shortcode']) ? 1 : 0;
264 $finish_word = ('on' == $_POST[$this->name . '_finish_word']) ? 1 : 0;
265 $finish_sentence = ('on' == $_POST[$this->name . '_finish_sentence']) ? 1 : 0;
266 $add_link = ('on' == $_POST[$this->name . '_add_link']) ? 1 : 0;
267
268 $ellipsis = (get_magic_quotes_gpc() == 1) ? stripslashes($_POST[$this->name . '_ellipsis']) : $_POST[$this->name . '_ellipsis'];
269 $read_more = (get_magic_quotes_gpc() == 1) ? stripslashes($_POST[$this->name . '_read_more']) : $_POST[$this->name . '_read_more'];
270
271 $allowed_tags = array_unique((array) $_POST[$this->name . '_allowed_tags']);
272
273 update_option($this->name . '_length', $length);
274 update_option($this->name . '_use_words', $use_words);
275 update_option($this->name . '_no_custom', $no_custom);
276 update_option($this->name . '_no_shortcode', $no_shortcode);
277 update_option($this->name . '_finish_word', $finish_word);
278 update_option($this->name . '_finish_sentence', $finish_sentence);
279 update_option($this->name . '_ellipsis', $ellipsis);
280 update_option($this->name . '_read_more', $read_more);
281 update_option($this->name . '_add_link', $add_link);
282 update_option($this->name . '_allowed_tags', $allowed_tags);
283
284 $this->load_options();
285 ?>
286 <div id="message" class="updated fade"><p>Options saved.</p></div>
287 <?php
288 }
289
290 function page_options()
291 {
292 if ('POST' == $_SERVER['REQUEST_METHOD'])
293 {
294 check_admin_referer($this->name . '_update_options');
295 $this->update_options();
296 }
297
298 extract($this->default_options, EXTR_SKIP);
299
300 // HTML entities for textbox
301 $ellipsis = htmlentities($ellipsis);
302 $read_more = htmlentities($read_more);
303
304 // Basic tags + enabled tags
305 $tag_list = $this->set_union($this->options_basic_tags, $allowed_tags);
306 sort($tag_list);
307 $tag_cols = 5;
308 ?>
309 <div class="wrap">
310 <div id="icon-options-general" class="icon32"><br /></div>
311 <h2><?php
312 _e("Advanced Excerpt Options", $this->text_domain);
313 ?></h2>
314 <form method="post" action="">
315 <?php
316 if (function_exists('wp_nonce_field'))
317 wp_nonce_field($this->name . '_update_options');
318 ?>
319
320 <table class="form-table">
321 <tr valign="top">
322 <th scope="row"><label for="<?php echo $this->name; ?>_length">
323 <?php _e("Excerpt Length:", $this->text_domain); ?></label></th>
324 <td>
325 <input name="<?php echo $this->name; ?>_length" type="text"
326 id="<?php echo $this->name; ?>_length"
327 value="<?php echo $length; ?>" size="2"/>
328 <input name="<?php echo $this->name; ?>_use_words" type="checkbox"
329 id="<?php echo $this->name; ?>_use_words" value="on"<?php
330 echo (1 == $use_words) ? ' checked="checked"' : ''; ?>/>
331 <?php _e("Use words?", $this->text_domain); ?>
332 </td>
333 </tr>
334 <tr valign="top">
335 <th scope="row"><label for="<?php echo $this->name; ?>_ellipsis">
336 <?php _e("Ellipsis:", $this->text_domain); ?></label></th>
337 <td>
338 <input name="<?php echo $this->name; ?>_ellipsis" type="text"
339 id="<?php echo $this->name; ?>_ellipsis"
340 value="<?php echo $ellipsis; ?>" size="5"/>
341 <?php _e('(use <a href="http://www.w3schools.com/tags/ref_entities.asp">HTML entities</a>)', $this->text_domain); ?>
342 <br />
343 <?php _e("Will substitute the part of the post that is omitted in the excerpt.", $this->text_domain); ?>
344 </td>
345 </tr>
346 <tr valign="top">
347 <th scope="row"><label for="<?php echo $this->name; ?>_length">
348 <?php _e("Finish:", $this->text_domain); ?></label></th>
349 <td>
350 <input name="<?php echo $this->name; ?>_finish_word" type="checkbox"
351 id="<?php echo $this->name; ?>_finish_word" value="on"<?php
352 echo (1 == $finish_word) ? ' checked="checked"' : ''; ?>/>
353 <?php _e("Word", $this->text_domain); ?><br/>
354 <input name="<?php echo $this->name; ?>_finish_sentence" type="checkbox"
355 id="<?php echo $this->name; ?>_finish_sentence" value="on"<?php
356 echo (1 == $finish_sentence) ? ' checked="checked"' : ''; ?>/>
357 <?php _e("Sentence", $this->text_domain); ?>
358 <br />
359 <?php _e("Prevents cutting a word or sentence at the end of an excerpt. This option can result in (slightly) longer excerpts.", $this->text_domain); ?>
360 </td>
361 </tr>
362 <tr valign="top">
363 <th scope="row"><label for="<?php echo $this->name; ?>_read_more">
364 <?php _e("&lsquo;Read-more&rsquo; Text:", $this->text_domain); ?></label></th>
365 <td>
366 <input name="<?php echo $this->name; ?>_read_more" type="text"
367 id="<?php echo $this->name; ?>_read_more" value="<?php echo $read_more; ?>" />
368 <input name="<?php echo $this->name; ?>_add_link" type="checkbox"
369 id="<?php echo $this->name; ?>_add_link" value="on" <?php
370 echo (1 == $add_link) ? 'checked="checked" ' : ''; ?>/>
371 <?php _e("Add link to excerpt", $this->text_domain); ?>
372 </td>
373 </tr>
374 <tr valign="top">
375 <th scope="row"><label for="<?php echo $this->name; ?>_no_custom">
376 <?php _e("No Custom Excerpts:", $this->text_domain); ?></label></th>
377 <td>
378 <input name="<?php echo $this->name; ?>_no_custom" type="checkbox"
379 id="<?php echo $this->name; ?>_no_custom" value="on" <?php
380 echo (1 == $no_custom) ? 'checked="checked" ' : ''; ?>/>
381 <?php _e("Generate excerpts even if a post has a custom excerpt attached.", $this->text_domain); ?>
382 </td>
383 </tr>
384 <tr valign="top">
385 <th scope="row"><label for="<?php echo $this->name; ?>_no_shortcode">
386 <?php _e("Strip Shortcodes:", $this->text_domain); ?></label></th>
387 <td>
388 <input name="<?php echo $this->name; ?>_no_shortcode" type="checkbox"
389 id="<?php echo $this->name; ?>_no_shortcode" value="on" <?php
390 echo (1 == $no_shortcode) ? 'checked="checked" ' : ''; ?>/>
391 <?php _e("Remove shortcodes from the excerpt. <em>(recommended)</em>", $this->text_domain); ?>
392 </td>
393 </tr>
394 <tr valign="top">
395 <th scope="row"><?php _e("Keep Markup:", $this->text_domain); ?></th>
396 <td>
397 <table id="<?php echo $this->name; ?>_tags_table">
398 <tr>
399 <td colspan="<?php echo $tag_cols; ?>">
400 <input name="<?php echo $this->name; ?>_allowed_tags[]" type="checkbox"
401 value="_all" <?php echo (in_array('_all', $allowed_tags)) ? 'checked="checked" ' : ''; ?>/>
402 <?php _e("Don't remove any markup", $this->text_domain); ?>
403 </td>
404 </tr>
405 <?php
406 $i = 0;
407 foreach ($tag_list as $tag):
408 if ($tag == '_all')
409 continue;
410 if (0 == $i % $tag_cols):
411 ?>
412 <tr>
413 <?php
414 endif;
415 $i++;
416 ?>
417 <td>
418 <input name="<?php echo $this->name; ?>_allowed_tags[]" type="checkbox"
419 value="<?php echo $tag; ?>" <?php
420 echo (in_array($tag, $allowed_tags)) ? 'checked="checked" ' : ''; ?>/>
421 <code><?php echo $tag; ?></code>
422 </td>
423 <?php
424 if (0 == $i % $tag_cols):
425 $i = 0;
426 echo '</tr>';
427 endif;
428 endforeach;
429 if (0 != $i % $tag_cols):
430 ?>
431 <td colspan="<?php echo ($tag_cols - $i); ?>">&nbsp;</td>
432 </tr>
433 <?php
434 endif;
435 ?>
436 </table>
437 <a href="" id="<?php echo $this->name; ?>_select_all">Select all</a>
438 / <a href="" id="<?php echo $this->name; ?>_select_none">Select none</a><br />
439 More tags:
440 <select name="<?php echo $this->name; ?>_more_tags" id="<?php echo $this->name; ?>_more_tags">
441 <?php
442 foreach ($this->options_body_tags as $tag):
443 ?>
444 <option value="<?php echo $tag; ?>"><?php echo $tag; ?></option>
445 <?php
446 endforeach;
447 ?>
448 </select>
449 <input type="button" name="<?php echo $this->name; ?>_add_tag" id="<?php echo $this->name; ?>_add_tag" class="button" value="Add tag" />
450 </td>
451 </tr>
452 </table>
453 <p class="submit"><input type="submit" name="Submit" class="button-primary"
454 value="<?php _e("Save Changes", $this->text_domain); ?>" /></p>
455 </form>
456 </div>
457 <?php
458 }
459
460 function page_script()
461 {
462 wp_enqueue_script($this->name . '_script', WP_PLUGIN_URL . '/advanced-excerpt/advanced-excerpt.js', array(
463 'jquery'
464 ));
465 }
466
467 function add_pages()
468 {
469 $options_page = add_options_page(__("Advanced Excerpt Options", $this->text_domain), __("Excerpt", $this->text_domain), 'manage_options', 'options-' . $this->name, array(
470 &$this,
471 'page_options'
472 ));
473
474 // Scripts
475 add_action('admin_print_scripts-' . $options_page, array(
476 &$this,
477 'page_script'
478 ));
479 }
480
481 // Careful multibyte support (fallback to normal functions if not available)
482
483 function substr($str, $start, $length = null)
484 {
485 $length = (is_null($length)) ? $this->strlen($str) : $length;
486 if ($this->mb)
487 return mb_substr($str, $start, $length, $this->charset);
488 else
489 return substr($str, $start, $length);
490 }
491
492 function strlen($str)
493 {
494 if ($this->mb)
495 return mb_strlen($str, $this->charset);
496 else
497 return strlen($str);
498 }
499
500 // Some utility functions
501
502 function set_complement($a, $b)
503 {
504 $c = array_diff($a, $b);
505 return array_unique($c);
506 }
507
508 function set_union($a, $b)
509 {
510 $c = array_merge($a, $b);
511 return array_unique($c);
512 }
513 }
514
515 $advancedexcerpt = new AdvancedExcerpt();
516
517 // Do not use outside the Loop!
518 function the_advanced_excerpt($args = '', $get = true)
519 {
520 global $advancedexcerpt;
521
522 $r = wp_parse_args($args);
523
524 if (isset($r['ellipsis']))
525 $r['ellipsis'] = urldecode($r['ellipsis']);
526
527 // TODO: Switch to 'allowed_tags' (compatibility code)
528 if (isset($r['allow_tags']))
529 {
530 $r['allowed_tags'] = $r['allow_tags'];
531 unset($r['allow_tags']);
532 }
533
534 if (isset($r['allowed_tags']))
535 $r['allowed_tags'] = preg_split('/[\s,]+/', $r['allow_tags']);
536
537 if (isset($r['exclude_tags']))
538 {
539 $r['exclude_tags'] = preg_split('/[\s,]+/', $r['exclude_tags']);
540 // {all_tags} - {exclude_tags}
541 $r['allowed_tags'] = $advancedexcerpt->set_complement($advancedexcerpt->options_body_tags, $r['exclude_tags']);
542 unset($r['exclude_tags']);
543 }
544
545 // Set custom options (discard after use)
546 $advancedexcerpt->custom_options = $r;
547 if ($get)
548 echo get_the_excerpt();
549 else
550 the_excerpt();
551 $advancedexcerpt->custom_options = null;
552 }
553 endif;
554