Advanced Excerpt - Version 4.0

Version Description

  • Feature: Brand new parsing algorithm which should resolve some running time issues
  • Feature: Options to finish a word or sentence before cutting the excerpt
  • Fix: A few small bugs
Download this release

Release Info

Developer basvd
Plugin Icon wp plugin Advanced Excerpt
Version 4.0
Comparing to
See all releases

Code changes from version 3.1 to 4.0

Files changed (2) hide show
  1. advanced-excerpt.php +384 -344
  2. readme.txt +12 -10
advanced-excerpt.php CHANGED
@@ -3,7 +3,7 @@
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: 3.1
7
  Author: Bas van Doren
8
  Author URI: http://sparepencil.com/
9
 
@@ -23,491 +23,531 @@ 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
- 'a', 'abbr', 'acronym',
44
- 'b', 'big', 'blockquote', 'br',
45
- 'center', 'cite', 'code',
46
- 'dd', 'del', 'div', 'dl', 'dt',
47
- 'em',
48
- 'form',
49
- 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr',
50
- 'i', 'img', 'ins',
51
- 'li',
52
- 'ol',
53
- 'p', 'pre',
54
- 'q',
55
- 's', 'small', 'span', 'strike', 'strong', 'sub', 'sup',
56
- 'table', 'td', 'th', 'tr',
57
- 'u', 'ul'
58
  );
59
-
60
  // HTML tags allowed in <body>
61
  // <style> is <head>-only, but usage is often non-standard, so it's included here
62
  var $options_body_tags = array(
63
- 'a', 'abbr', 'acronym', 'address', 'applet', 'area',
64
- 'b', 'bdo', 'big', 'blockquote', 'br', 'button',
65
- 'caption', 'center', 'cite', 'code', 'col', 'colgroup',
66
- 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt',
67
- 'em',
68
- 'fieldset', 'font', 'form', 'frame', 'frameset',
69
- 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr',
70
- 'i', 'iframe', 'img', 'input', 'ins', 'isindex',
71
- 'kbd',
72
- 'label', 'legend', 'li',
73
- 'map', 'menu',
74
- 'noframes', 'noscript',
75
- 'object', 'ol', 'optgroup', 'option',
76
- 'p', 'param', 'pre',
77
- 'q',
78
- 's', 'samp', 'script', 'select', 'small', 'span', 'strike', 'strong', 'style', 'sub', 'sup',
79
- 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'tt',
80
- 'u', 'ul',
81
- 'var'
82
  );
83
-
84
  // (not used) HTML tags which may have content that should not be considered actual text
85
  // TODO: Implement a way to remove tag + content if tag is not allowed (low priority)
86
  var $non_text_tags = array(
87
- 'applet', 'noframes', 'noscript', 'object', 'select', 'script', 'style'
88
  );
89
-
90
 
91
  function AdvancedExcerpt()
92
  {
93
- $this->name = strtolower(get_class($this));
94
- $this->text_domain = $this->name;
95
- $this->skip_next_call = false;
96
- $this->charset = get_bloginfo('charset');
97
-
98
- $this->load_options();
99
-
100
- // Carefully support multibyte languages
101
- if(extension_loaded('mbstring') && function_exists('mb_list_encodings'))
102
- $this->mb = in_array($this->charset, mb_list_encodings());
103
-
104
- load_plugin_textdomain($this->text_domain, PLUGINDIR . '/advanced-excerpt/');
105
-
106
- register_activation_hook(__FILE__, array(&$this, 'install'));
107
-
108
- //register_deactivation_hook(__FILE__, array(&$this, 'uninstall'));
109
-
110
- add_action('admin_menu', array(&$this, 'add_pages'));
111
-
112
- // Replace the default filter (see /wp-includes/default-filters.php)
113
- remove_filter('get_the_excerpt', 'wp_trim_excerpt');
114
- add_filter('get_the_excerpt', array(&$this, 'filter'));
 
 
 
 
 
 
 
 
 
 
 
115
  }
