<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[929] sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer: Code Reference: add up/down voting to user contributed notes.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://meta.trac.wordpress.org/changeset/929">929</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://meta.trac.wordpress.org/changeset/929","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>coffee2code</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2014-10-23 07:44:26 +0000 (Thu, 23 Oct 2014)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Code Reference: add up/down voting to user contributed notes. Fixes <a href="http://meta.trac.wordpress.org/ticket/551">#551</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperfunctionsphp">sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/functions.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperinctemplatetagsphp">sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/inc/template-tags.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperscssmainscss">sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/scss/main.scss</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperstylesheetsmaincss">sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/stylesheets/main.css</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperincusercontentvotingphp">sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/inc/user-content-voting.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperjsusernotesvotingjs">sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/js/user-notes-voting.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperfunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/functions.php 2014-10-23 06:18:52 UTC (rev 928)
+++ sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/functions.php   2014-10-23 07:44:26 UTC (rev 929)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -41,6 +41,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require __DIR__ . '/inc/user-content.php';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Voting for user-submitted content.
+ */
+require __DIR__ . '/inc/user-content-voting.php';
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px">  * Redirects.
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> require __DIR__ . '/inc/redirects.php';
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperinctemplatetagsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/inc/template-tags.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/inc/template-tags.php 2014-10-23 06:18:52 UTC (rev 928)
+++ sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/inc/template-tags.php   2014-10-23 07:44:26 UTC (rev 929)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -176,6 +176,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        <!-- .comment-content -->
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                        <footer class="comment-meta">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                <?php DevHub_User_Submitted_Content_Voting::show_voting(); ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 <div class="comment-author vcard">
</span><span class="cx" style="display: block; padding: 0 10px">                                                        <span class="comment-author-attribution">
</span><span class="cx" style="display: block; padding: 0 10px">                                                        <?php if ( 0 != $args['avatar_size'] ) {
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperincusercontentvotingphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/inc/user-content-voting.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/inc/user-content-voting.php                           (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/inc/user-content-voting.php     2014-10-23 07:44:26 UTC (rev 929)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,473 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Code Reference voting for user contributed notes.
+ *
+ * Any user can vote once on any user contributed note.
+ *
+ * TODO:
+ *   - If a user gets blocked as spam, any vote cast by that user should get removed.
+ *
+ * @package wporg-developer
+ */
+
+/**
+ * Class to handle voting for user contributed notes.
+ */
+class DevHub_User_Contributed_Notes_Voting {
+
+       /**
+        * Meta key name for list of all user IDs that submitted an upvote.
+        *
+        * @var array
+        * @access public
+        */
+       public static $meta_upvotes = 'devhub_up_votes';
+
+       /**
+        * Meta key name for list of all user IDs that submitted a downvote.
+        *
+        * @var array
+        * @access public
+        */
+       public static $meta_downvotes = 'devhub_down_votes';
+
+       /**
+        * Initializer
+        *
+        * @access public
+        */
+       public static function init() {
+               add_action( 'init', array( __CLASS__, 'do_init' ) );
+       }
+
+       /**
+        * Initialization
+        *
+        * @access public
+        */
+       public static function do_init() {
+               // Save a non-AJAX submitted vote.
+               add_action( 'template_redirect',  array( __CLASS__, 'vote_submission' ) );
+
+               // Save AJAX submitted vote.
+               add_action( 'wp_ajax_note_vote',  array( __CLASS__, 'ajax_vote_submission' ) );
+
+               // Enqueue scripts and styles.
+               add_action( 'wp_enqueue_scripts', array( __CLASS__, 'scripts_and_styles' ), 11 );
+       }
+
+       /**
+        * Enqueues scripts and styles.
+        *
+        * @access public
+        */
+       public static function scripts_and_styles() {
+               // Only need to enqueue voting-related resources if there are comments to vote on.
+               if ( self::user_can_vote() && is_singular() && '0' != get_comments_number() ) {
+                       wp_register_script( 'wporg-developer-user-notes-voting', get_template_directory_uri() . '/js/user-notes-voting.js', array(), '20141022', true );
+                       wp_localize_script( 'wporg-developer-user-notes-voting', 'ajaxurl', admin_url( 'admin-ajax.php' ) );
+                       wp_enqueue_script( 'wporg-developer-user-notes-voting' );
+               }
+       }
+
+       /**
+        * Handles vote submission.
+        *
+        * @access public
+        *
+        * @return bool True if vote resulted in success or a change.
+        */
+       public static function vote_submission( $redirect = true ) {
+               $success = false;
+
+               if ( isset( $_REQUEST['comment'] ) && $_REQUEST['comment']
+                       && isset( $_REQUEST['vote'] ) && $_REQUEST['vote'] && in_array( $_REQUEST['vote'], array( 'down', 'up' ) )
+                       && isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'user-note-vote-' . $_REQUEST['comment'] )
+                       && self::user_can_vote( get_current_user_id(), $_REQUEST['comment'] )
+               ) {
+                       $success = ( 'down' == $_REQUEST['vote'] ) ?
+                               self::vote_down( (int) $_REQUEST['comment'], get_current_user_id() ) :
+                               self::vote_up( (int) $_REQUEST['comment'], get_current_user_id() );
+
+                       // Redirect user back to comment unless this was an AJAX request.
+                       if ( ! isset( $_REQUEST['is_ajax'] ) ) {
+                               wp_redirect( get_comment_link( $_REQUEST['comment'] ) );
+                               exit();
+                       }
+
+               }
+
+               return $success;
+       }
+
+       /**
+        * Handles AJAX vote submission.
+        *
+        * @access public
+        *
+        * @return int|string Returns 0 on error or no change; else the markup to be used to replace .user-note-voting
+        */
+       public static function ajax_vote_submission() {
+               check_ajax_referer( 'user-note-vote-' . $_POST['comment'], $_POST['_wpnonce'] );
+
+               $_REQUEST['is_ajax'] = true;
+               // If voting succeeded and resulted in a change, send back full replacement
+               // markup.
+               if ( self::vote_submission( false ) ) {
+                       self::show_voting( (int) $_POST['comment'] );
+                       die();
+               }
+               die( 0 );
+       }
+
+       /**
+        * Returns the list of upvotes for a comment.
+        *
+        * @access public
+        *
+        * @param  int $comment_id The comment ID.
+        * @return array
+        */
+       public static function get_comment_upvotes( $comment_id ) {
+               return self::get_comment_votes( $comment_id, self::$meta_upvotes );
+       }
+
+       /**
+        * Returns the list of downvotes for a comment.
+        *
+        * @access public
+        *
+        * @param  int $comment_id The comment ID.
+        * @return array
+        */
+       public static function get_comment_downvotes( $comment_id ) {
+               return self::get_comment_votes( $comment_id, self::$meta_downvotes );
+       }
+
+       /**
+        * Returns the list of vote for a specific vote type for a comment.
+        *
+        * @access protected
+        *
+        * @param  int    $comment_id The comment ID.
+        * @param  string $field
+        * @return array
+        */
+       protected static function get_comment_votes( $comment_id, $field ) {
+               $votes = get_comment_meta( $comment_id, $field, true );
+
+               if ( ! $votes ) {
+                       $votes = array();
+               }
+
+               return $votes;
+       }
+
+       /**
+        * Determines if the user can vote on user contributed notes.
+        *
+        * By default, the only requirements are:
+        * - the user is logged in.
+        * - the comment must be approvedUse the
+        * filter 'devhub_user_can_vote' to configure custom permissions for the
+        * user and/or the comment.
+        *
+        * @access public
+        *
+        * @param  int  $user_id    Optional. The user ID. If not defined, assumes current user.
+        * @param  int  $comment_id Optional. The comment ID. If not defined, assumes being able to comment generally.
+        * @return bool True if the user can vote.
+        */
+       public static function user_can_vote( $user_id = '', $comment_id = '' ) {
+               // If no user specified, assume current user.
+               if ( ! $user_id ) {
+                       $user_id = get_current_user_id();
+               }
+
+               // Must be a user to vote.
+               if ( ! $user_id ) {
+                       return false;
+               }
+
+               $can = true;
+
+               // Comment, if provided, must be approved.
+               if ( $comment_id ) {
+                       $can = ( '1' == get_comment( $comment_id )->comment_approved );
+               }
+
+               return apply_filters( 'devhub_user_can_vote', $can, $user_id, $comment_id );
+       }
+
+       /**
+        * Has user upvoted the comment?
+        *
+        * @access public
+        *
+        * @param  int    $comment_id The comment ID
+        * @param  int    $user_id    Optional. The user ID. If not defined, assumes current user.
+        * @return bool   True if the user has upvoted the comment.
+        */
+       public static function has_user_upvoted_comment( $comment_id, $user_id = '' ) {
+               // If no user specified, assume current user.
+               if ( ! $user_id ) {
+                       $user_id = get_current_user_id();
+               }
+
+               // Must be logged in to have voted.
+               if ( ! $user_id ) {
+                       return false;
+               }
+
+               $upvotes = self::get_comment_upvotes( $comment_id );
+
+               return in_array( $user_id, $upvotes );
+       }
+
+       /**
+        * Has user downvoted the comment?
+        *
+        * @access public
+        *
+        * @param  int    $comment_id The comment ID
+        * @param  int    $user_id    Optional. The user ID. If not defined, assumes current user.
+        * @return bool   True if the user has downvoted the comment.
+        */
+       public static function has_user_downvoted_comment( $comment_id, $user_id = '' ) {
+               // If no user specified, assume current user.
+               if ( ! $user_id ) {
+                       $user_id = get_current_user_id();
+               }
+
+               // Must be logged in to have voted.
+               if ( ! $user_id ) {
+                       return false;
+               }
+
+               $downvotes = self::get_comment_downvotes( $comment_id );
+
+               return in_array( $user_id, $downvotes );
+       }
+
+       /**
+        * Outputs the voting markup for user contributed note.
+        *
+        * @access public
+        *
+        * @param int $comment_id The comment ID, or empty to use current comment.
+        */
+       public static function show_voting( $comment_id = '') {
+               if ( ! $comment_id ) {
+                       global $comment;
+                       $comment_id = $comment->comment_ID;
+               }
+
+               $can_vote     = self::user_can_vote( get_current_user_id(), $comment_id );
+               $comment_link = get_comment_link( $comment_id );
+               $nonce        = wp_create_nonce( 'user-note-vote-' . $comment_id );
+
+               echo '<div class="user-note-voting" data-nonce="' . esc_attr( $nonce ) . '">';
+
+               // Up vote link
+               $user_upvoted = self::has_user_upvoted_comment( $comment_id );
+               if ( $can_vote ) {
+                       $title = $user_upvoted ?
+                               __( 'You have voted to indicate this note was helpful', 'wporg' ) :
+                               __( 'Vote up if this note was helpful', 'wporg' );
+                       $tag = $user_upvoted ? 'span' : 'a';
+               } else {
+                       $title = ! is_user_logged_in() ?
+                               __( 'You must log in to vote on the helpfulness of this note', 'wporg' ) :
+                               '';
+                       $tag = 'span';
+               }
+               echo "<{$tag} "
+                       . 'class="user-note-voting-up' . ( $user_upvoted ? ' user-voted' : '' )
+                       . '" title="' . esc_attr( $title )
+                       . '" data-id="' . esc_attr( $comment_id )
+                       . '" data-vote="up';
+               if ( ! $user_upvoted ) {
+                       echo '" href="'
+                               . esc_url( add_query_arg( array( '_wpnonce' => $nonce , 'comment' => $comment_id, 'vote' => 'up' ), $comment_link ) );
+               }
+               echo '">';
+               echo '<span class="dashicons dashicons-arrow-up"></span>';
+               echo "</{$tag}>";
+
+               // Total count
+               // Don't indicate a like percentage if no one voted.
+               $title = ( 0 == self::count_votes( $comment_id, 'total' ) ) ?
+                       '' :
+                       sprintf( __( '%s like this', 'wporg' ), self::count_votes( $comment_id, 'like_percentage' ) . '%' );
+               $class = '';
+               echo '<span '
+                       . 'class="user-note-voting-count ' . esc_attr( $class ) . '" '
+                       . 'title="' . esc_attr( $title ) . '">'
+                       . self::count_votes( $comment_id, 'difference' )
+                       . '</span>';
+
+               // Down vote link
+               $user_downvoted = ( $user_upvoted ? false : self::has_user_downvoted_comment( $comment_id ) );
+               if ( $can_vote ) {
+                       $title = $user_downvoted ?
+                               __( 'You have voted to indicate this note was not helpful', 'wporg' ) :
+                               __( 'Vote down if this note was not helpful', 'wporg' );
+                       $tag = $user_downvoted ? 'span' : 'a';
+               } else {
+                       $title = ! is_user_logged_in() ?
+                               __( 'You must log in to vote on the helpfulness of this note', 'wporg' ) :
+                               '';
+                       $tag = 'span';
+               }
+               echo "<{$tag} "
+                       . 'class="user-note-voting-down' . ( $user_downvoted ? ' user-voted' : '' )
+                       . '" title="' . esc_attr( $title )
+                       . '" data-id="' . esc_attr( $comment_id )
+                       . '" data-vote="down';
+               if ( ! $user_downvoted ) {
+                       echo '" href="'
+                               . esc_url( add_query_arg( array( '_wpnonce' => $nonce , 'comment' => $comment_id, 'vote' => 'down' ), $comment_link ) );
+               }
+               echo '">';
+               echo '<span class="dashicons dashicons-arrow-down"></span>';
+               echo "</{$tag}>";
+
+               echo '</div>';
+       }
+
+       /**
+        * Returns a count relating to the voting.
+        *
+        * Supported $type values:
+        * 'up'              : The total number of upvotes
+        * 'down'            : The total number of downvotes
+        * 'total'           : The total number of votes (upvotes + downvotes)
+        * 'difference'      : The difference between upvotes and downvotes (upvotes - downvotes)
+        * 'like_percentage' : The percentage of total votes that upvoted
+        * 
+        * @access public
+        *
+        * @param  string $type The type of count to return.
+        * @return int    The requested count.
+        */
+       public static function count_votes( $comment_id, $type ) {
+               // The 'up' count is needed in all cases except for 'down'.
+               if ( 'down' != $type ) {
+                       $up = count( self::get_comment_upvotes( $comment_id ) );
+               }
+               // The 'down' count is needed in all cases except for 'up'.
+               if ( 'up' != $type ) {
+                       $down = count( self::get_comment_downvotes( $comment_id ) );
+               }
+
+               switch ( $type ) {
+                       case 'up':
+                               return $up;
+                       case 'down':
+                               return $down;
+                       case 'total':
+                               return $up + $down;
+                       case 'difference':
+                               return $up - $down;
+                       case 'like_percentage':
+                               $total = $up + $down;
+                               // If no votes have been cast, return 0 to avoid dividing by 0.
+                               if ( 0 == $total ) {
+                                       return 0;
+                               }
+                               // More precise, and floatval() will drop ".00" when present
+                               //return floatval( round( ( $up / $total ) * 100, 2 ) );
+                               // Less precise; rounds to nearest integer
+                               return round( ( $up / $total ) * 100 );
+               }
+       }
+
+       /**
+        * Records an up vote.
+        *
+        * @access public
+        *
+        * @param  int  $comment_id The comment ID
+        * @param  int  $user_id    Optional. The user ID. Default is current user.
+        * @return bool Whether the up vote succeed (a new vote or a change in vote).
+        */
+       public static function vote_up( $comment_id, $user_id = '' ) {
+               return self::vote_handler( $comment_id, $user_id, 'up' );
+       }
+
+       /**
+        * Records a down vote.
+        *
+        * @access public
+        *
+        * @param  int  $comment_id The comment ID
+        * @param  int  $user_id    Optional. The user ID. Default is current user.
+        * @return bool Whether the down vote succeed (a new vote or a change in vote).
+        */
+       public static function vote_down( $comment_id, $user_id = '' ) {
+               return self::vote_handler( $comment_id, $user_id, 'down' );
+       }
+
+       /**
+        * Handles abstraction between an up or down vote.
+        *
+        * @access protected
+        *
+        * @param  int    $comment_id The comment ID
+        * @param  int    $user_id    Optional. The user ID. Default is current user.
+        * @param  string $type       Optional. 'up' for an up vote, 'down' for a down vote. Default is 'up'.
+        * @return bool   Whether the vote succeed (a new vote or a change in vote).
+        */
+       protected static function vote_handler( $comment_id, $user_id = '', $type = 'up' ) {
+               if ( ! $user_id ) {
+                       $user_id = get_current_user_id();
+               }
+
+               // See if the user can vote on this comment.
+               $votable = self::user_can_vote( $user_id, $comment_id );
+
+               if ( ! $votable ) {
+                       return false;
+               }
+
+               // The difference between an up vote and a down vote is which meta list their
+               // vote was recorded in.
+               if ( 'up' == $type ) {
+                       $add_to      = self::$meta_upvotes;
+                       $remove_from = self::$meta_downvotes;
+               } else {
+                       $add_to      = self::$meta_downvotes;
+                       $remove_from = self::$meta_upvotes;
+               }
+
+               // Get list of people who cast the same vote.
+               $add_to_list = get_comment_meta( $comment_id, $add_to, true );
+
+               // Don't do anything if user is recasting the same vote as before.
+               if ( in_array( $user_id, (array) $add_to_list ) ) {
+                       return false;
+               }
+
+               // If the user had previously cast the opposite vote, undo that older vote.
+               $remove_from_list = (array) get_comment_meta( $comment_id, $remove_from, true );
+               if ( in_array( $user_id, $remove_from_list ) ) {
+                       unset( $remove_from_list[ array_search( $user_id, $remove_from_list ) ] );
+                       update_comment_meta( $comment_id, $remove_from, $remove_from_list );
+               }
+
+               // Add user to the list of people casting the identical vote.
+               if ( $add_to_list ) {
+                       $add_to_list[] = $user_id;
+               } else {
+                       $add_to_list = array( $user_id );
+               }
+               update_comment_meta( $comment_id, $add_to, $add_to_list );
+
+               // TODO: Store some value (the like_percentage perhaps) in comment_karma so it can be custom sorted?
+
+               return true;
+       }
+
+} // DevHub_User_Contributed_Notes_Voting
+
+DevHub_User_Contributed_Notes_Voting::init();
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/inc/user-content-voting.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperjsusernotesvotingjs"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/js/user-notes-voting.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/js/user-notes-voting.js                               (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/js/user-notes-voting.js 2014-10-23 07:44:26 UTC (rev 929)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,25 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * Dynamic functionality for voting on user submitted notes.
+ *
+ */
+
+( function( $ ) {
+       $( '.user-note-voting a' ).on( 'click', function(e) {
+               e.preventDefault();
+
+               var item = $(this);
+
+               $.post(ajaxurl, {
+                               action:   "note_vote",
+                               comment:  $(this).attr('data-id'),
+                               vote:     $(this).attr('data-vote'),
+                               _wpnonce: $(this).parent().attr('data-nonce')
+                       }, function(data) {
+                               if ("0" != data) {
+                                       item.closest('.user-note-voting').replaceWith(data);
+                               }
+                       }, "text"
+               );
+               return false;
+       });
+} )( jQuery );
</ins></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperscssmainscss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/scss/main.scss</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/scss/main.scss        2014-10-23 06:18:52 UTC (rev 928)
+++ sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/scss/main.scss  2014-10-23 07:44:26 UTC (rev 929)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1203,6 +1203,35 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       .comment-author {
+               float: left;
+       }
+       .user-note-voting {
+               font-size: 1.5em;
+               clear: left;
+               float: left;
+               margin-top: -5px;
+               margin-right: 10px;
+       }
+       .user-note-voting-up .dashicons, .user-note-voting-down .dashicons {
+               font-size: 30px;
+               height: 30px;
+               width: 30px;
+               color: #000;
+       }
+       .user-note-voting-up {
+               margin-left: -9px;
+       }
+       .user-note-voting-count {
+               margin-right: -2px;
+       }
+       .user-voted.user-note-voting-up .dashicons {
+               color: green;
+       }
+       .user-voted.user-note-voting-down .dashicons {
+               color: red;
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> @media ( max-width: 59.999999em ) {
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentthemespubwporgdeveloperstylesheetsmaincss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/stylesheets/main.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/stylesheets/main.css  2014-10-23 06:18:52 UTC (rev 928)
+++ sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/stylesheets/main.css    2014-10-23 07:44:26 UTC (rev 929)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1331,6 +1331,34 @@
</span><span class="cx" style="display: block; padding: 0 10px"> .devhub-wrap ul.items li a {
</span><span class="cx" style="display: block; padding: 0 10px">   color: #555 !important;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.devhub-wrap .comment-author {
+  float: left;
+}
+.devhub-wrap .user-note-voting {
+  font-size: 1.5em;
+  clear: left;
+  float: left;
+  margin-top: -5px;
+  margin-right: 10px;
+}
+.devhub-wrap .user-note-voting-up .dashicons, .devhub-wrap .user-note-voting-down .dashicons {
+  font-size: 30px;
+  height: 30px;
+  width: 30px;
+  color: #000;
+}
+.devhub-wrap .user-note-voting-up {
+  margin-left: -9px;
+}
+.devhub-wrap .user-note-voting-count {
+  margin-right: -2px;
+}
+.devhub-wrap .user-voted.user-note-voting-up .dashicons {
+  color: green;
+}
+.devhub-wrap .user-voted.user-note-voting-down .dashicons {
+  color: red;
+}
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> @media (max-width: 60em) {
</span><span class="cx" style="display: block; padding: 0 10px">   .devhub-wrap {
</span></span></pre>
</div>
</div>

</body>
</html>