How do I insert a short tag after 350 words in post_content?
I have 700 posts mostly in excess of 1000 words each. I’m setting this up as a membership site. I’m using wishlist member and would like to protect all the content after the first 350 words. This requires placing a short tag after the 350th word and another short tag at the very end of the post.
Adding the shortcode at the end seems pretty straightforward. However, I canât figure out how to get the short code into the middle of post_content..
Iâve considered a couple different approaches including:
a. Trying to figure out a regular expression find and replace in dreamweaver working with the xml flat files
b. Trying to work with a query in mysql to query the post content field, count the words in that field, and then insert the shortcode tag there
c. Running some php script to achieve the result (something similar here, but not quite the same http://bacsoftwareconsulting.com/blog/index.php/wordpress-cat/how-to-create-a-variable-length-excerpt-in-wordpress-without-a-plugin/ )
d. Or some combination of the above or some more incisive simpler answer
As I am looking to make a permanent change to the data (and not do something on the fly with the theme each time an article loads up) the mysql option seems like the most appropriate way to do this.
This is not a find and replace in the simple context of looking for a specific string and replacing it, but instead counting to a certain position in post_content and then either
- adding a shortcode [example_shortcode] or
- copying the first portion of post_content up to the and including the 350th word, concatenating that with [example_shortcode], plus all remaining text after the 350th word, plus the closing [/example_shortcode]
Some of my research leads so far:
ergo a little more involved than
example of search and replace query
I suspect it may require the substring function more on substrings in mysql as in this example where substrings were used for pagination pagination link
SELECT SUBSTRING(post_content,1000,1000) FROM posts WHERE id=x
Visual example of my goal
How can I insert the first tag after say the first 350 words of a post and the last tag at the end of a post and repeat across all of our posts 700+ posts?
ergoLorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec
odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla
quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent
mauris. Fusce nec tellus sed augue semper porta. Mauris massa.
Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad
litora torquent perskip to -> ** word 350 ->Vestibulum [example_shortcode]**ante ipsum
primis in faucibus orci luctus et ultrices posuere cubilia Curae;
Morbi lacinia molestie dui. Praesent blanditskip to end of post Morbi lacinia molestie dui. Praesent blandit
dolor. Sed non quam. In vel mi sit amet augue congue elementum. Morbi
in ipsum si. [/example_shortcode]
For a variety of reasons, the path of simply writing a function to dynamically add the tags as opposed to updating the db has proven unworkable. The main reason is due to a deficiency (lack of options) in the Wishlist member api.
bottom line it doesn’t support using the php version of short tags. (note if I had it to do over again I probably would have gone with a different member plugin solution and not something so closed down as wishlist).
That said, the only viable option at this point is to simply update the db by inserting the shortags into the posts with the appropriate category. (This is what I asked here in the original question and thus far no solutions have been suggested, although the alternatives were plausible they were not workable in this situation).
So now, I’m half way back to the first step. How to do a mass update of almost all wordpress posts. Specifically all posts that are not in two categories.
There are 3 related suggestions that I have found to do mass updates in wordpress although they mostly perform updates on fields in wp_posts and not an update within the string of post_content.
-
See https://stackoverflow.com/questions/7917725/update-all-wordpress-posts
/*
Plugin Name: Example
Description: This is not just a plugin, it’s CODE..
Author:
*/
add_action(‘init’,’example_hide’);function example_hide(){
$my_posts = get_posts( array(‘post_type’ => ‘post’, ‘numberposts’ => 10 ) );
foreach ( $my_posts as $my_post ):
$my_post[‘post_content’] = ‘This is the updated content.’;
wp_update_post( $my_post );
endforeach;
}
-
Then see this example that only updates slugs (something far easier to do in mysql btw https://stackoverflow.com/questions/12707469/update-all-the-posts-at-once-nothing-want-to-add-or-delete-only-update-so-that-p
// get all posts
$posts = get_posts( array ( ‘numberposts’ => -1 ) );foreach ( $posts as $post )
{
// check the slug and run an update if necessary
$new_slug = sanitize_title( $post->post_title );
wp_update_post(
array (
‘ID’ => $post->ID,
‘post_name’ => $new_slug
)
);}
Then 3 and I think this has the most promise http://www.michaelbrentecklund.com/wordpress-mass-update-posts-mass-update-pages-custom-post-types/07-18-2012/
/**
* Mass Update Posts
*
* Mass update portions of posts, pages or custom post types.
* Get all posts from a specified post type, determine the
* status of the post, choose a portion of the post structure,
* search for a string value associated with the specified key,
* specify a replacement string, and choose whether you want to
* search for a partial or exact match.
*
* @param string $post_type Post type to fetch posts from.
* @param string $post_status Status of the post (eg. publish, private).
* @param string $key Portion of the post (eg. post_status, post_type).
* @param string $find Value of the portion of post (eg. publish, page).
* @param string $replace String to replace value with. (eg. private, post).
* @param string $match_type Accepts 'partial' or 'exact' match.
**/
function mass_update_posts($post_type, $post_status, $key, $find, $replace, $match_type){
$args = array(
'numberposts' => -1,
'post_type' => $post_type,
'post_status' => $post_status
);
$posts = get_posts($args);
$return = '';
for($i = 0; $i < count($posts); $i++){
// Check if the portion of the post exists.
if(array_key_exists($key, $posts[$i])){
if($match_type == 'exact'){
// Check if the portion of the post matches exact search.
if($posts[$i]->$key == $find){
$action = 'update';
} else{
$action = 'return';
}
} elseif($match_type == 'partial'){
// Check if the portion of the post matches partial search.
$search = strpos($posts[$i]->$key, $find);
if($search !== false){
$action = 'update';
} else{
$action = 'return';
}
} else{
$return .= '<p><strong>Error!</strong> Specify either "partial" or "exact" match.</p>';
}
// Found the search query. Carry on.
if($action == 'update'){
// Set the portion of the post to be updated.
$update['ID'] = $posts[$i]->ID;
$update[$key] = $replace;
// Update the portion of the post.
$updated = wp_update_post($update);
// Check if update passes/fails.
if($updated){
//array_push($replaced, array($posts[$i]->$key => str_replace($find, $replace, $posts[$i]->$key)));
$replaced[][$posts[$i]->$key] = str_replace($find, $replace, $posts[$i]->$key);
} else{
$return .= '<p><strong>Error!</strong> Key: '.$key.' was found, however, failed to update the post: <strong>#'.$posts[$i]->ID.'</strong> <em>'.$posts[$i]->post_title.'</em>.</p>';
}
} elseif($action == 'return'){
$return .= '<p><strong>Error!</strong> Could not find "'.$find.'" in key: <strong>'.$key.'</strong>.</p>';
}
} else{
$return .= '<p><strong>Error!</strong> Invalid key specified.</p>';
}
}
// Posts were updated. Output the result.
if(!empty($replaced)){
$return .= '<p><strong>Success!</strong> Updated '.count($replaced).' posts.</p>';
foreach($replaced as $key => $value){
foreach($value as $old => $new){
$return .= '<p><strong>Old:</strong> <em>'.$old.'</em> <span style="padding: 0px 24px;">=></span><br /> <strong>New:</strong> '.$new.'</p>';
}
}
}
echo $return;
}
I think that a combination of that last function combined with the evolving content limit function as initially conceived below by @userabuser and modestly amended by myself, might ultimately prove to be the solution…
//* Limited Content
add_filter('the_content', 'limited_content', 11);
function post_is_in_descendant_category( $cats, $_post = null ) {
foreach ( (array) $cats as $cat ) {
// get_term_children() accepts integer ID only
$descendants = get_term_children( (int) $cat, 'category' );
if ( $descendants && in_category( $descendants, $_post ) )
return true;
}
return false;
}
add_filter('the_content', 'do_shortcode', 11); // From shortcodes.php
function limited_content($content) {
$limit = 350; //word limit for non-member/non-subscriber
$words = str_word_count($content);
$user_id = get_current_user_id();
//if it is not in either category we have to test membership
if ( in_category( array('2497','2811')) /*|| post_is_in_descendant_category(array('2497','2811')) */ ) {
return $content;
//check if the user is part of the "member" role if so, return the full content
} elseif ( user_can($user_id, 'member') ) {
return $content;
//otherwise for non members return 350 of the content
} elseif ( $words >= $limit) {
$contentteaser = explode(' ', $content, $limit);
array_pop($contentteaser);
$contentremainder = explode(' ', $content);
$contentremainder = array_slice($contentremainder, 349);
$contentremainder = implode(" ", $contentremainder);
$contentteaser = implode(" ", $contentteaser);
echo do_shortcode($contentteaser.' '.'[private_Silver level]'.$contentremainder.'[/private_Silver level]'. '... <a href="#">sign up to read more</a>');
//if there's less than 350 words, just return whatever exists
} else {
return $content;
}
} // end function Limited Content
The following will allow you to limit content to any word count you like based upon whether the user is part of a certain user role or not. The function can be improved and or made more efficient but at least it provides you the basis for filtering your content without having to physically edit the content or run any complex regular expressions.
Modified your edit so you can see the logic properly,
I anticipated that you would say something like this,
You might think that what you’re doing is a one-time change, but in fact its not!
Wrapping your content after 350 words in shortcodes for each of your 700 posts is potentially disasterous and here’s why.
The shortcode itself is a function or rather it refers to a function, so when any piece of content that contains this shortcode (in
the_content
) is called upon, it will execute its logic.You would need to wrap each of your 700 posts with a shortcode at the mark of 350 words, so that’s 700 shortcodes added to your database in the
post_content
– which although may be considered a small increase, is still an uneccesary increase in database size.What happens if for some reason you decide you want to now restrict content after 200 words instead of 350?
You’re in trouble. Can’t be done, you need to physically move the shortcode position.
Or what about the scenario where you decide that you want to BOTH restrict content after 200 words and 350 words depending upon membership levels?
You’re in trouble. Can’t be done, you need to physically move the shortcode position and modify the function that controls the shortcode to allow for conditional restrictions.
What a nightmare right?
This is where filters come into play and that’s why they exist and importantly this is why they are the most efficient way of handling and manipulating data without ever having to modify the original contents of your posts.
This is how your data should be treated. Instead of polluting your post content with logic, keep it pure and pristine, let filters do the crunching and heavy lifting for you to achieve your requirements.
This makes your content very flexible as it can be served in multiple ways to multiple people without you smacking your head against the wall. Ultimately, filters make your membership site powerful, shortcodes make your membership site weak. Filters allow you to scale your ideas, shortcodes box you into a corner.
Shortcodes are useful for repetitious tasks such as inserting verbose content into your post when writing, like maybe a bullet point image, or a special link or alert box that you might regularly use in the middle of your content or subscription links or… you get the gist. But when it comes to manipulating and controlling the output of content, shortcodes are a bad choice.
Instead of going to the database, what’s the problem in doing it directly on the front-end when displaying the text? It would be extremely simple, take a look at this example
substr
is not something you should use as it might break the html, but you get the idea 😉