| 1 | <?php |
|---|
| 2 | |
|---|
| 3 | class Dotorg_Theme_Review_Assigner { |
|---|
| 4 | |
|---|
| 5 | protected $trac; |
|---|
| 6 | |
|---|
| 7 | function __construct() { |
|---|
| 8 | add_action( 'widgets_init', array( $this, 'widgets_init' ) ); |
|---|
| 9 | add_action( 'admin_post_assign_theme_review', array( $this, 'admin_post' ) ); |
|---|
| 10 | } |
|---|
| 11 | |
|---|
| 12 | function require_trac() { |
|---|
| 13 | require_once ABSPATH . WPINC . '/class-IXR.php'; |
|---|
| 14 | require_once ABSPATH . WPINC . '/class-wp-http-ixr-client.php'; |
|---|
| 15 | require_once WPORGPATH . 'bb-theme/themes/lib/class-trac.php'; |
|---|
| 16 | $this->trac = new Trac( 'themetracbot', THEME_TRACBOT_PASSWORD, 'https://themes.trac.wordpress.org/login/xmlrpc' ); |
|---|
| 17 | } |
|---|
| 18 | |
|---|
| 19 | function widgets_init() { |
|---|
| 20 | register_widget( 'Dotorg_Theme_Review_Widget' ); |
|---|
| 21 | } |
|---|
| 22 | |
|---|
| 23 | function admin_post() { |
|---|
| 24 | check_admin_referer( 'assign-theme-review' ); |
|---|
| 25 | |
|---|
| 26 | $this->require_trac(); |
|---|
| 27 | |
|---|
| 28 | $assignments = $this->get_current_assignments(); |
|---|
| 29 | if ( is_wp_error( $assignments ) ) { |
|---|
| 30 | $result = 'failed'; |
|---|
| 31 | } elseif ( $assignments ) { |
|---|
| 32 | $result = 'existing'; |
|---|
| 33 | } else { |
|---|
| 34 | $ticket = $this->assign_next_theme(); |
|---|
| 35 | if ( is_wp_error( $ticket ) ) { |
|---|
| 36 | $result = 'failed'; |
|---|
| 37 | } elseif ( $ticket ) { |
|---|
| 38 | $result = $ticket; |
|---|
| 39 | } else { |
|---|
| 40 | $result = 'none'; |
|---|
| 41 | } |
|---|
| 42 | } |
|---|
| 43 | |
|---|
| 44 | if ( is_int( $result ) ) { |
|---|
| 45 | wp_redirect( 'https://themes.trac.wordpress.org/ticket/' . $result ); |
|---|
| 46 | exit; |
|---|
| 47 | } |
|---|
| 48 | |
|---|
| 49 | $redirect = add_query_arg( array( 'theme-assign' => $result, 'now' => time() ), wp_get_referer() ); |
|---|
| 50 | wp_safe_redirect( $redirect ); |
|---|
| 51 | exit; |
|---|
| 52 | } |
|---|
| 53 | |
|---|
| 54 | function get_current_assignments() { |
|---|
| 55 | $username = wp_get_current_user()->user_login; |
|---|
| 56 | $assigned = $this->trac->ticket_query( sprintf( 'owner=%s&status=reviewing', $username ) ); |
|---|
| 57 | if ( $assigned ) { |
|---|
| 58 | return $assigned; |
|---|
| 59 | } elseif ( $assigned === array() ) { |
|---|
| 60 | return false; |
|---|
| 61 | } else { |
|---|
| 62 | // RPC failure condition ($assigned is false here) |
|---|
| 63 | return new WP_Error; |
|---|
| 64 | } |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | function assign_next_theme() { |
|---|
| 68 | $username = wp_get_current_user()->user_login; |
|---|
| 69 | $assigned = false; |
|---|
| 70 | $i = 0; |
|---|
| 71 | do { |
|---|
| 72 | if ( ++$i > 10 ) { |
|---|
| 73 | // Safeguard. |
|---|
| 74 | return new WP_Error; |
|---|
| 75 | } |
|---|
| 76 | |
|---|
| 77 | $next_ticket = $this->trac->ticket_query( 'priority=new theme&priority=previously reviewed&status=new&keywords=!~buddypress&order=time&max=1&owner=' ); |
|---|
| 78 | if ( $next_ticket === array() ) { |
|---|
| 79 | return false; |
|---|
| 80 | } elseif ( $next_ticket === false ) { |
|---|
| 81 | return new WP_Error; |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | $ticket = $this->trac->ticket_get( $next_ticket[0] ); |
|---|
| 85 | // Avoid race condition: Maybe someone was just assigned this one between query and get. |
|---|
| 86 | if ( $ticket['owner'] || $ticket['status'] !== 'new' ) { |
|---|
| 87 | continue; |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | // Assign the ticket using the proper workflow actions. |
|---|
| 91 | $attributes = array( 'action' => 'review_other', 'action_review_other_reassign_owner' => $username ); |
|---|
| 92 | |
|---|
| 93 | // Set _ts (changetime) so the update will fail if the ticket was updated since our fetch. |
|---|
| 94 | $attributes['_ts'] = $ticket['_ts']; |
|---|
| 95 | |
|---|
| 96 | // No comment, yes notify. User is 'themetracbot'. |
|---|
| 97 | $updated = $this->trac->ticket_update( $ticket['id'], '', $attributes, true ); |
|---|
| 98 | if ( ! $updated ) { |
|---|
| 99 | // Update failed. Try another ticket. |
|---|
| 100 | continue; |
|---|
| 101 | } |
|---|
| 102 | |
|---|
| 103 | $assigned = (int) $ticket['id']; |
|---|
| 104 | } while ( ! $assigned ); |
|---|
| 105 | return $assigned; |
|---|
| 106 | } |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | // Hack so this can sit in a mu-plugin (included before widgets are) |
|---|
| 110 | add_action( 'widgets_init', function() { |
|---|
| 111 | |
|---|
| 112 | class Dotorg_Theme_Review_Widget extends WP_Widget_Text { |
|---|
| 113 | function __construct() { |
|---|
| 114 | $widget_ops = array( 'classname' => 'dotorg_theme_review_widget', 'description' => 'Widget for theme reviewers to self-assign themselves themes.' ); |
|---|
| 115 | $control_ops = array( 'width' => 400, 'height' => 350 ); |
|---|
| 116 | WP_Widget::__construct('dotorg_theme_review', __('Theme Review Self-Assign'), $widget_ops, $control_ops ); |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | function widget( $args, $instance ) { |
|---|
| 120 | ob_start(); |
|---|
| 121 | echo '<style> |
|---|
| 122 | .dotorg_theme_review_widget .textwidget { font-size: 14px; color: #333 } |
|---|
| 123 | .dotorg_theme_review_widget .textwidget p { margin: 1em 0; } |
|---|
| 124 | .dotorg_theme_review_widget button { font-size: 13px; } |
|---|
| 125 | </style>'; |
|---|
| 126 | $action = isset( $_GET['theme-assign'] ) ? $_GET['theme-assign'] : false; |
|---|
| 127 | |
|---|
| 128 | if ( $action && $_GET['now'] < time() - 10 ) { |
|---|
| 129 | // If the page was refreshed > 10 seconds later, ignore the action and show the button again. |
|---|
| 130 | $action = false; |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | if ( 'existing' === $action ) { |
|---|
| 134 | printf( '<p><strong>You have reviews <a href="%s">assigned to you</a> that you need to complete first.</strong></p>', |
|---|
| 135 | esc_url( 'https://themes.trac.wordpress.org/query?status=!closed&owner=$USER' ) ); |
|---|
| 136 | } elseif ( 'none' === $action ) { |
|---|
| 137 | echo '<p><strong>There are no themes in the review queue to assign right now. Check back soon.</strong></p>'; |
|---|
| 138 | } elseif ( 'failed' === $action ) { |
|---|
| 139 | echo '<p><strong>Something went wrong; the system can᾿t assign you a review right now.</strong></p>'; |
|---|
| 140 | } elseif ( is_user_logged_in() ) { |
|---|
| 141 | $url = add_query_arg( '_wp_get_referer', $_SERVER['REQUEST_URI'], admin_url( 'admin-post.php?action=assign_theme_review' ) ); |
|---|
| 142 | $url = wp_nonce_url( $url, 'assign-theme-review' ); |
|---|
| 143 | printf( '<p><form method="post" action="%s"><button>Request a theme to review</button></form></p>', esc_url( $url ) ); |
|---|
| 144 | } else { |
|---|
| 145 | printf( '<p>Please <a href="%s">log in</a> first to review.</em></p>', esc_url( wp_login_url( $_SERVER['REQUEST_URI'] ) ) ); |
|---|
| 146 | } |
|---|
| 147 | $instance['text'] .= ob_get_clean(); |
|---|
| 148 | parent::widget( $args, $instance ); |
|---|
| 149 | } |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | }, 9 ); |
|---|
| 153 | |
|---|
| 154 | new Dotorg_Theme_Review_Assigner( $themes_trac ); |
|---|
| 155 | |
|---|