Making WordPress.org

Changeset 13335

Timestamp:
03/14/2024 05:40:44 AM (5 months ago)
Author:
tellyworth
Message:

Plugin directory: prompt plugin developers to create a blueprint for previews

This adds a notice, visible only to a plugin's developers, prompting them to test and create a blueprint.json file so users can run a Live Preview of their plugin in the WordPress Playground. The notice can be dismissed.

See #7487.

Location:
sites/trunk/wordpress.org/public_html/wp-content
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-plugin-blueprint.php

    r13189 r13335  
    4040            $this->reviewer_blueprint( $request, $plugin );
    4141        }
     42
     43
     44
    4245
    4346        $blueprints = get_post_meta( $plugin->ID, 'assets_blueprints', true );
     
    7679        if ( $request->get_param('zip_hash') ) {
    7780            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') ) ) {
    8084                    $zip_url = wp_get_attachment_url( $zip_file->ID );
    8185                    if ( $zip_url ) {
    82 
    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                         }
    90                         if ( stripos( $plugin->post_title, 'buddypress' ) ) {
    91                             $dependencies[] = 'buddypress';
    92                         }
    93 
    94                         $dependencies = array_diff( $dependencies, [ $plugin->post_name ] );
    95 
    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                         }
    102 
    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                         ];
    140 
    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                         }
    158 
    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                         }
    176 
    177                         $output = json_encode( $zip_blueprint );
     86                        $is_pcp = 'pcp' === $request->get_param('type');
     87                        $output = $this->generate_blueprint( $request, $plugin, $zip_url, $is_pcp, true );
    17888
    17989                        if ( $output ) {
     
    18797
    18898        return new \WP_Error( 'invalid_blueprint', 'Invalid file', array( 'status' => 500 ) );
    189 
     99    }
     100
     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 );
     109
     110                    if ( $output ) {
     111                        header( 'Access-Control-Allow-Origin: https://playground.wordpress.net' );
     112                        die( $output );
     113                    }
     114                }
     115            }
     116        }
     117    }
     118
     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 ?: [];
     123
     124        if ( stripos( $plugin->post_title, 'woocommerce' ) ) {
     125            $dependencies[] = 'woocommerce';
     126        }
     127        if ( stripos( $plugin->post_title, 'buddypress' ) ) {
     128            $dependencies[] = 'buddypress';
     129        }
     130
     131        $dependencies = array_diff( $dependencies, [ $plugin->post_name ] );
     132
     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                ];
     195            }
     196        }
     197
     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                ];
     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, JSON_PRETTY_PRINT );
     220
     221        return $output;
    190222    }
    191223
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-plugin-self-toggle-preview.php

    r13037 r13335  
    6464        header( 'Location: ' . $result['location'] );
    6565
     66
     67
     68
     69
    6670        // Toggle the postmeta value. Note that there is a race condition here.
    6771        $did = '';
     
    8387    }
    8488
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
     100
     101
     102
     103
     104
     105
    85106}
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php

    r13310 r13335  
    774774    public static function preview_link_zip( $slug, $attachment_id, $type = null ) {
    775775
    776         $zip_hash = self::preview_link_hash( $attachment_id );
     776        $file = get_attached_file( $attachment_id );
     777        $zip_hash = self::preview_link_hash( $file );
    777778        if ( !$zip_hash ) {
    778779            return false;
     
    788789
    789790    /**
     791
     792
     793
     794
     795
     796
     797
     798
     799
     800
     801
     802
     803
     804
     805
     806
     807
     808
     809
     810
     811
     812
     813
    790814     * Return a time-dependent variable for zip preview links.
    791815     *
     
    800824     * Return a nonce-style hash for zip preview links.
    801825     *
    802      * @param int $attachment_id      The ID of the attachment post corresponding to a plugin zip file.
     826     * @param zip file.
    803827     * @param int $tick_offest        Number to subtract from the nonce tick. Use both 0 and -1 to verify older nonces.
    804828     * @return false|string           The hash as a hex string; or false if the attachment ID is invalid.
    805829     */
    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 ) {
    809832            return false;
    810833        }
    811834        $tick = self::preview_link_tick() - $tick_offset;
    812         return wp_hash( $tick . '|' . $file, 'nonce' );
     835        return wp_hash( $tick . '|' . $file, 'nonce' );
    813836    }
    814837
     
    912935        return add_query_arg(
    913936            array( '_wpnonce' => wp_create_nonce( 'wp_rest' ) ),
     937
     938
     939
     940
     941
     942
     943
     944
     945
     946
     947
     948
     949
     950
     951
    914952            home_url( 'wp-json/plugins/v1/plugin/' . $post->post_name . '/self-toggle-preview' )
    915953        );
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php

    r13234 r13335  
    391391        } else {
    392392            delete_post_meta( $plugin->ID, 'assets_blueprints' );
     393
     394
    393395        }
    394396
  • sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins/inc/template-tags.php

    r13238 r13335  
    728728        );
    729729    }
    730 }
     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    }
     771}
Note: See TracChangeset for help on using the changeset viewer.