<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[13610] sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory: Plugin Directory: Add a 'pending plugin' API endpoint to fetch information about a plugin in review.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://meta.trac.wordpress.org/changeset/13610">13610</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://meta.trac.wordpress.org/changeset/13610","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>dd32</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2024-04-29 07:36:53 +0000 (Mon, 29 Apr 2024)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Plugin Directory: Add a 'pending plugin' API endpoint to fetch information about a plugin in review.

This is intended to be used by the Reviewer tools, such that the tools can use information known to the plugin directory about the ZIP being reviewed.

See <a href="http://meta.trac.wordpress.org/ticket/7385">#7385</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryapiclassbasephp">sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/class-base.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryapiroutesclasspluginphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-plugin.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryclassplugindirectoryphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-directory.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryshortcodesclassuploadhandlerphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-upload-handler.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryapiroutesclasspendingpluginphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-pending-plugin.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryapiclassbasephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/class-base.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/class-base.php      2024-04-29 07:02:02 UTC (rev 13609)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/class-base.php        2024-04-29 07:36:53 UTC (rev 13610)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -39,6 +39,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                new Routes\Plugin_Categorization();
</span><span class="cx" style="display: block; padding: 0 10px">                new Routes\Plugin_Upload();
</span><span class="cx" style="display: block; padding: 0 10px">                new Routes\Plugin_Blueprint();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                new Routes\Pending_Plugin();
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryapiroutesclasspendingpluginphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-pending-plugin.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-pending-plugin.php                             (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-pending-plugin.php       2024-04-29 07:36:53 UTC (rev 13610)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,72 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+namespace WordPressdotorg\Plugin_Directory\API\Routes;
+use WordPressdotorg\Plugin_Directory\API\Base;
+use WP_Error;
+use WP_REST_Server;
+
+/**
+ * An API Endpoint to expose a single Plugin data via api.wordpress.org/plugins/info/1.x
+ *
+ * @package WordPressdotorg_Plugin_Directory
+ */
+class Pending_Plugin extends Base {
+
+       /**
+        * Plugin constructor.
+        */
+       function __construct() {
+               register_rest_route( 'plugins/v1', '/pending-plugin/(?P<plugin_id>\d+)-(?P<token>[a-f0-9]{32})/?', array(
+                       'methods'             => WP_REST_Server::READABLE,
+                       'callback'            => array( $this, 'pending_plugin_info' ),
+                       'permission_callback' => array( $this, 'pending_plugin_permission_check' ),
+               ) );
+       }
+
+       /**
+        * Permission check that validates the hash for a pending plugin.
+        *
+        * @param \WP_REST_Request $request The Rest API Request.
+        * @return array A formatted array of all the data for the plugin.
+        */
+       public function pending_plugin_permission_check( $request ) {
+               $post          = get_post( $request['plugin_id'] );
+               $expected_hash = $post->{'_pending_access_token'} ?? false;
+
+               return (
+                       $post &&
+                       $expected_hash &&
+                       ! empty( $request['token'] ) &&
+                       hash_equals( $expected_hash, $request['token'] )
+               );
+       }
+
+       /**
+        * Endpoint to retrieve a full plugin representation for a pending plugin.
+        *
+        * @param \WP_REST_Request $request The Rest API Request.
+        * @return array A formatted array of all the data for the plugin.
+        */
+       public function pending_plugin_info( $request ) {
+               $post      = get_post( $request['plugin_id'] );
+               $submitter = get_user_by( 'id', $post->post_author );
+
+               if ( ! $post || ! in_array( $post->post_status, [ 'new', 'pending', 'rejected', 'approved' ] ) ) {
+                       return new WP_Error( 'plugin_not_found', 'Plugin not found', [ 'status' => 404 ] );
+               }
+
+               // Pending plugin specific fields
+               $details = [
+                       'ID'          => $post->ID,
+                       'post_status' => $post->post_status,
+                       'edit_url'    => add_query_arg( [ 'action' => 'edit', 'post' => $post->ID ], admin_url( 'post.php' ) ),
+                       'submitter'   => [
+                               'user_login' => $submitter->user_login,
+                               'user_email' => $submitter->user_email,
+                       ]
+               ];
+
+               $plugin_endpoint = new Plugin;
+
+               return $details + $plugin_endpoint->plugin_info_data( $request, $post );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-pending-plugin.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryapiroutesclasspluginphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-plugin.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-plugin.php     2024-04-29 07:02:02 UTC (rev 13609)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/api/routes/class-plugin.php       2024-04-29 07:36:53 UTC (rev 13610)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -38,12 +38,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return array A formatted array of all the data for the plugin.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        function plugin_info( $request ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $plugin_slug = $request['plugin_slug'];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $post = Plugin_Directory::get_plugin_post( $request['plugin_slug'] );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                global $post;
-               $post = Plugin_Directory::get_plugin_post( $plugin_slug );
-
-               if ( 'publish' != $post->post_status ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ! $post || 'publish' != $post->post_status ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         // Copy what the REST API does if the param is incorrect
</span><span class="cx" style="display: block; padding: 0 10px">                        return new \WP_Error(
</span><span class="cx" style="display: block; padding: 0 10px">                                'rest_invalid_param',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -57,13 +54,29 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                return $this->plugin_info_data( $request, $post );
+       }
+
+       /**
+        * The underlying API for the plugin information.
+        *
+        * Expects that the input has been validated, and that the $post object is safe for display.
+        * This is shared with/called from Pending_Plugin too.
+        *
+        * @param \WP_REST_Request $request The request object.
+        * @param \WP_Post         $post    The post object for the plugin.
+        * @return array The formatted array of all the data for the plugin.
+        */
+       public function plugin_info_data( $request, $post ) {
+               $GLOBALS['post'] = $post;
+               $plugin_slug     = $post->post_name;
+               $post_id         = $post->ID;
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Support returning API data in different locales, even on wordpress.org (for api.wordpress.org usage)
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! empty( $request['locale'] ) && ! in_array( strtolower( $request['locale'] ), array( 'en_us', 'en' ) ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        switch_to_locale( $request['locale'] );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $post_id = $post->ID;
-
</del><span class="cx" style="display: block; padding: 0 10px">                 $result            = array();
</span><span class="cx" style="display: block; padding: 0 10px">                $result['name']    = get_the_title();
</span><span class="cx" style="display: block; padding: 0 10px">                $result['slug']    = $post->post_name;
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryclassplugindirectoryphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-directory.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-directory.php      2024-04-29 07:02:02 UTC (rev 13609)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-directory.php        2024-04-29 07:36:53 UTC (rev 13610)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -51,6 +51,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'wp_head', array( Template::class, 'json_ld_schema' ), 1 );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'wp_head', array( Template::class, 'hreflang_link_attributes' ), 2 );
</span><span class="cx" style="display: block; padding: 0 10px">                add_filter( 'allowed_redirect_hosts', array( $this, 'filter_redirect_hosts' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                add_filter( 'wp_get_attachment_url', array( $this, 'add_info_to_zip_url' ), 100, 2 );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Add no-index headers where appropriate.
</span><span class="cx" style="display: block; padding: 0 10px">                add_filter( 'wporg_noindex_request', [ Template::class, 'should_noindex_request' ] );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1834,6 +1835,37 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Add additional context to ZIP urls.
+        *
+        * The ZIP URL will have a 'info' key attached which is a rest api URL to information about the plugin.
+        *
+        * @param string $url           The URL to the ZIP file.
+        * @param int    $attachment_id The attachment ID.
+        * @return string The URL to the ZIP file.
+        */
+       public function add_info_to_zip_url( $url, $attachment_id ) {
+               $attachment = get_post( $attachment_id );
+               $post       = get_post( $attachment->post_parent );
+               $token      = $post->{'_pending_access_token'} ?? false;
+
+               if ( ! $url || ! $attachment || ! $post || ! $token || ! current_user_can( 'edit_post', $post->ID ) ) {
+                       return $url;
+               }
+
+               $url = add_query_arg(
+                       'info',
+                       urlencode( rest_url( sprintf(
+                               'plugins/v1/pending-plugin/%d-%s/',
+                               $post->ID,
+                               $token
+                       ) ) ),
+                       $url
+               );
+
+               return $url;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Retrieve the WP_Post object representing a given plugin.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @static
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsplugindirectoryshortcodesclassuploadhandlerphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-upload-handler.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-upload-handler.php     2024-04-29 07:02:02 UTC (rev 13609)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-upload-handler.php       2024-04-29 07:36:53 UTC (rev 13610)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -468,9 +468,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // First time submission, track some additional metadata.
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! $updating_existing ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $post_args['meta_input']['_author_ip']        = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] );
-                       $post_args['meta_input']['_submitted_date']   = time();
-                       $post_args['meta_input']['_used_upload_token'] = $has_upload_token;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $post_args['meta_input']['_author_ip']            = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] );
+                       $post_args['meta_input']['_submitted_date']       = time();
+                       $post_args['meta_input']['_used_upload_token']    = $has_upload_token;
+                       $post_args['meta_input']['_pending_access_token'] = md5( wp_generate_password( 32, true, true ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Add/Update the Plugin Directory entry for this plugin.
</span></span></pre>
</div>
</div>

</body>
</html>