Version Description
- Major release since 0.2.2 (also removed the
0.
prefix from the version number) - Feature: Shortcodes can be removed from the excerpt
- Feature: Virtually any HTML tag may now be stripped
- Feature: A read-more link with custom text can be added
- Fix: Word-based trimming speed improved
- Fix: Template tag function improved
- Fix: Improved ellipsis placement
Download this release
Release Info
Developer | basvd |
Plugin | Advanced Excerpt |
Version | 3.0 |
Comparing to | |
See all releases |
Code changes from version 0.2.2 to 3.0
- advanced-excerpt.js +70 -0
- advanced-excerpt.php +467 -300
- readme.txt +26 -6
advanced-excerpt.js
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Advanced Excerpt options script
|
2 |
+
jQuery(function($)
|
3 |
+
{
|
4 |
+
var plugin_prefix = 'advancedexcerpt';
|
5 |
+
var tag_cols = $('#' + plugin_prefix + '_tags_table tr:eq(1) td').length;
|
6 |
+
|
7 |
+
var tag_list = new Array();
|
8 |
+
$('#' + plugin_prefix + '_tags_table input').each(function(i, el)
|
9 |
+
{
|
10 |
+
tag_list.push($(el).val());
|
11 |
+
});
|
12 |
+
|
13 |
+
// Add a tag to the checkbox table
|
14 |
+
$('#' + plugin_prefix + '_add_tag').click(function(event)
|
15 |
+
{
|
16 |
+
event.preventDefault();
|
17 |
+
|
18 |
+
var tag = $('#' + plugin_prefix + '_more_tags option:selected').val();
|
19 |
+
|
20 |
+
// No duplicate tags in the table
|
21 |
+
if($.inArray(tag, tag_list) > -1)
|
22 |
+
{
|
23 |
+
return;
|
24 |
+
}
|
25 |
+
tag_list.push(tag);
|
26 |
+
|
27 |
+
var last_row = $('#' + plugin_prefix + '_tags_table tr:last-child');
|
28 |
+
var tag_count = last_row.find('input').length;
|
29 |
+
var tag_cell = $(
|
30 |
+
'<td>' +
|
31 |
+
'<input name="' + plugin_prefix + '_allowed_tags[]" type="checkbox" value="' + tag + '" checked="checked" />' +
|
32 |
+
'<code>' + tag + '</code>' +
|
33 |
+
'</td>'
|
34 |
+
);
|
35 |
+
|
36 |
+
if(tag_count < tag_cols)
|
37 |
+
{
|
38 |
+
// Add to last row
|
39 |
+
var span = last_row.find('td[colspan]');
|
40 |
+
if(span.attr('colspan') > 1)
|
41 |
+
{
|
42 |
+
span.attr('colspan', span.attr('colspan') - 1);
|
43 |
+
tag_cell.insertBefore(span);
|
44 |
+
}
|
45 |
+
else
|
46 |
+
{
|
47 |
+
span.replaceWith(tag_cell);
|
48 |
+
}
|
49 |
+
}
|
50 |
+
else
|
51 |
+
{
|
52 |
+
// New row
|
53 |
+
$('<tr><td colspan="' + (tag_cols - 1) + '"> </td></tr>').insertAfter(last_row).prepend(tag_cell);
|
54 |
+
}
|
55 |
+
});
|
56 |
+
|
57 |
+
// Check all boxes
|
58 |
+
$('#' + plugin_prefix + '_select_all').click(function(event)
|
59 |
+
{
|
60 |
+
event.preventDefault();
|
61 |
+
$('input[name="' + plugin_prefix + '_allowed_tags[]"]:gt(0)').attr('checked', 'checked');
|
62 |
+
});
|
63 |
+
|
64 |
+
// Uncheck all boxes
|
65 |
+
$('#' + plugin_prefix + '_select_none').click(function(event)
|
66 |
+
{
|
67 |
+
event.preventDefault();
|
68 |
+
$('input[name="' + plugin_prefix + '_allowed_tags[]"]:gt(0)').removeAttr('checked');
|
69 |
+
});
|
70 |
+
});
|
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: 0
|
7 |
Author: Bas van Doren
|
8 |
Author URI: http://sparepencil.com/
|
9 |
|
@@ -26,315 +26,482 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
26 |
if(!class_exists('AdvancedExcerpt')) :
|
27 |
class AdvancedExcerpt
|
28 |
{
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
// From the default wp_trim_excerpt():
|
92 |
-
// Some kind of precaution against malformed CDATA in RSS feeds I suppose
|
93 |
-
$text = str_replace(']]>', ']]>', $text);
|
94 |
-
$text = strip_tags($text, $allowed_tags);
|
95 |
-
|
96 |
-
if(1 == $use_words)
|
97 |
-
{
|
98 |
-
// Count words, not HTML tags
|
99 |
-
if($length > count(preg_split('/[\s]+/', strip_tags($text), -1)))
|
100 |
-
return $text;
|
101 |
-
|
102 |
-
// Now we start counting
|
103 |
-
$text_bits = preg_split('/([\s]+)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
|
104 |
-
$in_tag = false;
|
105 |
-
$n_words = 0;
|
106 |
-
$text = '';
|
107 |
-
foreach($text_bits as $chunk)
|
108 |
-
{
|
109 |
-
// Determine whether a tag is opened (and not immediately closed) in this chunk
|
110 |
-
if(0 < preg_match('/<[^>]*$/s', $chunk))
|
111 |
-
$in_tag = true;
|
112 |
-
elseif(0 < preg_match('/>[^<]*$/s', $chunk))
|
113 |
-
$in_tag = false;
|
114 |
-
|
115 |
-
// This should check if there is a word before the tag
|
116 |
-
// but it doesn't work very well
|
117 |
-
/*
|
118 |
-
if($in_tag && $this->substr($chunk, 0, 1) != '<')
|
119 |
-
$n_words++;
|
120 |
-
*/
|
121 |
-
|
122 |
-
// Is there a word?
|
123 |
-
if(!$in_tag && '' != trim($chunk) && $this->substr($chunk, -1, 1) != '>')
|
124 |
-
$n_words++;
|
125 |
-
|
126 |
-
$text .= $chunk;
|
127 |
-
|
128 |
-
if($n_words >= $length && !$in_tag)
|
129 |
-
break;
|
130 |
-
}
|
131 |
-
$text = $text . $ellipsis;
|
132 |
-
}
|
133 |
-
else
|
134 |
-
{
|
135 |
-
// Count characters, not whitespace, not those belonging to HTML tags
|
136 |
-
if($length > $this->strlen(strip_tags($text)))
|
137 |
-
return $text;
|
138 |
-
|
139 |
-
$in_tag = false;
|
140 |
-
$n_chars = 0;
|
141 |
-
for($i = 0; $n_chars < $length || $in_tag; $i++)
|
142 |
-
{
|
143 |
-
// Is the character worth counting (ie. not part of an HTML tag)
|
144 |
-
if($this->substr($text, $i, 1) == '<')
|
145 |
-
$in_tag = true;
|
146 |
-
elseif($this->substr($text, $i, 1) == '>')
|
147 |
-
$in_tag = false;
|
148 |
-
elseif(!$in_tag && '' != trim($this->substr($text, $i, 1)))
|
149 |
-
$n_chars++;
|
150 |
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
?>
|
200 |
<div class="wrap">
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
235 |
<?php
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
|
|
242 |
<?php
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
|
|
|
|
|
|
|
|
252 |
</div>
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
$c = array();
|
302 |
-
foreach($a as $t)
|
303 |
-
{
|
304 |
-
if(!in_array($t, $b))
|
305 |
-
$c[] = $t;
|
306 |
-
}
|
307 |
-
}
|
308 |
}
|
309 |
|
310 |
$advancedexcerpt = new AdvancedExcerpt();
|
311 |
|
312 |
-
|
|
|
313 |
{
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
339 |
}
|
340 |
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: 3.0
|
7 |
Author: Bas van Doren
|
8 |
Author URI: http://sparepencil.com/
|
9 |
|
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->custom_options, $this->default_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(']]>', ']]>', $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(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 |
+
// TODO: Improve this loop
|
196 |
+
|
197 |
+
$char = $this->substr($text, $i, 1);
|
198 |
+
// Is the character worth counting (ie. not part of an HTML tag)
|
199 |
+
if($char == '<')
|
200 |
+
$in_tag = true;
|
201 |
+
elseif($char == '>')
|
202 |
+
$in_tag = false;
|
203 |
+
elseif(!$in_tag && '' != trim($char))
|
204 |
+
$n_chars++;
|
205 |
+
|
206 |
+
// Prevent eternal loops (this could happen with incomplete HTML tags)
|
207 |
+
if($i >= $this->strlen($text) - 1)
|
208 |
+
break;
|
209 |
+
}
|
210 |
+
$text = $this->substr($text, 0, $i);
|
211 |
+
}
|
212 |
+
$text = force_balance_tags($text);
|
213 |
+
|
214 |
+
// New filter in WP2.9, seems unnecessary for now
|
215 |
+
//$ellipsis = apply_filters('excerpt_more', $ellipsis);
|
216 |
+
|
217 |
+
// Read more
|
218 |
+
if(1 == $add_link)
|
219 |
+
{
|
220 |
+
$ellipsis = $ellipsis . sprintf(' <a href="%s" class="read_more">%s</a>', get_permalink(), $read_more);
|
221 |
+
}
|
222 |
+
|
223 |
+
// Adding the ellipsis
|
224 |
+
if(($pos = strpos($text, '</p>', strlen($text) - 6)) !== false)
|
225 |
+
{
|
226 |
+
// Stay inside the last paragraph (if it's in the last 6 characters)
|
227 |
+
$text = substr_replace($text, $ellipsis, $pos, 0);
|
228 |
+
}
|
229 |
+
else
|
230 |
+
{
|
231 |
+
// If <p> is an allowed tag,
|
232 |
+
// wrap the ellipsis for consistency with excerpt markup
|
233 |
+
if(in_array('_all', $allowed_tags) || in_array('p', $allowed_tags))
|
234 |
+
$ellipsis = '<p>' . $ellipsis . '</p>';
|
235 |
+
|
236 |
+
$text = $text . $ellipsis;
|
237 |
+
}
|
238 |
+
}
|
239 |
+
|
240 |
+
return $text;
|
241 |
+
}
|
242 |
+
|
243 |
+
function install()
|
244 |
+
{
|
245 |
+
add_option($this->name . '_length', 40);
|
246 |
+
add_option($this->name . '_use_words', 1);
|
247 |
+
add_option($this->name . '_no_custom', 0);
|
248 |
+
add_option($this->name . '_no_shortcode', 1);
|
249 |
+
add_option($this->name . '_ellipsis', '…');
|
250 |
+
add_option($this->name . '_read_more', 'Read the rest');
|
251 |
+
add_option($this->name . '_add_link', 0);
|
252 |
+
add_option($this->name . '_allowed_tags', $this->options_basic_tags);
|
253 |
+
|
254 |
+
//$this->load_options();
|
255 |
+
}
|
256 |
+
|
257 |
+
function uninstall()
|
258 |
+
{
|
259 |
+
// Nothing to do (note: deactivation hook is also disabled)
|
260 |
+
}
|
261 |
+
|
262 |
+
function load_options()
|
263 |
+
{
|
264 |
+
$this->default_options = array(
|
265 |
+
'length' => get_option($this->name . '_length'),
|
266 |
+
'use_words' => get_option($this->name . '_use_words'),
|
267 |
+
'no_custom' => get_option($this->name . '_no_custom'),
|
268 |
+
'no_shortcode' => get_option($this->name . '_no_shortcode'),
|
269 |
+
'ellipsis' => get_option($this->name . '_ellipsis'),
|
270 |
+
'read_more' => get_option($this->name . '_read_more'),
|
271 |
+
'add_link' => get_option($this->name . '_add_link'),
|
272 |
+
'allowed_tags' => get_option($this->name . '_allowed_tags')
|
273 |
+
);
|
274 |
+
}
|
275 |
+
|
276 |
+
|
277 |
+
function update_options()
|
278 |
+
{
|
279 |
+
$length = (int) $_POST[$this->name . '_length'];
|
280 |
+
$use_words = ('on' == $_POST[$this->name . '_use_words']) ? 1 : 0 ;
|
281 |
+
$no_custom = ('on' == $_POST[$this->name . '_no_custom']) ? 1 : 0 ;
|
282 |
+
$no_shortcode = ('on' == $_POST[$this->name . '_no_shortcode']) ? 1 : 0 ;
|
283 |
+
$add_link = ('on' == $_POST[$this->name . '_add_link']) ? 1 : 0 ;
|
284 |
+
|
285 |
+
$ellipsis = (get_magic_quotes_gpc() == 1) ? stripslashes($_POST[$this->name . '_ellipsis']) : $_POST[$this->name . '_ellipsis'];
|
286 |
+
$read_more = (get_magic_quotes_gpc() == 1) ? stripslashes($_POST[$this->name . '_read_more']) : $_POST[$this->name . '_read_more'];
|
287 |
+
|
288 |
+
$allowed_tags = array_unique((array) $_POST[$this->name . '_allowed_tags']);
|
289 |
+
|
290 |
+
update_option($this->name . '_length', $length);
|
291 |
+
update_option($this->name . '_use_words', $use_words);
|
292 |
+
update_option($this->name . '_no_custom', $no_custom);
|
293 |
+
update_option($this->name . '_no_shortcode', $no_shortcode);
|
294 |
+
update_option($this->name . '_ellipsis', $ellipsis);
|
295 |
+
update_option($this->name . '_read_more', $read_more);
|
296 |
+
update_option($this->name . '_add_link', $add_link);
|
297 |
+
update_option($this->name . '_allowed_tags', $allowed_tags);
|
298 |
+
|
299 |
+
$this->load_options();
|
300 |
+
?>
|
301 |
+
<div id="message" class="updated fade"><p>Options saved.</p></div>
|
302 |
+
<?php
|
303 |
+
}
|
304 |
+
|
305 |
+
function page_options()
|
306 |
+
{
|
307 |
+
if ('POST' == $_SERVER['REQUEST_METHOD'])
|
308 |
+
{
|
309 |
+
check_admin_referer($this->name . '_update_options');
|
310 |
+
$this->update_options();
|
311 |
+
}
|
312 |
+
|
313 |
+
extract($this->default_options, EXTR_SKIP);
|
314 |
+
|
315 |
+
// HTML entities for textbox
|
316 |
+
$ellipsis = htmlentities($ellipsis);
|
317 |
+
$read_more = htmlentities($read_more);
|
318 |
+
|
319 |
+
// Basic tags + enabled tags
|
320 |
+
$tag_list = $this->set_union($this->options_basic_tags, $allowed_tags);
|
321 |
+
sort($tag_list);
|
322 |
+
$tag_cols = 5;
|
323 |
?>
|
324 |
<div class="wrap">
|
325 |
+
<div id="icon-options-general" class="icon32"><br /></div>
|
326 |
+
<h2><?php _e("Advanced Excerpt Options", $this->text_domain); ?></h2>
|
327 |
+
<form method="post" action="">
|
328 |
+
<?php
|
329 |
+
if ( function_exists('wp_nonce_field') )
|
330 |
+
wp_nonce_field($this->name . '_update_options'); ?>
|
331 |
+
|
332 |
+
<table class="form-table">
|
333 |
+
<tr valign="top">
|
334 |
+
<th scope="row"><label for="<?php echo $this->name ?>_length"><?php _e("Excerpt Length:", $this->text_domain); ?></label></th>
|
335 |
+
<td>
|
336 |
+
<input name="<?php echo $this->name ?>_length" type="text" id="<?php echo $this->name ?>_length" value="<?php echo $length; ?>" size="2" />
|
337 |
+
<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); ?>
|
338 |
+
</td>
|
339 |
+
</tr>
|
340 |
+
<tr valign="top">
|
341 |
+
<th scope="row"><label for="<?php echo $this->name ?>_ellipsis"><?php _e("Ellipsis:", $this->text_domain); ?></label></th>
|
342 |
+
<td>
|
343 |
+
<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); ?>
|
344 |
+
<br />
|
345 |
+
<?php _e("Will substitute the part of the post that is omitted in the excerpt.", $this->text_domain); ?>
|
346 |
+
</td>
|
347 |
+
</tr>
|
348 |
+
<tr valign="top">
|
349 |
+
<th scope="row"><label for="<?php echo $this->name ?>_read_more"><?php _e("‘Read-more’ Text:", $this->text_domain); ?></label></th>
|
350 |
+
<td>
|
351 |
+
<input name="<?php echo $this->name ?>_read_more" type="text" id="<?php echo $this->name ?>_read_more" value="<?php echo $read_more; ?>" />
|
352 |
+
<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); ?>
|
353 |
+
</td>
|
354 |
+
</tr>
|
355 |
+
<tr valign="top">
|
356 |
+
<th scope="row"><label for="<?php echo $this->name ?>_no_custom"><?php _e("No Custom Excerpts:", $this->text_domain); ?></label></th>
|
357 |
+
<td>
|
358 |
+
<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" ': ''; ?>/>
|
359 |
+
<?php _e("Generate excerpts even if a post has a custom excerpt attached.", $this->text_domain); ?>
|
360 |
+
</td>
|
361 |
+
</tr>
|
362 |
+
<tr valign="top">
|
363 |
+
<th scope="row"><label for="<?php echo $this->name ?>_no_shortcode"><?php _e("Strip Shortcodes:", $this->text_domain); ?></label></th>
|
364 |
+
<td>
|
365 |
+
<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" ': ''; ?>/>
|
366 |
+
<?php _e("Remove shortcodes from the excerpt. <em>(recommended)</em>", $this->text_domain); ?>
|
367 |
+
</td>
|
368 |
+
</tr>
|
369 |
+
<tr valign="top">
|
370 |
+
<th scope="row"><?php _e("Keep Markup:", $this->text_domain); ?></th>
|
371 |
+
<td>
|
372 |
+
<table id="<?php echo $this->name ?>_tags_table">
|
373 |
+
<tr>
|
374 |
+
<td colspan="<?php echo $tag_cols; ?>">
|
375 |
+
<input name="<?php echo $this->name ?>_allowed_tags[]" type="checkbox" value="_all" <?php echo (in_array('_all', $allowed_tags)) ? 'checked="checked" ': ''; ?>/>
|
376 |
+
<?php _e("Don't remove any markup", $this->text_domain); ?>
|
377 |
+
</td>
|
378 |
+
</tr>
|
379 |
+
<?php
|
380 |
+
$i = 0;
|
381 |
+
foreach($tag_list as $tag) :
|
382 |
+
if($tag == '_all') continue;
|
383 |
+
if(0 == $i % $tag_cols) : ?>
|
384 |
+
<tr>
|
385 |
+
<?php
|
386 |
+
endif;
|
387 |
+
$i++;
|
388 |
+
?>
|
389 |
+
<td>
|
390 |
+
<input name="<?php echo $this->name ?>_allowed_tags[]" type="checkbox" value="<?php echo $tag; ?>" <?php echo (in_array($tag, $allowed_tags)) ? 'checked="checked" ': ''; ?>/>
|
391 |
+
<code><?php echo $tag; ?></code>
|
392 |
+
</td>
|
393 |
+
<?php
|
394 |
+
if(0 == $i % $tag_cols) : $i = 0; ?>
|
395 |
+
</tr>
|
396 |
<?php
|
397 |
+
endif;
|
398 |
+
endforeach;
|
399 |
+
if(0 != $i % $tag_cols) : ?><td colspan="<?php echo ($tag_cols - $i); ?>"> </td></tr><?php endif;?>
|
400 |
+
</table>
|
401 |
+
<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 />
|
402 |
+
More tags:
|
403 |
+
<select name="<?php echo $this->name ?>_more_tags" id="<?php echo $this->name ?>_more_tags">
|
404 |
<?php
|
405 |
+
foreach($this->options_body_tags as $tag) :
|
406 |
+
?>
|
407 |
+
<option value="<?php echo $tag; ?>"><?php echo $tag; ?></option>
|
408 |
+
<?php
|
409 |
+
endforeach;
|
410 |
+
?>
|
411 |
+
</select>
|
412 |
+
<input type="button" name="<?php echo $this->name ?>_add_tag" id="<?php echo $this->name ?>_add_tag" class="button" value="Add tag" />
|
413 |
+
</td>
|
414 |
+
</tr>
|
415 |
+
</table>
|
416 |
+
<p class="submit"><input type="submit" name="Submit" class="button-primary" value="<?php _e("Save Changes", $this->text_domain); ?>" /></p>
|
417 |
+
</form>
|
418 |
</div>
|
419 |
+
<?php
|
420 |
+
}
|
421 |
+
|
422 |
+
function page_script()
|
423 |
+
{
|
424 |
+
wp_enqueue_script($this->name . '_script', WP_PLUGIN_URL . '/advanced-excerpt/advanced-excerpt.js', array('jquery'));
|
425 |
+
}
|
426 |
+
|
427 |
+
function add_pages()
|
428 |
+
{
|
429 |
+
$options_page = add_options_page(__("Advanced Excerpt Options", $this->text_domain), __("Excerpt", $this->text_domain), 'manage_options', 'options-' . $this->name, array(&$this, 'page_options'));
|
430 |
+
|
431 |
+
// Scripts
|
432 |
+
add_action('admin_print_scripts-' . $options_page, array(&$this, 'page_script'));
|
433 |
+
}
|
434 |
+
|
435 |
+
// Careful multibyte support (fallback to normal functions if not available)
|
436 |
+
|
437 |
+
function substr($str, $start, $length = null)
|
438 |
+
{
|
439 |
+
$length = (is_null($length)) ? $this->strlen($str) : $length;
|
440 |
+
if($this->mb)
|
441 |
+
return mb_substr($str, $start, $length, $this->charset);
|
442 |
+
else
|
443 |
+
return substr($str, $start, $length);
|
444 |
+
}
|
445 |
+
|
446 |
+
function strlen($str)
|
447 |
+
{
|
448 |
+
if($this->mb)
|
449 |
+
return mb_strlen($str, $this->charset);
|
450 |
+
else
|
451 |
+
return strlen($str);
|
452 |
+
}
|
453 |
+
|
454 |
+
// Some utility functions
|
455 |
+
|
456 |
+
function set_complement($a, $b)
|
457 |
+
{
|
458 |
+
$c = array_diff($a, $b);
|
459 |
+
return array_unique($c);
|
460 |
+
}
|
461 |
+
|
462 |
+
function set_union($a, $b)
|
463 |
+
{
|
464 |
+
$c = array_merge($a, $b);
|
465 |
+
return array_unique($c);
|
466 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
467 |
}
|
468 |
|
469 |
$advancedexcerpt = new AdvancedExcerpt();
|
470 |
|
471 |
+
// Do not use outside the Loop!
|
472 |
+
function the_advanced_excerpt($args = '', $get = true)
|
473 |
{
|
474 |
+
global $advancedexcerpt;
|
475 |
+
|
476 |
+
$r = wp_parse_args($args);
|
477 |
+
|
478 |
+
if(isset($r['ellipsis']))
|
479 |
+
$r['ellipsis'] = urldecode($r['ellipsis']);
|
480 |
+
|
481 |
+
// TODO: Switch to 'allowed_tags' (compatibility code)
|
482 |
+
if(isset($r['allow_tags']))
|
483 |
+
{
|
484 |
+
$r['allowed_tags'] = $r['allow_tags'];
|
485 |
+
unset($r['allow_tags']);
|
486 |
+
}
|
487 |
+
|
488 |
+
if(isset($r['allowed_tags']))
|
489 |
+
$r['allowed_tags'] = preg_split('/[\s,]+/', $r['allow_tags']);
|
490 |
+
|
491 |
+
if(isset($r['exclude_tags']))
|
492 |
+
{
|
493 |
+
$r['exclude_tags'] = preg_split('/[\s,]+/', $r['exclude_tags']);
|
494 |
+
// {all_tags} - {exclude_tags}
|
495 |
+
$r['allowed_tags'] = $advancedexcerpt->set_complement($advancedexcerpt->options_body_tags, $r['exclude_tags']);
|
496 |
+
unset($r['exclude_tags']);
|
497 |
+
}
|
498 |
+
|
499 |
+
// Set custom options (discard after use)
|
500 |
+
$advancedexcerpt->custom_options = $r;
|
501 |
+
if($get)
|
502 |
+
echo get_the_excerpt();
|
503 |
+
else
|
504 |
+
the_excerpt();
|
505 |
+
$advancedexcerpt->custom_options = null;
|
506 |
}
|
507 |
endif;
|
readme.txt
CHANGED
@@ -3,8 +3,8 @@ 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.
|
7 |
-
Stable tag:
|
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 |
|
@@ -15,12 +15,17 @@ This plugin adds several improvements to WordPress' default way of creating exce
|
|
15 |
1. It can keep HTML markup in the excerpt (and you get to choose which tags are included)
|
16 |
2. It trims the excerpt to a given length using either character count or word count
|
17 |
3. You can customise the excerpt length and the ellipsis character that will be used when trimming
|
|
|
18 |
4. The excerpt length is *real* (everything belonging to HTML tags is not counted)
|
19 |
-
5.
|
20 |
6. Theme developers can use `the_advanced_excerpt()` for even more control (see the FAQ)
|
21 |
|
22 |
In addition to keeping HTML markup in the excerpt, the plugin also corrects HTML that might have been broken due to the trimming process.
|
23 |
|
|
|
|
|
|
|
|
|
24 |
Version 0.2.1 adds support for multibyte characters (eg. Chinese and Japanese). This is slightly experimental, more details in the FAQ.
|
25 |
Plugin translations are fully supported and language files are included for translation. The FAQ provides more info on this, also.
|
26 |
|
@@ -41,7 +46,7 @@ The default excerpt created by WordPress removes all HTML. If your theme uses `t
|
|
41 |
|
42 |
= Does it work for WordPress version x.x.x? =
|
43 |
|
44 |
-
I haven't had the chance to test the plugin on many versions of WordPress. It has been tested on 2.
|
45 |
|
46 |
= Is this plugin available in my language? / How do I translate this plugin? =
|
47 |
|
@@ -67,9 +72,12 @@ The following parameters can be set:
|
|
67 |
* `length`, an integer that determines the length of the excerpt
|
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 |
* `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
|
71 |
-
* `
|
72 |
-
* `
|
|
|
|
|
73 |
|
74 |
A custom, advanced excerpt call could look like this:
|
75 |
|
@@ -79,3 +87,15 @@ A custom, advanced excerpt call could look like this:
|
|
79 |
|
80 |
No, this plugin fetches the post from The Loop and there is currently no way to pass a post ID or anything custom of that kind to it.
|
81 |
You can, however, consider to [start The Loop manually](http://codex.wordpress.org/The_Loop#Multiple_Loops).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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: 0.2.2
|
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 |
|
15 |
1. It can keep HTML markup in the excerpt (and you get to choose which tags are included)
|
16 |
2. It trims the excerpt to a given length using either character count or word count
|
17 |
3. You can customise the excerpt length and the ellipsis character that will be used when trimming
|
18 |
+
4. A read-more link can be added automatically
|
19 |
4. The excerpt length is *real* (everything belonging to HTML tags is not counted)
|
20 |
+
5. Can ignore custom excerpts and use the generated one instead
|
21 |
6. Theme developers can use `the_advanced_excerpt()` for even more control (see the FAQ)
|
22 |
|
23 |
In addition to keeping HTML markup in the excerpt, the plugin also corrects HTML that might have been broken due to the trimming process.
|
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 (eg. 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 |
|
46 |
|
47 |
= Does it work for WordPress version x.x.x? =
|
48 |
|
49 |
+
I haven't had the chance to test the plugin on many versions of WordPress. It has recently been tested on 2.9, but it might work on other versions, too. You can safely try it yourself, because the plugin is unlikely to break anything (it's only an output filter). Please let me know if you succesfully tested it on another version of WordPress.
|
50 |
|
51 |
= Is this plugin available in my language? / How do I translate this plugin? =
|
52 |
|
72 |
* `length`, an integer that determines the length of the excerpt
|
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
|
79 |
+
* `allowed_tags`, a comma-separated list of HTML tags that are allowed in the excerpt. Entering `_all` will preserve all tags.
|
80 |
+
* `exclude_tags`, a comma-separated list of HTML tags that must be removed from the excerpt. Using this setting in combination with `allowed_tags` makes no sense
|
81 |
|
82 |
A custom, advanced excerpt call could look like this:
|
83 |
|
87 |
|
88 |
No, this plugin fetches the post from The Loop and there is currently no way to pass a post ID or anything custom of that kind to it.
|
89 |
You can, however, consider to [start The Loop manually](http://codex.wordpress.org/The_Loop#Multiple_Loops).
|
90 |
+
|
91 |
+
== Changelog ==
|
92 |
+
|
93 |
+
= 3.0 =
|
94 |
+
|
95 |
+
* Major release since 0.2.2 (also removed the `0.` prefix from the version number)
|
96 |
+
* Feature: Shortcodes can be removed from the excerpt
|
97 |
+
* Feature: Virtually any HTML tag may now be stripped
|
98 |
+
* Feature: A read-more link with custom text can be added
|
99 |
+
* Fix: Word-based trimming speed improved
|
100 |
+
* Fix: Template tag function improved
|
101 |
+
* Fix: Improved ellipsis placement
|