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? ergo
Lorem 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 per
skip to -> ** word 350 ->Vestibulum [example_shortcode]**ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Morbi lacinia molestie dui. Praesent blandit
skip 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