WordPress.org

Making WordPress.org

Ticket #2465: 2465.1.diff

File 2465.1.diff, 9.3 KB (added by danielbachhuber, 5 years ago)
  • wp-content/themes/pub/wporg-developer/functions.php

     
    5656require __DIR__ . '/inc/user-content-voting.php';
    5757
    5858/**
     59 * CLI commands custom post type and importer
     60 */
     61require __DIR__ . '/inc/cli.php';
     62
     63/**
    5964 * Explanations for functions. hooks, classes, and methods.
    6065 */
    6166require( __DIR__ . '/inc/explanations.php' );
  • wp-content/themes/pub/wporg-developer/inc/cli.php

     
     1<?php
     2
     3class DevHub_CLI {
     4
     5        private static $commands_manifest = 'https://raw.githubusercontent.com/wp-cli/handbook/master/bin/commands-manifest.json';
     6        private static $meta_key = 'wporg_cli_markdown_source';
     7        private static $supported_post_types = array( 'command' );
     8        private static $posts_per_page = 350;
     9
     10        public function __construct() {
     11                $this->init();
     12        }
     13
     14        public function init() {
     15                add_action( 'init', array( $this, 'action_init_register_cron_jobs' ) );
     16                add_action( 'init', array( $this, 'action_init_register_post_types' ) );
     17                add_action( 'devhub_cli_manifest_import', array( $this, 'action_devhub_cli_manifest_import' ) );
     18                add_action( 'devhub_cli_markdown_import', array( $this, 'action_devhub_cli_markdown_import' ) );
     19        }
     20
     21        public static function action_init_register_cron_jobs() {
     22                if ( ! wp_next_scheduled( 'devhub_cli_manifest_import' ) ) {
     23                        wp_schedule_event( time(), 'twicedaily', 'devhub_cli_manifest_import' );
     24                }
     25                if ( ! wp_next_scheduled( 'devhub_cli_markdown_import' ) ) {
     26                        wp_schedule_event( time(), 'twicedaily', 'devhub_cli_markdown_import' );
     27                }
     28        }
     29
     30        public static function action_init_register_post_types() {
     31                $supports = array(
     32                        'comments',
     33                        'custom-fields',
     34                        'editor',
     35                        'excerpt',
     36                        'revisions',
     37                        'title',
     38                );
     39                register_post_type( 'command', array(
     40                        'has_archive' => 'cli/commands',
     41                        'label'       => __( 'Commands', 'wporg' ),
     42                        'labels'      => array(
     43                                'name'               => __( 'Commands', 'wporg' ),
     44                                'singular_name'      => __( 'Command', 'wporg' ),
     45                                'all_items'          => __( 'Commands', 'wporg' ),
     46                                'new_item'           => __( 'New Command', 'wporg' ),
     47                                'add_new'            => __( 'Add New', 'wporg' ),
     48                                'add_new_item'       => __( 'Add New Command', 'wporg' ),
     49                                'edit_item'          => __( 'Edit Command', 'wporg' ),
     50                                'view_item'          => __( 'View Command', 'wporg' ),
     51                                'search_items'       => __( 'Search Commands', 'wporg' ),
     52                                'not_found'          => __( 'No Commands found', 'wporg' ),
     53                                'not_found_in_trash' => __( 'No Commands found in trash', 'wporg' ),
     54                                'parent_item_colon'  => __( 'Parent Command', 'wporg' ),
     55                                'menu_name'          => __( 'Commands', 'wporg' ),
     56                        ),
     57                        'public'      => true,
     58                        'hierarchical'=> true,
     59                        'rewrite'     => array(
     60                                'feeds'      => false,
     61                                'slug'       => 'cli/commands',
     62                                'with_front' => false,
     63                        ),
     64                        'supports'    => $supports,
     65                ) );
     66        }
     67
     68        public static function action_devhub_cli_manifest_import() {
     69                $response = wp_remote_get( self::$commands_manifest );
     70                if ( is_wp_error( $response ) ) {
     71                        return $response;
     72                } elseif ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
     73                        return new WP_Error( 'invalid-http-code', 'Markdown source returned non-200 http code.' );
     74                }
     75                $manifest = json_decode( wp_remote_retrieve_body( $response ), true );
     76                if ( ! $manifest ) {
     77                        return new WP_Error( 'invalid-manifest', 'Manifest did not unfurl properly.' );;
     78                }
     79                // Fetch all handbook posts for comparison
     80                $q = new WP_Query( array(
     81                        'post_type'      => self::$supported_post_types,
     82                        'post_status'    => 'publish',
     83                        'posts_per_page' => self::$posts_per_page,
     84                ) );
     85                $existing = array();
     86                foreach( $q->posts as $post ) {
     87                        $cmd_path = rtrim( str_replace( home_url( 'cli/commands/' ), '', get_permalink( $post->ID ) ), '/' );
     88                        $existing[ $cmd_path ] = array(
     89                                'post_id'   => $post->ID,
     90                                'cmd_path'  => $cmd_path,
     91                        );
     92                }
     93                $created = 0;
     94                foreach( $manifest as $doc ) {
     95                        // Already exists
     96                        if ( wp_filter_object_list( $existing, array( 'cmd_path' => $doc['cmd_path'] ) ) ) {
     97                                continue;
     98                        }
     99                        if ( self::process_manifest_doc( $doc, $existing, $manifest ) ) {
     100                                $created++;
     101                        }
     102                }
     103                if ( class_exists( 'WP_CLI' ) ) {
     104                        \WP_CLI::success( "Successfully created {$created} handbook pages." );
     105                }
     106        }
     107
     108        private static function process_manifest_doc( $doc, &$existing, $manifest ) {
     109                $post_parent = null;
     110                if ( ! empty( $doc['parent'] ) ) {
     111                        // Find the parent in the existing set
     112                        $parents = wp_filter_object_list( $existing, array( 'cmd_path' => $doc['parent'] ) );
     113                        if ( empty( $parents ) ) {
     114                                if ( ! self::process_manifest_doc( $manifest[ $doc['parent'] ], $existing, $manifest ) ) {
     115                                        return;
     116                                }
     117                                $parents = wp_filter_object_list( $existing, array( 'cmd_path' => $doc['parent'] ) );
     118                        }
     119                        if ( ! empty( $parents ) ) {
     120                                $parent = array_shift( $parents );
     121                                $post_parent = $parent['post_id'];
     122                        }
     123                }
     124                $post = self::create_post_from_manifest_doc( $doc, $post_parent );
     125                if ( $post ) {
     126                        $cmd_path = rtrim( str_replace( home_url( 'cli/commands/' ), '', get_permalink( $post->ID ) ), '/' );
     127                        $existing[ $cmd_path ] = array(
     128                                'post_id'   => $post->ID,
     129                                'cmd_path'  => $cmd_path,
     130                        );
     131                        return true;
     132                }
     133                return false;
     134        }
     135
     136        public static function action_devhub_cli_markdown_import() {
     137                $q = new WP_Query( array(
     138                        'post_type'      => self::$supported_post_types,
     139                        'post_status'    => 'publish',
     140                        'fields'         => 'ids',
     141                        'posts_per_page' => self::$posts_per_page,
     142                ) );
     143                $ids = $q->posts;
     144                $success = 0;
     145                foreach( $ids as $id ) {
     146                        $ret = self::update_post_from_markdown_source( $id );
     147                        if ( class_exists( 'WP_CLI' ) ) {
     148                                if ( is_wp_error( $ret ) ) {
     149                                        \WP_CLI::warning( $ret->get_error_message() );
     150                                } else {
     151                                        \WP_CLI::log( "Updated {$id} from markdown source" );
     152                                        $success++;
     153                                }
     154                        }
     155                }
     156                if ( class_exists( 'WP_CLI' ) ) {
     157                        $total = count( $ids );
     158                        \WP_CLI::success( "Successfully updated {$success} of {$total} CLI command pages." );
     159                }
     160        }
     161
     162        /**
     163         * Create a new handbook page from the manifest document
     164         */
     165        private static function create_post_from_manifest_doc( $doc, $post_parent = null ) {
     166                $post_data = array(
     167                        'post_type'   => 'command',
     168                        'post_status' => 'publish',
     169                        'post_parent' => $post_parent,
     170                        'post_title'  => sanitize_text_field( wp_slash( $doc['title'] ) ),
     171                        'post_name'   => sanitize_title_with_dashes( $doc['slug'] ),
     172                );
     173                $post_id = wp_insert_post( $post_data );
     174                if ( ! $post_id ) {
     175                        return false;
     176                }
     177                if ( class_exists( 'WP_CLI' ) ) {
     178                        \WP_CLI::log( "Created post {$post_id} for {$doc['title']}." );
     179                }
     180                update_post_meta( $post_id, self::$meta_key, esc_url_raw( $doc['markdown_source'] ) );
     181                return get_post( $post_id );
     182        }
     183
     184        /**
     185         * Update a post from its Markdown source
     186         */
     187        private static function update_post_from_markdown_source( $post_id ) {
     188                $markdown_source = self::get_markdown_source( $post_id );
     189                if ( is_wp_error( $markdown_source ) ) {
     190                        return $markdown_source;
     191                }
     192                if ( ! function_exists( 'jetpack_require_lib' ) ) {
     193                        return new WP_Error( 'missing-jetpack-require-lib', 'jetpack_require_lib() is missing on system.' );
     194                }
     195
     196                // Transform GitHub repo HTML pages into their raw equivalents
     197                $markdown_source = preg_replace( '#https?://github\.com/([^/]+/[^/]+)/blob/(.+)#', 'https://raw.githubusercontent.com/$1/$2', $markdown_source );
     198                $markdown_source = add_query_arg( 'v', time(), $markdown_source );
     199                $response = wp_remote_get( $markdown_source );
     200                if ( is_wp_error( $response ) ) {
     201                        return $response;
     202                } elseif ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
     203                        return new WP_Error( 'invalid-http-code', 'Markdown source returned non-200 http code.' );
     204                }
     205
     206                $markdown = wp_remote_retrieve_body( $response );
     207                // Strip YAML doc from the header
     208                $markdown = preg_replace( '#^---(.+)---#Us', '', $markdown );
     209
     210                $title = null;
     211                if ( preg_match( '/^#\s(.+)/', $markdown, $matches ) ) {
     212                        $title = $matches[1];
     213                        $markdown = preg_replace( '/^#\swp\s(.+)/', '', $markdown );
     214                }
     215
     216                // Transform to HTML and save the post
     217                jetpack_require_lib( 'markdown' );
     218                $parser = new \WPCom_GHF_Markdown_Parser;
     219                $html = $parser->transform( $markdown );
     220                $post_data = array(
     221                        'ID'           => $post_id,
     222                        'post_content' => wp_filter_post_kses( wp_slash( $html ) ),
     223                );
     224                if ( ! is_null( $title ) ) {
     225                        $post_data['post_title'] = sanitize_text_field( wp_slash( $title ) );
     226                }
     227                wp_update_post( $post_data );
     228                return true;
     229        }
     230
     231        /**
     232         * Retrieve the markdown source URL for a given post.
     233         */
     234        public static function get_markdown_source( $post_id ) {
     235                $markdown_source = get_post_meta( $post_id, self::$meta_key, true );
     236                if ( ! $markdown_source ) {
     237                        return new WP_Error( 'missing-markdown-source', 'Markdown source is missing for post.' );
     238                }
     239
     240                return $markdown_source;
     241        }
     242
     243}
     244
     245$devhub_cli = new DevHub_CLI();