| 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 | } |