116
-
 
117
  function __construct()
118
  {
119
- self::AdvancedExcerpt();
120
  }
121
-
122
- function filter($text)
123
  {
124
- // Merge custom parameters
125
- if(is_array($this->custom_options))
126
- $r = array_merge($this->default_options, $this->custom_options);
127
- else
128
- $r = $this->default_options;
129
-
130
- extract($r, EXTR_SKIP);
131
-
132
- // Only make the excerpt if it does not exist or 'No Custom Excerpt' is set to true
133
- if('' == $text || $no_custom)
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  {
135
- // Get the full content and filter it
136
- $text = get_the_content('');
137
- if(1 == $no_shortcode)
138
- $text = strip_shortcodes($text);
139
- $text = apply_filters('the_content', $text);
140
-
141
- // From the default wp_trim_excerpt():
142
- // Some kind of precaution against malformed CDATA in RSS feeds I suppose
143
- $text = str_replace(']]>', ']]&gt;', $text);
144
-
145
- // Strip HTML if allow-all is not set
146
- if(!in_array('_all', $allowed_tags))
147
- {
148
- if(count($allowed_tags) > 0)
149
- $tag_string = '<' . implode('><', $allowed_tags) . '>';
150
- else
151
- $tag_string = '';
152
- $text = strip_tags($text, $tag_string);
 
 
 
 
 
 
 
153
  }
154
-
155
  if(1 == $use_words)
156
- {
157
- // Words
158
-
159
- // Skip if text is already within limit
160
- if($length >= count(preg_split('/[\s]+/', strip_tags($text))))
161
- return $text;
162
-
163
- // Split on whitespace and start counting (for real)
164
- $text_bits = preg_split('/([\s]+)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
165
- $in_tag = false;
166
- $n_words = 0;
167
- $text = '';
168
- foreach($text_bits as $chunk)
169
- {
170
- if(!in_tag || strpos($chunk, '>') !== false)
171
- $in_tag = (strrpos($chunk, '>') < strrpos($chunk, '<'));
172
-
173
- // Whitespace outside tags is word separator
174
- if(!$in_tag && '' == trim($chunk))
175
- $n_words++;
176
-
177
- if($n_words >= $length && !$in_tag)
178
- break;
179
-
180
- $text .= $chunk;
181
- }
182
  }
183
  else
184
- {
185
- // Characters
186
-
187
- // Count characters, not whitespace, not those belonging to HTML tags
188
- if($length >= $this->strlen(preg_replace('/[\s]+/', '', strip_tags($text))))
189
- return $text;
190
-
191
- $in_tag = false;
192
- $n_chars = 0;
193
- for($i = 0; $n_chars < $length || $in_tag; $i++)
194
- {
195
- $char = $this->substr($text, $i, 1);
196
- // Is the character worth counting (ie. not part of an HTML tag)
197
- if($char == '<')
198
- {
199
- $in_tag = true;
200
- if(($pos = strpos($text, '>', $i)) !== false)
201
- {
202
- $i = $pos - 1;
203
- continue;
204
- }
205
- }
206
- elseif($char == '>')
207
- $in_tag = false;
208
- elseif(!$in_tag && '' != trim($char))
209
- $n_chars++;
210
-
211
- // Prevent eternal loops (this could happen with incomplete HTML tags)
212
- if($i >= $this->strlen($text) - 1)
213
- break;
214
- }
215
- $text = $this->substr($text, 0, $i);
216
- }
217
-
218
- $text = trim(force_balance_tags($text));
219
-
220
- // New filter in WP2.9, seems unnecessary for now
221
- //$ellipsis = apply_filters('excerpt_more', $ellipsis);
222
-
223
- // Read more
224
- if(1 == $add_link)
225
- {
226
- $ellipsis = $ellipsis . sprintf(' <a href="%s" class="read_more">%s</a>', get_permalink(), $read_more);
227
- }
228
-
229
- // Adding the ellipsis
230
- if(($pos = strpos($text, '</p>', strlen($text) - 7)) !== false)
231
- {
232
- // Stay inside the last paragraph (if it's in the last 6 characters)
233
- $text = substr_replace($text, $ellipsis, $pos, 0);
234
- }
235
- else
236
- {
237
- // If <p> is an allowed tag,
238
- // wrap the ellipsis for consistency with excerpt markup
239
- if(in_array('_all', $allowed_tags) || in_array('p', $allowed_tags))
240
- $ellipsis = '<p>' . $ellipsis . '</p>';
241
-
242
- $text = $text . $ellipsis;
243
  }
 
 
 
244
  }
245
-
246
- return $text;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  }
248
-
249
  function install()
250
  {
251
- add_option($this->name . '_length', 40);
252
- add_option($this->name . '_use_words', 1);
253
- add_option($this->name . '_no_custom', 0);
254
- add_option($this->name . '_no_shortcode', 1);
255
- add_option($this->name . '_ellipsis', '&hellip;');
256
- add_option($this->name . '_read_more', 'Read the rest');
257
- add_option($this->name . '_add_link', 0);
258
- add_option($this->name . '_allowed_tags', $this->options_basic_tags);
259
-
260
- //$this->load_options();
 
 
261
  }
262
-
263
  function uninstall()
264
  {
265
- // Nothing to do (note: deactivation hook is also disabled)
266
  }
267
-
268
  function load_options()
269
  {
270
- $this->default_options = array(
271
- 'length' => get_option($this->name . '_length'),
272
- 'use_words' => get_option($this->name . '_use_words'),
273
- 'no_custom' => get_option($this->name . '_no_custom'),
274
- 'no_shortcode' => get_option($this->name . '_no_shortcode'),
275
- 'ellipsis' => get_option($this->name . '_ellipsis'),
276
- 'read_more' => get_option($this->name . '_read_more'),
277
- 'add_link' => get_option($this->name . '_add_link'),
278
- 'allowed_tags' => get_option($this->name . '_allowed_tags')
279
- );
 
 
280
  }
281
-
282
-
283
  function update_options()
284
  {
285
- $length = (int) $_POST[$this->name . '_length'];
286
- $use_words = ('on' == $_POST[$this->name . '_use_words']) ? 1 : 0 ;
287
- $no_custom = ('on' == $_POST[$this->name . '_no_custom']) ? 1 : 0 ;
288
- $no_shortcode = ('on' == $_POST[$this->name . '_no_shortcode']) ? 1 : 0 ;
289
- $add_link = ('on' == $_POST[$this->name . '_add_link']) ? 1 : 0 ;
290
-
291
- $ellipsis = (get_magic_quotes_gpc() == 1) ? stripslashes($_POST[$this->name . '_ellipsis']) : $_POST[$this->name . '_ellipsis'];
292
- $read_more = (get_magic_quotes_gpc() == 1) ? stripslashes($_POST[$this->name . '_read_more']) : $_POST[$this->name . '_read_more'];
293
-
294
- $allowed_tags = array_unique((array) $_POST[$this->name . '_allowed_tags']);
295
-
296
- update_option($this->name . '_length', $length);
297
- update_option($this->name . '_use_words', $use_words);
298
- update_option($this->name . '_no_custom', $no_custom);
299
- update_option($this->name . '_no_shortcode', $no_shortcode);
300
- update_option($this->name . '_ellipsis', $ellipsis);
301
- update_option($this->name . '_read_more', $read_more);
302
- update_option($this->name . '_add_link', $add_link);
303
- update_option($this->name . '_allowed_tags', $allowed_tags);
304
-
305
- $this->load_options();
306
- ?>
 
 
 
 
307
  <div id="message" class="updated fade"><p>Options saved.</p></div>
308
  <?php
309
  }
310
-
311
  function page_options()
312
  {
313
- if ('POST' == $_SERVER['REQUEST_METHOD'])
314
- {
315
- check_admin_referer($this->name . '_update_options');
316
- $this->update_options();
317
- }
318
-
319
- extract($this->default_options, EXTR_SKIP);
320
-
321
- // HTML entities for textbox
322
- $ellipsis = htmlentities($ellipsis);
323
- $read_more = htmlentities($read_more);
324
-
325
- // Basic tags + enabled tags
326
- $tag_list = $this->set_union($this->options_basic_tags, $allowed_tags);
327
- sort($tag_list);
328
- $tag_cols = 5;
329
  ?>
330
  <div class="wrap">
331
  <div id="icon-options-general" class="icon32"><br /></div>
332
- <h2><?php _e("Advanced Excerpt Options", $this->text_domain); ?></h2>
 
 
333
  <form method="post" action="">
334
  <?php
335
- if ( function_exists('wp_nonce_field') )
336
- wp_nonce_field($this->name . '_update_options'); ?>
 
337
 
338
  <table class="form-table">
339
  <tr valign="top">
340
- <th scope="row"><label for="<?php echo $this->name ?>_length"><?php _e("Excerpt Length:", $this->text_domain); ?></label></th>
 
341
  <td>
342
- <input name="<?php echo $this->name ?>_length" type="text" id="<?php echo $this->name ?>_length" value="<?php echo $length; ?>" size="2" />
343
- <input name="<?php echo $this->name ?>_use_words" type="checkbox" id="<?php echo $this->name ?>_use_words" value="on" <?php echo (1 == $use_words) ? 'checked="checked" ': ''; ?>/> <?php _e("Use words?", $this->text_domain); ?>
 
 
 
 
 
344
  </td>
345
  </tr>
346
  <tr valign="top">
347
- <th scope="row"><label for="<?php echo $this->name ?>_ellipsis"><?php _e("Ellipsis:", $this->text_domain); ?></label></th>
 
348
  <td>
349
- <input name="<?php echo $this->name ?>_ellipsis" type="text" id="<?php echo $this->name ?>_ellipsis" value="<?php echo $ellipsis; ?>" size="5" /> <?php _e('(use <a href="http://www.w3schools.com/tags/ref_entities.asp">HTML entities</a>)', $this->text_domain); ?>
 
 
 
350
  <br />
351
  <?php _e("Will substitute the part of the post that is omitted in the excerpt.", $this->text_domain); ?>
352
  </td>
353
  </tr>
354
  <tr valign="top">
355
- <th scope="row"><label for="<?php echo $this->name ?>_read_more"><?php _e("&lsquo;Read-more&rsquo; Text:", $this->text_domain); ?></label></th>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  <td>
357
- <input name="<?php echo $this->name ?>_read_more" type="text" id="<?php echo $this->name ?>_read_more" value="<?php echo $read_more; ?>" />
358
- <input name="<?php echo $this->name ?>_add_link" type="checkbox" id="<?php echo $this->name ?>_add_link" value="on" <?php echo (1 == $add_link) ? 'checked="checked" ': ''; ?>/> <?php _e("Add link to excerpt", $this->text_domain); ?>
 
 
 
 
359
  </td>
360
  </tr>
361
  <tr valign="top">
362
- <th scope="row"><label for="<?php echo $this->name ?>_no_custom"><?php _e("No Custom Excerpts:", $this->text_domain); ?></label></th>
 
363
  <td>
364
- <input name="<?php echo $this->name ?>_no_custom" type="checkbox" id="<?php echo $this->name ?>_no_custom" value="on" <?php echo (1 == $no_custom) ? 'checked="checked" ': ''; ?>/>
365
- <?php _e("Generate excerpts even if a post has a custom excerpt attached.", $this->text_domain); ?>
 
 
366
  </td>
367
  </tr>
368
  <tr valign="top">
369
- <th scope="row"><label for="<?php echo $this->name ?>_no_shortcode"><?php _e("Strip Shortcodes:", $this->text_domain); ?></label></th>
 
370
  <td>
371
- <input name="<?php echo $this->name ?>_no_shortcode" type="checkbox" id="<?php echo $this->name ?>_no_shortcode" value="on" <?php echo (1 == $no_shortcode) ? 'checked="checked" ': ''; ?>/>
372
- <?php _e("Remove shortcodes from the excerpt. <em>(recommended)</em>", $this->text_domain); ?>
 
 
373
  </td>
374
  </tr>
375
  <tr valign="top">
376
  <th scope="row"><?php _e("Keep Markup:", $this->text_domain); ?></th>
377
  <td>
378
- <table id="<?php echo $this->name ?>_tags_table">
379
  <tr>
380
  <td colspan="<?php echo $tag_cols; ?>">
381
- <input name="<?php echo $this->name ?>_allowed_tags[]" type="checkbox" value="_all" <?php echo (in_array('_all', $allowed_tags)) ? 'checked="checked" ': ''; ?>/>
382
- <?php _e("Don't remove any markup", $this->text_domain); ?>
 
383
  </td>
384
  </tr>
385
  <?php
386
- $i = 0;
387
- foreach($tag_list as $tag) :
388
- if($tag == '_all') continue;
389
- if(0 == $i % $tag_cols) : ?>
 
 
390
  <tr>
391
  <?php
392
- endif;
393
- $i++;
394
  ?>
395
  <td>
396
- <input name="<?php echo $this->name ?>_allowed_tags[]" type="checkbox" value="<?php echo $tag; ?>" <?php echo (in_array($tag, $allowed_tags)) ? 'checked="checked" ': ''; ?>/>
 
 
397
  <code><?php echo $tag; ?></code>
398
  </td>
399
  <?php
400
- if(0 == $i % $tag_cols) : $i = 0; ?>
 
 
 
 
 
 
 
401
  </tr>
402
  <?php
403
- endif;
404
- endforeach;
405
- if(0 != $i % $tag_cols) : ?><td colspan="<?php echo ($tag_cols - $i); ?>">&nbsp;</td></tr><?php endif;?>
406
  </table>
407
- <a href="" id="<?php echo $this->name ?>_select_all">Select all</a> / <a href="" id="<?php echo $this->name ?>_select_none">Select none</a><br />
 
408
  More tags:
409
- <select name="<?php echo $this->name ?>_more_tags" id="<?php echo $this->name ?>_more_tags">
410
  <?php
411
- foreach($this->options_body_tags as $tag) :
412
  ?>
413
  <option value="<?php echo $tag; ?>"><?php echo $tag; ?></option>
414
  <?php
415
- endforeach;
416
  ?>
417
  </select>
418
- <input type="button" name="<?php echo $this->name ?>_add_tag" id="<?php echo $this->name ?>_add_tag" class="button" value="Add tag" />
419
  </td>
420
  </tr>
421
  </table>
422
- <p class="submit"><input type="submit" name="Submit" class="button-primary" value="<?php _e("Save Changes", $this->text_domain); ?>" /></p>
 
423
  </form>
424
  </div>
425
  <?php
426
  }
427
-
428
  function page_script()
429
  {
430
- wp_enqueue_script($this->name . '_script', WP_PLUGIN_URL . '/advanced-excerpt/advanced-excerpt.js', array('jquery'));
 
 
431
  }
432
-
433
  function add_pages()
434
  {
435
- $options_page = add_options_page(__("Advanced Excerpt Options", $this->text_domain), __("Excerpt", $this->text_domain), 'manage_options', 'options-' . $this->name, array(&$this, 'page_options'));
436
-
437
- // Scripts
438
- add_action('admin_print_scripts-' . $options_page, array(&$this, 'page_script'));
 
 
 
 
 
 
439
  }
440
-
441
  // Careful multibyte support (fallback to normal functions if not available)
442
-
443
  function substr($str, $start, $length = null)
444
  {
445
- $length = (is_null($length)) ? $this->strlen($str) : $length;
446
- if($this->mb)
447
- return mb_substr($str, $start, $length, $this->charset);
448
- else
449
- return substr($str, $start, $length);
450
  }
451
-
452
  function strlen($str)
453
  {
454
- if($this->mb)
455
- return mb_strlen($str, $this->charset);
456
- else
457
- return strlen($str);
458
  }
459
-
460
  // Some utility functions
461
-
462
  function set_complement($a, $b)
463
  {
464
- $c = array_diff($a, $b);
465
- return array_unique($c);
466
  }
467
-
468
  function set_union($a, $b)
469
  {
470
- $c = array_merge($a, $b);
471
- return array_unique($c);
472
  }
473
- }
474
 
