Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: comments

  • All Comments By Email

    All Comments By Email

    By default, when you look at the list of comments on your WP Admin dashboard, you get a list like this:

    The Edit Comments page

    On that list, if you click on the IP address of the commenter, you go to all comments by that IP, but if you click on the email you get a mailto link, to let you email the person. That’s great, but as my friend, and fellow fansite runner, Liv pointed out, a lot of people post from multiple IP addresses these days, but only one email. What she wanted was for the icon that gave you the number of approved comments to link to that person’s approved comments.

    Me, being the sort to poke around, decided to see if that could be done. I already knew how to filter columns and tables, after all. What I learned was that there actually isn’t a filter for those columns, and the only way around it was to replace it. This means I was going to have to rebuild everything, and in doing so, I wanted that email address to be a link to the search. While annoying, it was pretty easy:

    <?php
    /*
    	Plugin Name: Easy Comment Search By Email
    	Description: Changes the default link for emails in the comment lists from mailto links to search results for that address.
    	Author: Mika A Epstein (ipstenu)
    	Author URI: https://halfelf.org
    
    Credit: https://wordpress.stackexchange.com/questions/83769/hook-to-edit-an-column-on-comments-screen
     */
    
    class ecsbePlugin {
    
    	public function __construct() {
    		add_action( 'admin_head' , array( &$this, 'column_style') );
    
    	add_filter( 'manage_edit-comments_columns', function($columns) {
    		unset($columns["author"]);
    		$columns_one = array_slice($columns,0,1);
    		$columns_two = array_slice($columns,1);
    		$columns_one["author-new"] = "Author";
    		$columns = $columns_one + $columns_two;
    		return $columns;
    	});
    
    	add_filter( 'manage_comments_custom_column', function($column, $column_id) {
    		
    		global $comment_status;
    				$author_url = get_comment_author_url();
    					if ( 'http://' == $author_url )
    							$author_url = '';
    					$author_url_display = preg_replace( '|http://(www\.)?|i', '', $author_url );
    					if ( strlen( $author_url_display ) > 50 )
    							$author_url_display = substr( $author_url_display, 0, 49 ) . '…';
    	
    					echo "<strong>"; comment_author(); echo '</strong><br />';
    					if ( !empty( $author_url ) )
    							echo "<a title='$author_url' href='$author_url'>$author_url_display</a><br />";
    	
    					if ( current_user_can( 'edit_posts' ) ) {
    						$author_email = get_comment_author_email();
    					
    							if ( !empty( $author_email ) ) {
    									echo '<a href="edit-comments.php?s=';
    									echo $author_email;
    									echo '&mode=detail';
    						   		if ( 'spam' == $comment_status )
    									echo '&comment_status=spam';
    								echo '">';
    								echo $author_email;
    								echo '</a><br />';
    							}
    							
    							echo '<a href="edit-comments.php?s=';
    							comment_author_IP();
    							echo '&mode=detail';
    							if ( 'spam' == $comment_status )
    									echo '&comment_status=spam';
    							echo '">';
    							comment_author_IP();
    							echo '</a>';
    					}
    		
    		}, 10, 2 );
    
    	}
    
     	public function column_style() {
    		echo '<style type="text/css">
    			#comments-form .fixed .column-author-new {
    				width: 20%;
    			}
    		 </style>';
    	}
    
    }
    
    new ecsbePlugin();
    

    The plugin needs a way better name, though, because this is just … bad. The array slice in the beginning was to remove the first item and replace it, without having to do a lot of overly wrought arguing with possible columns.

    That said, this is the sort of thing I may submit a patch for in core, since IPs change a heckuvalot more now, and while that’s a great way to find some serial-accounts and sockpuppets, sorting by email helps you find people being trolls. Both would be good, and I don’t think a lot of us email people. If anything, I’d change the author NAME to be a mailto link.

    Food for thought.

  • Mailbag: Pinging Pingbacks

    Mailbag: Pinging Pingbacks

    I run a fan site, and so does a friend of mine. Liv and I were chatting about wishlists in WordPress for fansites, and she mentioned this:

    I also like seeing who has linked to my site from other WP blogs because that helps me create fandom connections with other bloggers. I wish there was a quick button I could hit that would allow me to email those bloggers with a quick note of thanks for the connection

    When you’re running a fan website, communicating and connecting with those other sites is a killer feature. We network and that’s how we make our communities bloom, after all, since most of us can’t afford a budget for ‘real’ advertising, and it’s probably not entirely legal for us to do that anyway. So outside of spending days tracking everyone down, what about using the power of ping-backs for ourselves?

    Table Tennis

    I’m sure Liv has an unshakable confidence in my ability to code her things (and I love the requests she makes, they stretch my brain) but this one kicked my patootie a lot. Getting a list of pingbacks isn’t all that hard. There’s a plugin called Commenter Emails by Scott, which nicely lists all the email addresses used to make comments. Using that logic, it’s pretty easy to list all the pingbacks. I mean, hey, we can already do that!

    If you go to /wp-admin/edit-comments.php?s&comment_status=all&comment_type=pings you’ll see all your pings:

    All pings listed

    Just looking at that, however, made me notice a horrible problem. There are no emails listed in pingbacks. This makes perfect sense. The emails aren’t (generally) listed on a page that links to your site. That means without doing some serious site-scraping, there’s no way to get that email.

    Putting that aside, the other option is to, perhaps, list the ‘parent’ domain that pinged you. So I went back to Scott’s plugin and forked it into this:

    <?php
    
    /*
    Plugin Name: Pingers List
    License: GPLv2 or later
    License URI: http://www.gnu.org/licenses/gpl-2.0.html
    Description: List all pingbacks with links to their main domain.
    
    	Quasi fork of http://wordpress.org/plugins/commenter-pings/
    
    	Copyright (c) 2007-2014 by Scott Reilly (aka coffee2code)
    	Copyright (c) 2014 by Mika Epstein (aka ipstenu)
    */
    
    defined( 'ABSPATH' ) or die();
    
    if ( is_admin() && ! class_exists( 'PingersList' ) ) :
    
    class PingersList {
    
    	private static $plugin_basename = '';
    	private static $plugin_page     = '';
    
    	/**
    	 * Returns version of the plugin.
    	 *
    	 * @since 2.1
    	 */
    	public static function version() {
    		return '2.2.1';
    	}
    
    	/**
    	 * Constructor
    	 */
    	public static function init() {
    		self::$plugin_basename = plugin_basename( __FILE__ );
    
    		// Register hooks
    		add_action( 'admin_menu', array( __CLASS__, 'admin_menu' ) );
    		add_action( 'admin_menu', array( __CLASS__, 'do_init' ), 11 );
    	}
    
    	/**
    	 * Initialize hooks and data
    	 */
    	public static function do_init() {
    		// Currently empty
    	}
    
    	/**
    	 * Query database to obtain the list of commenter email addresses.
    	 * Only checks comments that are approved, have a author email, and are
    	 * of the comment_type 'comment' (or '').
    	 *
    	 * Only one entry is returned per email address.  If a given email address
    	 * has multiple instances in the database, each with different names, then
    	 * the most recent comment will be used to obtain any additional field data
    	 * such as comment_author, etc.
    	 *
    	 * @param array $fields  The fields to obtain from each comment
    	 * @param string $output (optional) Any of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K constants. See WP docs for wpdb::get_results() for more info
    	 * @return mixed List of email addresses
    	 */
    	public static function get_pings( $fields = array(  'comment_post_ID', 'comment_author', 'comment_author_url' ), $output = ARRAY_N ) {
    		global $wpdb;
    
    		// comment_author_url must be one of the fields
    		if ( ! in_array( 'comment_author_url', $fields ) )
    			array_unshift( $fields,  'comment_author_url' );
    
    		$fields = implode( ', ', $fields );
    		$sql = "SELECT $fields
    				FROM {$wpdb->comments} t1
    				INNER JOIN ( SELECT MAX(comment_ID) AS id FROM {$wpdb->comments} GROUP BY comment_author_url ) t2 ON t1.comment_ID = t2.id
    				WHERE
    					comment_approved = '1' AND
    					comment_type = 'pingback'
    				GROUP BY comment_author_url
    				ORDER BY comment_author_url ASC";
    		$pings = $wpdb->get_results( $sql, $output );
    		return $pings;
    	}
    
    
    	/**
    	 * Creates the admin menu.
    	 *
    	 * @return void
    	 */
    	public static function admin_menu() {
    		add_filter( 'plugin_action_links_' . self::$plugin_basename, array( __CLASS__, 'plugin_action_links' ) );
    		// Add menu under Comments
    		self::$plugin_page = add_comments_page( __( 'Pinger List', 'pinger-list' ), __( 'Pinger List', 'pinger-list' ),
    			apply_filters( 'manage_commenter_pings_options', 'manage_options' ), self::$plugin_basename, array( __CLASS__, 'admin_page' ) );
    	}
    
    	/**
    	 * Adds a 'Settings' link to the plugin action links.
    	 *
    	 * @param array $action_links The current action links
    	 * @return array The action links
    	 */
    	public static function plugin_action_links( $action_links ) {
    		$settings_link = '<a href="edit-comments.php?page=' . self::$plugin_basename.'" title="">' . __( 'Listing', 'pinger-list' ) . '</a>';
    		array_unshift( $action_links, $settings_link );
    		return $action_links;
    	}
    
    	/**
    	 * Outputs the contents of the plugin's admin page.
    	 *
    	 * @return void
    	 */
    	public static function admin_page() {
    		$pings = self::get_pings();
    		$pings_count = count( $pings );
    
    		echo '<div class="wrap">';
    		echo '<h2>' . __( 'Ping List', 'pinger-list' ) . '</h2>';
    		echo '<p>' . sprintf( __( 'There are %s unique ping locations for this site.', 'pinger-list' ), $pings_count ) . '</p>';
    		echo '</div>';
    
    			echo '<div class="wrap">';
    			echo '<h2>' . __( 'All Pings', 'pinger-list' ) . '</h2>';
    			echo '<table padding=2>';
    			echo '<tr><th>' . __( 'Post', 'pinger-list' ) . '</th><th>' . __( 'Source', 'pinger-list' ) . '</th><th>' . __( 'Direct Link', 'pinger-list' ) . '</th></tr>';
    
    			foreach ( $pings as $item ) {
    			
    				$pings_url = parse_url(esc_html( $item[2] ));
    				$ping_url = $pings_url[scheme].'://'. $pings_url[host];
    			
    				echo '<tr width="20%"><td><a href="' . get_permalink( $item[0] ) . '">'. get_the_title($item[0]) .'</a></td>';
    				echo '<td width="20%">' . make_clickable($ping_url).'</td>';
    				echo '<td><a href="'.esc_html( $item[2] ).'">'. esc_html( $item[1] ) . '</a></td></tr>';
    			}
    
    			echo '</table>';
    			echo '<p>' . sprintf( __( '%s pings listed.', 'pinger-list' ), $pings_count ) . '</p>';
    			echo '</div>';
    
    
    	}
    } // end PingersList
    
    PingersList::init();
    
    endif; // end if ! class_exists()
    

    The plugin’s crazy basic. It simply checks for unique ping sources and lists them. So if the same ‘main’ site links to you 10 times from 10 separate posts, it lists that. Probably a nice tweak would be to order them by domain, list the posts they link to and from where, and have a group by sort of list, but I didn’t get that far into it. Forks welcome, as are full blown plugins!

  • WP Comments ReplyTwo

    WP Comments ReplyTwo

    WPTavern has this cool thing where, without threaded comments, you still have a reply link, AND it creates an automatic link back to the original comment.

    Let me rewind. If you have threaded comments, then you get a ‘reply’ link on the bottom of a comment, and it lets you make a threaded reply. Yay! There are problems with this, though, as after a while, if you get nested deep enough, you can’t reply under anymore, and have to go up to click reply on the previous post and on and on.

    Threaded PipeBack eons ago, WPTavern solved this with a function, and like all great sites documented it! The problem? The documentation was busted. Now, yes, I did ping Jeff about this and asked him how it went, but in the meantime, I was impatient and looked up a plugin that almost fit my bill.

    Enter @ Reply (aka Reply To), which add in a reply link to all comments! Plus is gives you ‘Twitter like’ @replies, so when you comment, it starts “@foo:” automatically. This is just like what WPTavern has, perfect! Except… not quite.

    My problems became two:

    1. I want all comments to have a ‘reply’ link.
    2. I don’t want the hover over image.

    And I solved this with two code chunks: a plugin and a theme.

    See, by default WP stops showing you the ‘reply’ link when you can’t nest anymore. To change that, you have to edit how your theme calls comments. Or rather, you have to change the comment_reply_link() call.

    The Theme Code

    I’m already customizing my comments in Genesis so this was surprisingly simple.

    What was this:

    <div class="comment-reply">
        <?php comment_reply_link( array_merge( $args, array( 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ) ); ?>
        </div>
    

    Becomes this:

    <div class="comment-reply">
        <?php comment_reply_link( array_merge( $args, array( 'depth' => 1, 'max_depth' => 2 ) ) ); ?>
    </div>
    

    Basically we’re lying to WP and saying we always show the link. I got that idea from this stackexchange post, and as I understand it, we’re tricking WP by saying that every post is depth of 1, and has a max of 2, instead of letting it figure it out on it’s own. This is probably inelegant, but I couldn’t find another way to have the reply link always on.

    The Plugin Code

    nested-commentsThis was easier, in that I took the existing code, cleaned it up and removed the reply image, and that was pretty much it. Downside is that this does not work on every site. Notably, it doesn’t work unless threaded comments are turned on. When they’re not, it does a double-refresh.

    That said, this is probably never going to be a plugin for the masses, so I’m content with having it work this way for me. Even if I only thread one comment at a time, it would let me group them together and get the @-reply. It’s already rather popular on the site I intended it for, and people like it.

    So here’s the code:

    class AtReplyTwoHELF {
        public function __construct() {
            add_action( 'init', array( &$this, 'init' ) );
        }
    
        public function init() {
    		if (!is_admin()) {
    			 add_action('comment_form', array( $this, 'r2_reply_js'));
    			 add_filter('comment_reply_link', array( $this,'r2_reply'));
    		}
    	}
    
    	public function r2_reply_js() {
    	?>
    		<script type="text/javascript">
    			//<![CDATA[
    			function r2_replyTwo(commentID, author) {
    				var inReplyTo = '@<a href="' + commentID + '">' + author + '<\/a>: ';
    				var myField;
    				if (document.getElementById('comment') && document.getElementById('comment').type == 'textarea') {
    					myField = document.getElementById('comment');
    				} else {
    					return false;
    				}
    				if (document.selection) {
    					myField.focus();
    					sel = document.selection.createRange();
    					sel.text = inReplyTo;
    					myField.focus();
    				}
    				else if (myField.selectionStart || myField.selectionStart == '0') {
    					var startPos = myField.selectionStart;
    					var endPos = myField.selectionEnd;
    					var cursorPos = endPos;
    					myField.value = myField.value.substring(0, startPos) + inReplyTo + myField.value.substring(endPos, myField.value.length);
    					cursorPos += inReplyTo.length;
    					myField.focus();
    					myField.selectionStart = cursorPos;
    					myField.selectionEnd = cursorPos;
    				}
    				else {
    					myField.value += inReplyTo;
    					myField.focus();
    				}
    			}
    			//]]>
    		</script>
    	<?php
    	}
    
    	public function r2_reply($reply_link) {
    		 $comment_ID = '#comment-' . get_comment_ID();
    		 $comment_author = esc_html(get_comment_author());
    		 $r2_reply_link = 'onclick=\'return r2_replyTwo("' . $comment_ID . '", "' . $comment_author . '"),';
    		 return str_replace("onclick='return", "$r2_reply_link", $reply_link);
    	}
    }
    
    new AtReplyTwoHELF();
    

    Most of the javascript stuff is copy/pasta for me (just left of witchcraft), and I left it barebones (no configurations) on purpose. Simple is as simple does.

    Suggestions and tweaks welcome!