Ticket #7487: 7487-test-blueprint.3.diff
File 7487-test-blueprint.3.diff, 15.6 KB (added by , 10 months ago) |
---|
-
plugins/plugin-directory/api/routes/class-plugin-blueprint.php
39 39 if ( $request->get_param('zip_hash') ) { 40 40 $this->reviewer_blueprint( $request, $plugin ); 41 41 } 42 if ( $request->get_param('url_hash') ) { 43 $this->developer_blueprint( $request, $plugin ); 44 } 42 45 43 46 $blueprints = get_post_meta( $plugin->ID, 'assets_blueprints', true ); 44 47 // Note: for now, only use a file called `blueprint.json`. … … 75 78 // Direct zip preview for plugin reviewers 76 79 if ( $request->get_param('zip_hash') ) { 77 80 foreach ( get_attached_media( 'application/zip', $plugin ) as $zip_file ) { 78 if ( hash_equals( Template::preview_link_hash( $zip_file->ID, 0 ), $request->get_param('zip_hash') ) || 79 hash_equals( Template::preview_link_hash( $zip_file->ID, -1 ), $request->get_param('zip_hash') ) ) { 81 $zip_file_path = get_attached_file( $zip_file->ID ); 82 if ( hash_equals( Template::preview_link_hash( $zip_file_path, 0 ), $request->get_param('zip_hash') ) || 83 hash_equals( Template::preview_link_hash( $zip_file_path, -1 ), $request->get_param('zip_hash') ) ) { 80 84 $zip_url = wp_get_attachment_url( $zip_file->ID ); 81 85 if ( $zip_url ) { 86 $is_pcp = 'pcp' === $request->get_param('type'); 87 $output = $this->generate_blueprint( $request, $plugin, $zip_url, $is_pcp, true ); 82 88 83 $landing_page = '/wp-admin/plugins.php'; 84 $activate_plugin = true; 85 $dependencies = $plugin->requires_plugins ?: []; 86 87 if ( stripos( $plugin->post_title, 'woocommerce' ) ) { 88 $dependencies[] = 'woocommerce'; 89 if ( $output ) { 90 header( 'Access-Control-Allow-Origin: https://playground.wordpress.net' ); 91 die( $output ); 89 92 } 90 if ( stripos( $plugin->post_title, 'buddypress' ) ) { 91 $dependencies[] = 'buddypress'; 92 } 93 } 94 } 95 } 96 } 93 97 94 $dependencies = array_diff( $dependencies, [ $plugin->post_name ] ); 98 return new \WP_Error( 'invalid_blueprint', 'Invalid file', array( 'status' => 500 ) ); 99 } 95 100 96 // Plugin deactivated, and land on the Plugin Check page 97 if ( 'pcp' === $request->get_param('type') ) { 98 $landing_page = '/wp-admin/admin.php?page=plugin-check&plugin=' . sanitize_title( $request['plugin_slug'] ); 99 $activate_plugin = false; 100 $dependencies = []; 101 } 101 function developer_blueprint( $request, $plugin ) { 102 // Generated blueprint for developers who haven't yet created a custom blueprint 103 if ( $request->get_param('url_hash') ) { 104 $download_link = Template::download_link( $plugin ); 105 if ( $download_link ) { 106 if ( hash_equals( Template::preview_link_hash( $download_link, 0 ), $request->get_param('url_hash') ) || 107 hash_equals( Template::preview_link_hash( $download_link, -1 ), $request->get_param('url_hash') ) ) { 108 $output = $this->generate_blueprint( $request, $plugin, $download_link, false, false ); 102 109 103 $zip_blueprint = (object)[ 104 'landingPage' => $landing_page, 105 'preferredVersions' => (object)[ 106 'php' => '8.0', 107 'wp' => 'latest', 108 ], 109 'phpExtensionBundles' => [ 110 'kitchen-sink' 111 ], 112 'features' => (object)[ 113 'networking' => true 114 ], 115 'steps' => [ 116 (object)[ 117 'step' => 'installPlugin', 118 'pluginZipFile' => (object)[ 119 'resource' => 'wordpress.org/plugins', 120 'slug' => 'plugin-check', 121 ] 122 ], 123 (object)[ 124 'step' => 'installPlugin', 125 'pluginZipFile' => (object)[ 126 'resource' => 'url', 127 'url' => $zip_url, 128 ], 129 'options' => (object)[ 130 'activate' => (bool)$activate_plugin 131 ] 132 ], 133 (object)[ 134 'step' => 'login', 135 'username' => 'admin', 136 'password' => 'password', 137 ] 138 ] 139 ]; 110 if ( $output ) { 111 header( 'Access-Control-Allow-Origin: https://playground.wordpress.net' ); 112 die( $output ); 113 } 114 } 115 } 116 } 117 } 140 118 141 if ( $dependencies ) { 142 $dep_step = []; 143 foreach ( $dependencies as $slug ) { 144 $dep_step[] = (object)[ 145 'step' => 'installPlugin', 146 'pluginZipFile' => [ 147 'resource' => 'wordpress.org/plugins', 148 'slug' => sanitize_title( $slug ), 149 ], 150 'options' => (object)[ 151 'activate' => true 152 ] 153 ]; 154 } 155 // Insert dependencies aftter PCP 156 array_splice( $zip_blueprint->steps, 1, 0, $dep_step ); 157 } 119 public function generate_blueprint( $request, $plugin, $zip_url, $install_pcp = true, $install_prh = true ) { 120 $landing_page = '/wp-admin/plugins.php'; 121 $activate_plugin = true; 122 $dependencies = $plugin->requires_plugins ?: []; 158 123 159 // Include the helper plugin too 160 $helper_zip = self::get_zip_url_by_slug( 'playground-review-helper' ); 161 if ( $helper_zip && 'pcp' !== $request->get_param('type') ) { 162 $helper_step = [ 163 (object)[ 164 'step' => 'installPlugin', 165 'pluginZipFile' => [ 166 'resource' => 'url', 167 'url' => $helper_zip, 168 ], 169 'options' => (object)[ 170 'activate' => (bool)$activate_plugin 171 ] 172 ] 173 ]; 174 array_splice( $zip_blueprint->steps, 1, 0, $helper_step ); 175 } 124 if ( stripos( $plugin->post_title, 'woocommerce' ) ) { 125 $dependencies[] = 'woocommerce'; 126 } 127 if ( stripos( $plugin->post_title, 'buddypress' ) ) { 128 $dependencies[] = 'buddypress'; 129 } 176 130 177 $output = json_encode( $zip_blueprint);131 $dependencies = array_diff( $dependencies, [ $plugin->post_name ] ); 178 132 179 if ( $output ) { 180 header( 'Access-Control-Allow-Origin: https://playground.wordpress.net' ); 181 die( $output ); 182 } 183 } 184 } 133 // Plugin deactivated, and land on the Plugin Check page 134 if ( $install_pcp ) { 135 $landing_page = '/wp-admin/admin.php?page=plugin-check&plugin=' . sanitize_title( $request['plugin_slug'] ); 136 $activate_plugin = false; 137 $dependencies = []; 138 } 139 140 $zip_blueprint = (object)[ 141 'landingPage' => $landing_page, 142 'preferredVersions' => (object)[ 143 'php' => '8.0', 144 'wp' => 'latest', 145 ], 146 'phpExtensionBundles' => [ 147 'kitchen-sink' 148 ], 149 'features' => (object)[ 150 'networking' => true 151 ], 152 ]; 153 154 $steps = []; 155 156 // PCP first, if needed. 157 if ( $install_pcp ) { 158 $steps[] = (object)[ 159 'step' => 'installPlugin', 160 'pluginZipFile' => (object)[ 161 'resource' => 'wordpress.org/plugins', 162 'slug' => 'plugin-check', 163 ] 164 ]; 165 } 166 167 // Include the helper plugin too 168 $helper_zip = self::get_zip_url_by_slug( 'playground-review-helper' ); 169 if ( $helper_zip && $install_prh ) { 170 $steps[] = (object)[ 171 'step' => 'installPlugin', 172 'pluginZipFile' => [ 173 'resource' => 'url', 174 'url' => $helper_zip, 175 ], 176 'options' => (object)[ 177 'activate' => (bool)$activate_plugin 178 ] 179 ]; 180 } 181 182 // Dependencies next 183 if ( $dependencies ) { 184 foreach ( $dependencies as $slug ) { 185 $steps[] = (object)[ 186 'step' => 'installPlugin', 187 'pluginZipFile' => [ 188 'resource' => 'wordpress.org/plugins', 189 'slug' => sanitize_title( $slug ), 190 ], 191 'options' => (object)[ 192 'activate' => true 193 ] 194 ]; 185 195 } 186 196 } 187 197 188 return new \WP_Error( 'invalid_blueprint', 'Invalid file', array( 'status' => 500 ) ); 198 // Now the plugin itself 199 $steps[] = (object)[ 200 'step' => 'installPlugin', 201 'pluginZipFile' => (object)[ 202 'resource' => 'url', 203 'url' => $zip_url, 204 ], 205 'options' => (object)[ 206 'activate' => (bool)$activate_plugin 207 ] 208 ]; 189 209 210 // Finally log in 211 $steps[] = (object)[ 212 'step' => 'login', 213 'username' => 'admin', 214 'password' => 'password', 215 ]; 216 217 $zip_blueprint->steps = $steps; 218 219 $output = json_encode( $zip_blueprint ); 220 221 return $output; 190 222 } 191 223 192 224 } -
plugins/plugin-directory/api/routes/class-plugin-self-toggle-preview.php
63 63 ]; 64 64 header( 'Location: ' . $result['location'] ); 65 65 66 if ( $request->get_param('dismiss') ) { 67 return $this->self_dismiss( $request, $plugin ); 68 } 69 66 70 // Toggle the postmeta value. Note that there is a race condition here. 67 71 $did = ''; 68 72 if ( get_post_meta( $plugin->ID, '_public_preview', true ) ) { … … 82 86 return $result; 83 87 } 84 88 89 /** 90 * Endpoint special case to dismiss the missing blueprint notice. 91 * 92 * @param \WP_REST_Request $request The Rest API Request. 93 * @param object $plugin The plugin post. 94 * @return bool True if the toggle was successful. 95 */ 96 public function self_dismiss( $request, $plugin ) { 97 $result = [ 98 'location' => wp_get_referer() ?: get_permalink( $plugin ), 99 ]; 100 101 delete_post_meta( $plugin->ID, '_missing_blueprint_notice' ); 102 103 return $result; 104 } 85 105 } -
plugins/plugin-directory/class-template.php
773 773 */ 774 774 public static function preview_link_zip( $slug, $attachment_id, $type = null ) { 775 775 776 $zip_hash = self::preview_link_hash( $attachment_id ); 776 $file = get_attached_file( $attachment_id ); 777 $zip_hash = self::preview_link_hash( $file ); 777 778 if ( !$zip_hash ) { 778 779 return false; 779 780 } … … 787 788 } 788 789 789 790 /** 791 * Generate a live preview (playground) link for a published plugin that does not yet have a custom blueprint. Needed for developer testing. 792 * 793 * @param string $slug The slug of the plugin post. 794 * @param int $download_link The URL of the zip download for the plugin. 795 * @param bool $blueprint_only False will return a full preview URL. True will return only a blueprint URL. 796 * @return false|string The preview or blueprint URL. 797 */ 798 public static function preview_link_developer( $slug, $download_link, $blueprint_only = false ) { 799 800 $url_hash = self::preview_link_hash( $download_link ); 801 if ( !$url_hash ) { 802 return false; 803 } 804 $dev_blueprint = sprintf( 'https://wordpress.org/plugins/wp-json/plugins/v1/plugin/%s/blueprint.json?url_hash=%s', esc_attr( $slug ), esc_attr( $url_hash ) ); 805 if ( $blueprint_only ) { 806 return $dev_blueprint; 807 } 808 $url_preview = add_query_arg( 'blueprint-url', urlencode($dev_blueprint), 'https://playground.wordpress.net/' ); 809 810 return $url_preview; 811 } 812 813 /** 790 814 * Return a time-dependent variable for zip preview links. 791 815 * 792 816 * @param int $lifespan The life span of the nonce, in seconds. Default is one week. … … 799 823 /** 800 824 * Return a nonce-style hash for zip preview links. 801 825 * 802 * @param int $attachment_id The ID of the attachment post corresponding to a pluginzip file.826 * @param string $zip_file The filesystem path or URL of the zip file. 803 827 * @param int $tick_offest Number to subtract from the nonce tick. Use both 0 and -1 to verify older nonces. 804 828 * @return false|string The hash as a hex string; or false if the attachment ID is invalid. 805 829 */ 806 public static function preview_link_hash( $attachment_id, $tick_offset = 0 ) { 807 $file = get_attached_file( $attachment_id ); 808 if ( !$file ) { 830 public static function preview_link_hash( $zip_file, $tick_offset = 0 ) { 831 if ( !$zip_file ) { 809 832 return false; 810 833 } 811 834 $tick = self::preview_link_tick() - $tick_offset; 812 return wp_hash( $tick . '|' . $ file, 'nonce' );835 return wp_hash( $tick . '|' . $zip_file, 'nonce' ); 813 836 } 814 837 815 838 /** … … 916 939 } 917 940 918 941 /** 942 * Generates a link to dismiss a missing blueprint notice. 943 * 944 * @param int|\WP_Post|null $post Optional. Post ID or post object. Defaults to global $post. 945 * @return string URL to toggle status. 946 */ 947 public static function get_self_dismiss_blueprint_notice_link( $post = null ) { 948 $post = get_post( $post ); 949 950 return add_query_arg( 951 array( '_wpnonce' => wp_create_nonce( 'wp_rest' ), 'dismiss' => 1 ), 952 home_url( 'wp-json/plugins/v1/plugin/' . $post->post_name . '/self-toggle-preview' ) 953 ); 954 } 955 956 /** 919 957 * Generates a link to enable Release Confirmations. 920 958 * 921 959 * @param int|\WP_Post|null $post Optional. Post ID or post object. Defaults to global $post. -
plugins/plugin-directory/cli/class-import.php
390 390 update_post_meta( $plugin->ID, 'assets_blueprints', wp_slash( $assets['blueprint'] ) ); 391 391 } else { 392 392 delete_post_meta( $plugin->ID, 'assets_blueprints' ); 393 // TODO: maybe if ( $touches_stable_tag )? 394 add_post_meta( $plugin->ID, '_missing_blueprint_notice', 1, true ); 393 395 } 394 396 395 397 // Store the block data, if known -
themes/pub/wporg-plugins/inc/template-tags.php
727 727 wp_kses_post( $import_warnings ) 728 728 ); 729 729 } 730 731 // This is less important, so only show if there are no other notices. 732 if ( !$notice && !$import_warnings && get_post_meta( $post->ID, '_missing_blueprint_notice', true ) ) { 733 $blueprint_test_button = sprintf( 734 '<a class="plugin-preview" target="_blank" href="%s">%s</a>', 735 Template::preview_link_developer( $post->post_name, Template::download_link() ), 736 esc_html__( 'Test your plugin in Playground', 'wporg-plugins' ) 737 ); 738 $blueprint_download_button = sprintf( 739 '<a class="plugin-preview" target="_blank" href="%s">%s</a>', 740 Template::preview_link_developer( $post->post_name, Template::download_link(), true ), 741 esc_html__( 'Download blueprint.json', 'wporg-plugins' ) 742 ); 743 $blueprint_dismiss_button = sprintf( 744 '<form method="POST" action="%s"><p><input type="submit" name="dismiss" value="%s" class="plugin-preview button button-secondary alignright" /></p></form>', 745 #'<a class="plugin-preview button button-secondary alignright" href="%s">%s</a>', 746 Template::get_self_dismiss_blueprint_notice_link( $post ), 747 esc_html__( 'dismiss', 'wporg-plugins' ) 748 ); 749 750 // There is surely a neater way to format this. 751 $blueprint_notice = sprintf( 752 '<ol><li>%s</li><li>%s</li><li>%s</li><li>%s</li></ol>', 753 $blueprint_test_button, 754 esc_html__( 'Fix any bugs in your plugin that prevent it from working in Playground.', 'wporg-plugins' ), 755 $blueprint_download_button, 756 esc_html__( 'Commit your blueprint to svn.', 'wporg-plugins' ) 757 ); 758 $blueprint_more = sprintf( 759 __( 'More info can be found in the <a href="%s">plugin handbook</a>.', 'wporg-plugins' ), 760 'https://developer.wordpress.org/plugins/wordpress-org/previews-and-blueprints/' 761 ); 762 763 printf( 764 '<div class="notice notice-info notice-alt">%s</div>', 765 '<p><strong>' . __( 'Your plugin does not yet have a blueprint file for user previews. If you\'d like to enable previews, please follow these steps to create a blueprint.', 'wporg-plugins' ) . '</strong></p>' . 766 $blueprint_notice . 767 $blueprint_dismiss_button . 768 '<p>' . $blueprint_more . '</p>' 769 ); 770 } 730 771 }