475
- $advancedexcerpt = new AdvancedExcerpt();
476
 
477
- // Do not use outside the Loop!
478
- function the_advanced_excerpt($args = '', $get = true)
479
- {
480
  global $advancedexcerpt;
481
-
482
  $r = wp_parse_args($args);
483
-
484
- if(isset($r['ellipsis']))
485
- $r['ellipsis'] = urldecode($r['ellipsis']);
486
-
487
  // TODO: Switch to 'allowed_tags' (compatibility code)
488
- if(isset($r['allow_tags']))
489
  {
490
- $r['allowed_tags'] = $r['allow_tags'];
491
- unset($r['allow_tags']);
492
  }
493
-
494
- if(isset($r['allowed_tags']))
495
- $r['allowed_tags'] = preg_split('/[\s,]+/', $r['allow_tags']);
496
-
497
- if(isset($r['exclude_tags']))
498
  {
499
- $r['exclude_tags'] = preg_split('/[\s,]+/', $r['exclude_tags']);
500
- // {all_tags} - {exclude_tags}
501
- $r['allowed_tags'] = $advancedexcerpt->set_complement($advancedexcerpt->options_body_tags, $r['exclude_tags']);
502
- unset($r['exclude_tags']);
503
  }
504
-
505
  // Set custom options (discard after use)
506
  $advancedexcerpt->custom_options = $r;
507
- if($get)
508
- echo get_the_excerpt();
509
  else
510
- the_excerpt();
511
  $advancedexcerpt->custom_options = null;
512
- }
513
- endif;
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
 
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;
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === Advanced Excerpt ===
2
  Contributors: basvd
