| | 1 | <?php |
| | 2 | /** |
| | 3 | * Watch Trac reviews and queue up jobs to update themes with review results. |
| | 4 | * |
| | 5 | * @package WordPressdotorg\Theme_Directory\Jobs |
| | 6 | */ |
| | 7 | |
| | 8 | namespace WordPressdotorg\Theme_Directory\Jobs; |
| | 9 | |
| | 10 | /** |
| | 11 | * Class Trac_Sync |
| | 12 | * |
| | 13 | * @package WordPressdotorg\Theme_Directory\Jobs |
| | 14 | */ |
| | 15 | class Trac_Sync { |
| | 16 | |
| | 17 | /** |
| | 18 | * Trac statuses. |
| | 19 | * |
| | 20 | * @var array |
| | 21 | */ |
| | 22 | protected static $stati = [ |
| | 23 | 'new' => [ |
| | 24 | 'status' => 'reopened', |
| | 25 | ], |
| | 26 | 'live' => [ |
| | 27 | 'status' => 'closed', |
| | 28 | 'resolution' => 'live', |
| | 29 | ], |
| | 30 | 'old' => [ |
| | 31 | 'status' => 'closed', |
| | 32 | 'resolution' => 'not-approved', |
| | 33 | ], |
| | 34 | ]; |
| | 35 | |
| | 36 | /** |
| | 37 | * The cron trigger for the svn import job. |
| | 38 | */ |
| | 39 | public static function cron_trigger() { |
| | 40 | if ( ! class_exists( 'Trac' ) ) { |
| | 41 | require_once ABSPATH . WPINC . '/class-IXR.php'; |
| | 42 | require_once ABSPATH . WPINC . '/class-wp-http-ixr-client.php'; |
| | 43 | require_once WP_PLUGIN_DIR . '/theme-directory/lib/class-trac.php'; |
| | 44 | } |
| | 45 | |
| | 46 | $trac = new \Trac( 'themetracbot', THEME_TRACBOT_PASSWORD, 'https://themes.trac.wordpress.org/login/xmlrpc' ); |
| | 47 | $last_request = get_option( 'wporg-themes-last-trac-sync', strtotime( '-2 days' ) ); |
| | 48 | update_option( 'wporg-themes-last-trac-sync', time() ); |
| | 49 | |
| | 50 | foreach ( self::$stati as $new_status => $args ) { |
| | 51 | // Get array of tickets. |
| | 52 | $tickets = (array) $trac->ticket_query( add_query_arg( wp_parse_args( $args, [ |
| | 53 | 'order' => 'changetime', |
| | 54 | 'changetime' => date( 'c', $last_request ), |
| | 55 | 'desc' => 1, |
| | 56 | ] ) ) ); |
| | 57 | |
| | 58 | foreach ( $tickets as $ticket_id ) { |
| | 59 | // Get the theme associated with that ticket. |
| | 60 | $theme_id = self::get_theme_id( $ticket_id ); |
| | 61 | if ( ! $theme_id ) { |
| | 62 | continue; |
| | 63 | } |
| | 64 | |
| | 65 | // If there was a newer-version-uploaded, we have more than one version per ticket. |
| | 66 | $versions = array_keys( (array) get_post_meta( $theme_id, '_ticket_id', true ), $ticket_id, true ); |
| | 67 | usort( $versions, 'version_compare' ); |
| | 68 | $version = end( $versions ); |
| | 69 | |
| | 70 | // There should always be a version associated with a ticket. |
| | 71 | if ( ! $version ) { |
| | 72 | continue; |
| | 73 | } |
| | 74 | |
| | 75 | /* |
| | 76 | * Bail if the the theme has the wrong status. |
| | 77 | * |
| | 78 | * For approved and rejected themes, we bail if the current status is not |
| | 79 | * 'new' That can happen when there are additional ticket updates (like |
| | 80 | * comments) after the ticket was closed. |
| | 81 | * |
| | 82 | * For reopened tickets we bail if the version is already marked as 'new'. |
| | 83 | * This should only be the case if the ticket was closed and reopened before |
| | 84 | * this script was able to sync the closed status. |
| | 85 | */ |
| | 86 | $current_status = wporg_themes_get_version_status( $theme_id, $version ); |
| | 87 | if ( ( 'new' !== $new_status && 'new' !== $current_status ) || ( 'new' === $new_status && 'new' === $current_status ) ) { |
| | 88 | continue; |
| | 89 | } |
| | 90 | |
| | 91 | // We don't need to set an already approved live version to live again. |
| | 92 | if ( 'live' === $current_status && 'live' === $new_status ) { |
| | 93 | continue; |
| | 94 | } |
| | 95 | |
| | 96 | wporg_themes_update_version_status( $theme_id, $version, $new_status ); |
| | 97 | } |
| | 98 | } |
| | 99 | } |
| | 100 | |
| | 101 | /** |
| | 102 | * Returns the ID of a theme associated with the passed ticket number. |
| | 103 | * |
| | 104 | * @param string $ticket_id Trac ticket number. |
| | 105 | * @return int The post ID, or 0 if none can be found. |
| | 106 | */ |
| | 107 | public static function get_theme_id( $ticket_id ) { |
| | 108 | $post_id = 0; |
| | 109 | |
| | 110 | $post_ids = new WP_Query( [ |
| | 111 | 'fields' => 'ids', |
| | 112 | 'post_status' => 'any', |
| | 113 | 'post_type' => 'repopackage', |
| | 114 | |
| | 115 | // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_query |
| | 116 | 'meta_query' => [ |
| | 117 | [ |
| | 118 | 'value' => $ticket_id, |
| | 119 | 'compare' => 'IN', |
| | 120 | ], |
| | 121 | ], |
| | 122 | |
| | 123 | // phpcs:ignore WordPress.VIP.PostsPerPage.posts_per_page_posts_per_page |
| | 124 | 'posts_per_page' => - 1, |
| | 125 | ] ); |
| | 126 | |
| | 127 | if ( ! empty( $post_ids ) ) { |
| | 128 | $post_id = current( $post_ids ); |
| | 129 | } |
| | 130 | |
| | 131 | return $post_id; |
| | 132 | } |
| | 133 | } |