3
  Tags: excerpt, advanced, post, posts, template, formatting
4
- Donate link: http://sparepencil.com/code/advanced-excerpt/
5
  Requires at least: 2.2
6
- Tested up to: 2.9
7
- Stable tag: 3.1
8
 
9
  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.
10
 
@@ -24,18 +24,13 @@ In addition to keeping HTML markup in the excerpt, the plugin also corrects HTML
24
 
25
  This plugin is also compatible with Shortcodes.
26
 
27
- Version 3.0 may not be backwards compatible. Check and re-apply the settings after you upgrade to make sure everything works correctly.
28
-
29
- Version 0.2.1 adds support for multibyte characters (e.g. Chinese and Japanese). This is slightly experimental, more details in the FAQ.
30
- Plugin translations are fully supported and language files are included for translation. The FAQ provides more info on this, also.
31
-
32
  == Installation ==
33
 
34
  After you've downloaded and extracted the files:
35
 
36
  1. Upload the complete `advanced-excerpt` folder to the `/wp-content/plugins/` directory
37
  2. Activate the plugin through the 'Plugins' menu in WordPress
38
- 3. Go to 'Excerpt' under the 'Options' tab and configure the plugin
39
 
40
 
41
  == Frequently Asked Questions ==
@@ -61,7 +56,7 @@ First of all, it should be noted that word-based excerpt length only works if yo
61
  PHP's support for multibyte characters is not perfect. The plugin provides support for these characters to the best of its ability, but there are no guarantees that everything will work.
62
  Your best bet is to use UTF-8 encoding (which WordPress uses by default). If you still encounter problems, check with your host if the *mbstring* PHP extension is enabled on your server.
63
 
64
- = Can I manually call the filter in my WP templates, for example? =
65
 
66
  The plugin automatically hooks on `the_excerpt()` function and uses the parameters specified in the options panel.
67
 
@@ -73,6 +68,8 @@ The following parameters can be set:
73
  * `use_words`, if set to `1`, the excerpt length will be in words; if set to `0`, characters will be used for the count
74
  * `no_custom`, if set to `1`, an excerpt will be generated even if the post has a custom excerpt; if set to `0`, the custom excerpt will be used
75
  * `no_shortcode`, if set to `1`, shortcodes are removed from the excerpt; if set to `0`, shortcodes will be parsed
 
 
76
  * `ellipsis`, the string that will substitute the omitted part of the post; if you want to use HTML entities in the string, use `%26` instead of the `&` prefix to avoid breaking the query
77
  * `read_more`, the text used in the read-more link
78
  * `add_link`, if set to `1`, the read-more link will be appended; if `0`, no link will be added
@@ -90,6 +87,11 @@ You can, however, consider to [start The Loop manually](http://codex.wordpress.o
90
 
91
  == Changelog ==
92
 
 
 
 
 
 
93
  = 3.1 =
94
 
95
  * Fix: A few bugs with custom and character-based excerpts
1
  === Advanced Excerpt ===
2
  Contributors: basvd
3
  Tags: excerpt, advanced, post, posts, template, formatting
4
+ Donate link: http://basvd.com/code/advanced-excerpt/
5
  Requires at least: 2.2
6
+ Tested up to: 3.2.1
7
+ Stable tag: 4.0
8
 
9
  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.
10
 
24
 
25
  This plugin is also compatible with Shortcodes.
26
 
 
 
 
 
 
27
  == Installation ==
28
 
29
  After you've downloaded and extracted the files:
30
 
31
  1. Upload the complete `advanced-excerpt` folder to the `/wp-content/plugins/` directory
32
  2. Activate the plugin through the 'Plugins' menu in WordPress
33
+ 3. Go to 'Excerpt' under the 'Settings' menu and configure the plugin
34
 
35
 
36
  == Frequently Asked Questions ==
56
  PHP's support for multibyte characters is not perfect. The plugin provides support for these characters to the best of its ability, but there are no guarantees that everything will work.
57
  Your best bet is to use UTF-8 encoding (which WordPress uses by default). If you still encounter problems, check with your host if the *mbstring* PHP extension is enabled on your server.
58
 
59
+ = Can I manually call the filter in my WP theme or plugin? =
60
 
61
  The plugin automatically hooks on `the_excerpt()` function and uses the parameters specified in the options panel.
62
 
68
  * `use_words`, if set to `1`, the excerpt length will be in words; if set to `0`, characters will be used for the count
69
  * `no_custom`, if set to `1`, an excerpt will be generated even if the post has a custom excerpt; if set to `0`, the custom excerpt will be used
70
  * `no_shortcode`, if set to `1`, shortcodes are removed from the excerpt; if set to `0`, shortcodes will be parsed
71
+ * `finish_word`, if set to `1`, the last word in the excerpt will not be cut off; if set to `0`, no effort is made to finish the word
72
+ * `finish_sentence`, if set to `1`, the last sentence in the excerpt will not be cut off; if set to `0`, no effort is made to include the full sentence
73
  * `ellipsis`, the string that will substitute the omitted part of the post; if you want to use HTML entities in the string, use `%26` instead of the `&` prefix to avoid breaking the query
74
  * `read_more`, the text used in the read-more link
75
  * `add_link`, if set to `1`, the read-more link will be appended; if `0`, no link will be added
87
 
88
  == Changelog ==
89
 
90
+ = 4.0 =
91
+ * Feature: Brand new parsing algorithm which should resolve some running time issues
92
+ * Feature: Options to finish a word or sentence before cutting the excerpt
93
+ * Fix: A few small bugs
94
+
95
  = 3.1 =
96
 
97
  * Fix: A few bugs with custom and character-based excerpts