WordPress.org

Making WordPress.org

Ticket #2968: 2968.diff

File 2968.diff, 23.7 KB (added by rmccue, 2 years ago)

Add WPORG Markdown plugin for CLI/API handbooks

  • wp-content/plugins/wporg-markdown/README.md

     
     1# WPORG Markdown Importer
     2
     3Imports Markdown from a remote site (like GitHub) into WordPress as pages.
     4
     5## Configuration
     6
     7Each importer needs to override the abstract methods:
     8
     9* `get_base()` - Base URL for imported pages. This will be stripped from the key before comparing.
     10* `get_manifest_url()` - URL pointing to the manifest.
     11* `get_post_type()` - Post type to import as.
     12
     13## Manifest Format
     14
     15The manifest should be a JSON object, with the keys set to the desired permalink (excluding the base path). Each item should also be a JSON object, containing the following keys:
     16
     17* `slug` - Post name to insert. (Must match the final path-part of the key.)
     18* `markdown_source` - URL for the Markdown file to parse into content.
     19* `parent` - Key for the parent to store under. (Must correspond to the non-final path-parts of the key.)
     20* `title` - Title to use when creating post. Used temporarily, will be updated from the Markdown file. If not specified, defaults to `slug` (but will be updated from Markdown source).
     21
     22**Note:** The Handbook index should have the slug `index`.
     23
     24Example:
     25
     26```json
     27{
     28        "foo": {
     29                "title": "Temporary Foo Title",
     30                "slug": "foo",
     31                "markdown_source": "https://raw.githubusercontent.com/WordPress/doc-repo/master/foo.md",
     32                "parent": null
     33        },
     34        "foo/bar": {
     35                "title": "Temporary Bar Title",
     36                "slug": "bar",
     37                "markdown_source": "https://raw.githubusercontent.com/WordPress/doc-repo/master/foo/bar.md",
     38                "parent": "foo"
     39        },
     40        "foo/bar/quux": {
     41                "title": "Temporary Quux Title",
     42                "slug": "quux",
     43                "markdown_source": "https://raw.githubusercontent.com/WordPress/doc-repo/master/foo/bar/quux.md",
     44                "parent": "foo/bar"
     45        }
     46}
     47```
  • wp-content/plugins/wporg-markdown/inc/class-importer.php

     
     1<?php
     2
     3namespace WordPressdotorg\Markdown;
     4
     5use WP_CLI;
     6use WP_Error;
     7use WP_Post;
     8use WP_Query;
     9use WPCom_GHF_Markdown_Parser;
     10
     11abstract class Importer {
     12        /**
     13         * Meta key to store source in.
     14         *
     15         * @var string
     16         */
     17        protected $meta_key = 'wporg_markdown_source';
     18
     19        /**
     20         * Meta key to store request ETag in.
     21         *
     22         * @var string
     23         */
     24        protected $etag_meta_key = 'wporg_markdown_etag';
     25
     26        /**
     27         * Posts per page to query for.
     28         *
     29         * This needs to be set at least as high as the number of pages being
     30         * imported, but should not be unbounded (-1).
     31         *
     32         * @var int
     33         */
     34        protected $posts_per_page = 350;
     35
     36        /**
     37         * Get base URL for all pages.
     38         *
     39         * This is used for generating the keys for the existing pages.
     40         *
     41         * @see static::get_existing_for_post()
     42         *
     43         * @return string Base URL to strip from page permalink.
     44         */
     45        abstract protected function get_base();
     46
     47        /**
     48         * Get manifest URL.
     49         *
     50         * This URL should point to a JSON file containing the manifest for the
     51         * site's content. (Typically raw.githubusercontent.com)
     52         *
     53         * @return string URL for the manifest file.
     54         */
     55        abstract protected function get_manifest_url();
     56
     57        /**
     58         * Get post type for the type being imported.
     59         *
     60         * @return string Post type slug to import as.
     61         */
     62        abstract protected function get_post_type();
     63
     64        /**
     65         * Get existing data for a given post.
     66         *
     67         * @param WP_Post $post Post to get existing data for.
     68         * @return array 2-tuple of array key and data.
     69         */
     70        protected function get_existing_for_post( WP_Post $post ) {
     71                $key = rtrim( str_replace( $this->get_base(), '', get_permalink( $post->ID ) ), '/' );
     72                if ( empty( $key ) ) {
     73                        $key = 'index';
     74                }
     75
     76                $data = array(
     77                        'post_id' => $post->ID,
     78                );
     79                return array( $key, $data );
     80        }
     81
     82        /**
     83         * Import the manifest.
     84         *
     85         * Fetches the manifest, parses, and creates pages as needed.
     86         */
     87        public function import_manifest() {
     88                $response = wp_remote_get( $this->get_manifest_url() );
     89                if ( is_wp_error( $response ) ) {
     90                        if ( class_exists( 'WP_CLI' ) ) {
     91                                WP_CLI::error( $response->get_error_message() );
     92                        }
     93                        return $response;
     94                } elseif ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
     95                        if ( class_exists( 'WP_CLI' ) ) {
     96                                WP_CLI::error( 'Non-200 from Markdown source' );
     97                        }
     98                        return new WP_Error( 'invalid-http-code', 'Markdown source returned non-200 http code.' );
     99                }
     100                $manifest = json_decode( wp_remote_retrieve_body( $response ), true );
     101                if ( ! $manifest ) {
     102                        if ( class_exists( 'WP_CLI' ) ) {
     103                                WP_CLI::error( 'Invalid manifest' );
     104                        }
     105                        return new WP_Error( 'invalid-manifest', 'Manifest did not unfurl properly.' );;
     106                }
     107                // Fetch all handbook posts for comparison
     108                $q = new WP_Query( array(
     109                        'post_type'      => $this->get_post_type(),
     110                        'post_status'    => 'publish',
     111                        'posts_per_page' => $this->posts_per_page,
     112                ) );
     113                $existing = array();
     114                foreach ( $q->posts as $post ) {
     115                        list( $key, $data ) = $this->get_existing_for_post( $post );
     116                        $existing[ $key ] = $data;
     117                }
     118                $created = $updated = 0;
     119                foreach ( $manifest as $key => $doc ) {
     120                        // Already exists, update.
     121                        if ( ! empty( $existing[ $key ] ) ) {
     122                                $existing_id = $existing[ $key ]['post_id'];
     123                                if ( $this->update_post_from_manifest_doc( $existing_id, $doc ) ) {
     124                                        $updated++;
     125                                }
     126
     127                                continue;
     128                        }
     129                        if ( $this->process_manifest_doc( $doc, $existing, $manifest ) ) {
     130                                $created++;
     131                        }
     132                }
     133                if ( class_exists( 'WP_CLI' ) ) {
     134                        WP_CLI::success( "Successfully created {$created} and updated {$updated} handbook pages." );
     135                }
     136        }
     137
     138        /**
     139         * Process a document from the manifest.
     140         *
     141         * @param array $doc Document to process.
     142         * @param array $existing List of existing posts, will be added to.
     143         * @param array $manifest Manifest data.
     144         * @return boolean True if processing succeeded, false otherwise.
     145         */
     146        protected function process_manifest_doc( $doc, &$existing, $manifest ) {
     147                $post_parent = null;
     148                if ( ! empty( $doc['parent'] ) ) {
     149                        // Find the parent in the existing set
     150                        if ( empty( $existing[ $doc['parent'] ] ) ) {
     151                                if ( ! $this->process_manifest_doc( $manifest[ $doc['parent'] ], $existing, $manifest ) ) {
     152                                        return false;
     153                                }
     154                        }
     155                        if ( ! empty( $existing[ $doc['parent'] ] ) ) {
     156                                $parent = $existing[ $doc['parent'] ];
     157                                $post_parent = $parent['post_id'];
     158                        }
     159                }
     160                $post = $this->create_post_from_manifest_doc( $doc, $post_parent );
     161                if ( $post ) {
     162                        list( $key, $data ) = $this->get_existing_for_post( $post );
     163                        $existing[ $key ] = $data;
     164                        return true;
     165                }
     166                return false;
     167        }
     168
     169        /**
     170         * Create a new handbook page from the manifest document
     171         */
     172        protected function create_post_from_manifest_doc( $doc, $post_parent = null ) {
     173                if ( $doc['slug'] === 'index' ) {
     174                        $doc['slug'] = $this->get_post_type();
     175                }
     176                $post_data = array(
     177                        'post_type'   => $this->get_post_type(),
     178                        'post_status' => 'publish',
     179                        'post_parent' => $post_parent,
     180                        'post_title'  => wp_slash( $doc['slug'] ),
     181                        'post_name'   => sanitize_title_with_dashes( $doc['slug'] ),
     182                );
     183                if ( isset( $doc['title'] ) ) {
     184                        $doc['post_title'] = sanitize_text_field( wp_slash( $doc['title'] ) );
     185                }
     186                $post_id = wp_insert_post( $post_data );
     187                if ( ! $post_id ) {
     188                        return false;
     189                }
     190                if ( class_exists( 'WP_CLI' ) ) {
     191                        WP_CLI::log( "Created post {$post_id} for {$doc['slug']}." );
     192                }
     193                update_post_meta( $post_id, $this->meta_key, esc_url_raw( $doc['markdown_source'] ) );
     194                return get_post( $post_id );
     195        }
     196
     197        /**
     198         * Update an existing post from the manifest.
     199         *
     200         * @param int $post_id Existing post ID.
     201         * @param array $doc Document details from the manifest.
     202         * @return boolean True if updated, false otherwise.
     203         */
     204        protected function update_post_from_manifest_doc( $post_id, $doc ) {
     205                $did_update = update_post_meta( $post_id, $this->meta_key, esc_url_raw( $doc['markdown_source'] ) );
     206                return $did_update;
     207        }
     208
     209        /**
     210         * Update existing posts from Markdown source.
     211         *
     212         * Reparses the Markdown for every page.
     213         */
     214        public function import_all_markdown() {
     215                $q = new WP_Query( array(
     216                        'post_type'      => $this->get_post_type(),
     217                        'post_status'    => 'publish',
     218                        'fields'         => 'ids',
     219                        'posts_per_page' => $this->posts_per_page,
     220                ) );
     221                $ids = $q->posts;
     222                $success = 0;
     223                foreach( $ids as $id ) {
     224                        $ret = $this->update_post_from_markdown_source( $id );
     225                        if ( class_exists( 'WP_CLI' ) ) {
     226                                if ( is_wp_error( $ret ) ) {
     227                                        WP_CLI::warning( $ret->get_error_message() );
     228                                } elseif ( false === $ret ) {
     229                                        WP_CLI::log( "No updates for {$id}" );
     230                                        $success++;
     231                                } else {
     232                                        WP_CLI::log( "Updated {$id} from markdown source" );
     233                                        $success++;
     234                                }
     235                        }
     236                }
     237                if ( class_exists( 'WP_CLI' ) ) {
     238                        $total = count( $ids );
     239                        WP_CLI::success( "Successfully updated {$success} of {$total} pages." );
     240                }
     241        }
     242
     243        /**
     244         * Update a post from its Markdown source.
     245         *
     246         * @param int $post_id Post ID to update.
     247         * @return boolean|WP_Error True if updated, false if no update needed, error otherwise.
     248         */
     249        protected function update_post_from_markdown_source( $post_id ) {
     250                $markdown_source = $this->get_markdown_source( $post_id );
     251                if ( is_wp_error( $markdown_source ) ) {
     252                        return $markdown_source;
     253                }
     254                if ( ! function_exists( 'jetpack_require_lib' ) ) {
     255                        return new WP_Error( 'missing-jetpack-require-lib', 'jetpack_require_lib() is missing on system.' );
     256                }
     257
     258                // Transform GitHub repo HTML pages into their raw equivalents
     259                $markdown_source = preg_replace( '#https?://github\.com/([^/]+/[^/]+)/blob/(.+)#', 'https://raw.githubusercontent.com/$1/$2', $markdown_source );
     260                $markdown_source = add_query_arg( 'v', time(), $markdown_source );
     261
     262                // Grab the stored ETag, and use it to deduplicate.
     263                $args = array(
     264                        'headers' => array(),
     265                );
     266                $last_etag = get_post_meta( $post_id, $this->etag_meta_key, true );
     267                if ( ! empty( $last_etag ) ) {
     268                        $args['headers']['If-None-Match'] = $last_etag;
     269                }
     270
     271                $response = wp_remote_get( $markdown_source, $args );
     272                if ( is_wp_error( $response ) ) {
     273                        return $response;
     274                } elseif ( 304 === wp_remote_retrieve_response_code( $response ) ) {
     275                        // No update required!
     276                        return false;
     277                } elseif ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
     278                        return new WP_Error( 'invalid-http-code', 'Markdown source returned non-200 http code.' );
     279                }
     280
     281                $etag = wp_remote_retrieve_header( $response, 'etag' );
     282
     283                $markdown = wp_remote_retrieve_body( $response );
     284                // Strip YAML doc from the header
     285                $markdown = preg_replace( '#^---(.+)---#Us', '', $markdown );
     286
     287                $title = null;
     288                if ( preg_match( '/^#\s(.+)/', $markdown, $matches ) ) {
     289                        $title = $matches[1];
     290                        $markdown = preg_replace( '/^#\swp\s(.+)/', '', $markdown );
     291                }
     292                $markdown = trim( $markdown );
     293
     294                // Steal the first sentence as the excerpt
     295                $excerpt = '';
     296                if ( preg_match( '/^(.+)/', $markdown, $matches ) ) {
     297                        $excerpt = $matches[1];
     298                        $markdown = preg_replace( '/^(.+)/', '', $markdown );
     299                }
     300
     301                // Transform to HTML and save the post
     302                jetpack_require_lib( 'markdown' );
     303                $parser = new WPCom_GHF_Markdown_Parser();
     304                $html = $parser->transform( $markdown );
     305                $post_data = array(
     306                        'ID'           => $post_id,
     307                        'post_content' => wp_filter_post_kses( wp_slash( $html ) ),
     308                        'post_excerpt' => sanitize_text_field( wp_slash( $excerpt ) ),
     309                );
     310                if ( ! is_null( $title ) ) {
     311                        $post_data['post_title'] = sanitize_text_field( wp_slash( $title ) );
     312                }
     313                wp_update_post( $post_data );
     314
     315                // Set ETag for future updates.
     316                update_post_meta( $post_id, $this->etag_meta_key, wp_slash( $etag ) );
     317
     318                return true;
     319        }
     320
     321        /**
     322         * Retrieve the markdown source URL for a given post.
     323         */
     324        public function get_markdown_source( $post_id ) {
     325                $markdown_source = get_post_meta( $post_id, $this->meta_key, true );
     326                if ( ! $markdown_source ) {
     327                        return new WP_Error( 'missing-markdown-source', 'Markdown source is missing for post.' );
     328                }
     329
     330                return $markdown_source;
     331        }
     332}
  • wp-content/plugins/wporg-markdown/plugin.php

     
     1<?php
     2/**
     3 * Plugin Name: WPORG Markdown Importer
     4 * Description: Automatic Markdown imports for handbooks and DevHub (CLI/API handbooks)
     5 * Author: Daniel Bachhuber and Ryan McCue
     6 */
     7
     8require __DIR__ . '/inc/class-importer.php';
  • wp-content/themes/pub/wporg-developer/functions.php

     
    6161require __DIR__ . '/inc/cli.php';
    6262
    6363/**
     64 * REST API handbook.
     65 */
     66require __DIR__ . '/inc/rest-api.php';
     67
     68/**
    6469 * Explanations for functions. hooks, classes, and methods.
    6570 */
    6671require( __DIR__ . '/inc/explanations.php' );
  • wp-content/themes/pub/wporg-developer/inc/cli.php

     
    11<?php
    22
    3 class DevHub_CLI {
     3use WordPressdotorg\Markdown\Importer;
    44
    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;
     5class DevHub_CLI extends Importer {
     6        /**
     7         * Singleton instance.
     8         *
     9         * @var static
     10         */
     11        protected static $instance;
    912
    10         public static function init() {
    11                 add_action( 'init', array( __CLASS__, 'action_init_register_cron_jobs' ) );
    12                 add_action( 'init', array( __CLASS__, 'action_init_register_post_types' ) );
    13                 add_action( 'pre_get_posts', array( __CLASS__, 'action_pre_get_posts' ) );
    14                 add_action( 'devhub_cli_manifest_import', array( __CLASS__, 'action_devhub_cli_manifest_import' ) );
    15                 add_action( 'devhub_cli_markdown_import', array( __CLASS__, 'action_devhub_cli_markdown_import' ) );
     13        /**
     14         * Meta key to store source in.
     15         *
     16         * Overridden for compatibility.
     17         *
     18         * @var string
     19         */
     20        protected $meta_key = 'wporg_cli_markdown_source';
     21
     22        /**
     23         * Get the singleton instance, or create if needed.
     24         *
     25         * @return static
     26         */
     27        public static function instance() {
     28                if ( empty( static::$instance ) ) {
     29                        static::$instance = new static();
     30                }
     31
     32                return static::$instance;
    1633        }
    1734
     35        public function init() {
     36                add_action( 'init', array( $this, 'action_init_register_cron_jobs' ) );
     37                add_action( 'init', array( $this, 'action_init_register_post_types' ) );
     38                add_action( 'pre_get_posts', array( $this, 'action_pre_get_posts' ) );
     39                add_action( 'devhub_cli_manifest_import', array( $this, 'import_manifest' ) );
     40                add_action( 'devhub_cli_markdown_import', array( $this, 'import_all_markdown' ) );
     41        }
     42
     43        protected function get_base() {
     44                return home_url( 'cli/commands/' );
     45        }
     46
     47        protected function get_manifest_url() {
     48                return 'https://raw.githubusercontent.com/wp-cli/handbook/master/bin/commands-manifest.json';
     49        }
     50
     51        protected function get_post_type() {
     52                return 'command';
     53        }
     54
    1855        public static function action_init_register_cron_jobs() {
    1956                if ( ! wp_next_scheduled( 'devhub_cli_manifest_import' ) ) {
    2057                        wp_schedule_event( time(), 'twicedaily', 'devhub_cli_manifest_import' );
     
    70107                        $query->set( 'posts_per_page', 250 );
    71108                }
    72109        }
    73 
    74         public static function action_devhub_cli_manifest_import() {
    75                 $response = wp_remote_get( self::$commands_manifest );
    76                 if ( is_wp_error( $response ) ) {
    77                         return $response;
    78                 } elseif ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
    79                         return new WP_Error( 'invalid-http-code', 'Markdown source returned non-200 http code.' );
    80                 }
    81                 $manifest = json_decode( wp_remote_retrieve_body( $response ), true );
    82                 if ( ! $manifest ) {
    83                         return new WP_Error( 'invalid-manifest', 'Manifest did not unfurl properly.' );;
    84                 }
    85                 // Fetch all handbook posts for comparison
    86                 $q = new WP_Query( array(
    87                         'post_type'      => self::$supported_post_types,
    88                         'post_status'    => 'publish',
    89                         'posts_per_page' => self::$posts_per_page,
    90                 ) );
    91                 $existing = array();
    92                 foreach( $q->posts as $post ) {
    93                         $cmd_path = rtrim( str_replace( home_url( 'cli/commands/' ), '', get_permalink( $post->ID ) ), '/' );
    94                         $existing[ $cmd_path ] = array(
    95                                 'post_id'   => $post->ID,
    96                                 'cmd_path'  => $cmd_path,
    97                         );
    98                 }
    99                 $created = 0;
    100                 foreach( $manifest as $doc ) {
    101                         // Already exists
    102                         if ( wp_filter_object_list( $existing, array( 'cmd_path' => $doc['cmd_path'] ) ) ) {
    103                                 continue;
    104                         }
    105                         if ( self::process_manifest_doc( $doc, $existing, $manifest ) ) {
    106                                 $created++;
    107                         }
    108                 }
    109                 if ( class_exists( 'WP_CLI' ) ) {
    110                         \WP_CLI::success( "Successfully created {$created} handbook pages." );
    111                 }
    112         }
    113 
    114         private static function process_manifest_doc( $doc, &$existing, $manifest ) {
    115                 $post_parent = null;
    116                 if ( ! empty( $doc['parent'] ) ) {
    117                         // Find the parent in the existing set
    118                         $parents = wp_filter_object_list( $existing, array( 'cmd_path' => $doc['parent'] ) );
    119                         if ( empty( $parents ) ) {
    120                                 if ( ! self::process_manifest_doc( $manifest[ $doc['parent'] ], $existing, $manifest ) ) {
    121                                         return;
    122                                 }
    123                                 $parents = wp_filter_object_list( $existing, array( 'cmd_path' => $doc['parent'] ) );
    124                         }
    125                         if ( ! empty( $parents ) ) {
    126                                 $parent = array_shift( $parents );
    127                                 $post_parent = $parent['post_id'];
    128                         }
    129                 }
    130                 $post = self::create_post_from_manifest_doc( $doc, $post_parent );
    131                 if ( $post ) {
    132                         $cmd_path = rtrim( str_replace( home_url( 'cli/commands/' ), '', get_permalink( $post->ID ) ), '/' );
    133                         $existing[ $cmd_path ] = array(
    134                                 'post_id'   => $post->ID,
    135                                 'cmd_path'  => $cmd_path,
    136                         );
    137                         return true;
    138                 }
    139                 return false;
    140         }
    141 
    142         public static function action_devhub_cli_markdown_import() {
    143                 $q = new WP_Query( array(
    144                         'post_type'      => self::$supported_post_types,
    145                         'post_status'    => 'publish',
    146                         'fields'         => 'ids',
    147                         'posts_per_page' => self::$posts_per_page,
    148                 ) );
    149                 $ids = $q->posts;
    150                 $success = 0;
    151                 foreach( $ids as $id ) {
    152                         $ret = self::update_post_from_markdown_source( $id );
    153                         if ( class_exists( 'WP_CLI' ) ) {
    154                                 if ( is_wp_error( $ret ) ) {
    155                                         \WP_CLI::warning( $ret->get_error_message() );
    156                                 } else {
    157                                         \WP_CLI::log( "Updated {$id} from markdown source" );
    158                                         $success++;
    159                                 }
    160                         }
    161                 }
    162                 if ( class_exists( 'WP_CLI' ) ) {
    163                         $total = count( $ids );
    164                         \WP_CLI::success( "Successfully updated {$success} of {$total} CLI command pages." );
    165                 }
    166         }
    167 
    168         /**
    169          * Create a new handbook page from the manifest document
    170          */
    171         private static function create_post_from_manifest_doc( $doc, $post_parent = null ) {
    172                 $post_data = array(
    173                         'post_type'   => 'command',
    174                         'post_status' => 'publish',
    175                         'post_parent' => $post_parent,
    176                         'post_title'  => sanitize_text_field( wp_slash( $doc['title'] ) ),
    177                         'post_name'   => sanitize_title_with_dashes( $doc['slug'] ),
    178                 );
    179                 $post_id = wp_insert_post( $post_data );
    180                 if ( ! $post_id ) {
    181                         return false;
    182                 }
    183                 if ( class_exists( 'WP_CLI' ) ) {
    184                         \WP_CLI::log( "Created post {$post_id} for {$doc['title']}." );
    185                 }
    186                 update_post_meta( $post_id, self::$meta_key, esc_url_raw( $doc['markdown_source'] ) );
    187                 return get_post( $post_id );
    188         }
    189 
    190         /**
    191          * Update a post from its Markdown source
    192          */
    193         private static function update_post_from_markdown_source( $post_id ) {
    194                 $markdown_source = self::get_markdown_source( $post_id );
    195                 if ( is_wp_error( $markdown_source ) ) {
    196                         return $markdown_source;
    197                 }
    198                 if ( ! function_exists( 'jetpack_require_lib' ) ) {
    199                         return new WP_Error( 'missing-jetpack-require-lib', 'jetpack_require_lib() is missing on system.' );
    200                 }
    201 
    202                 // Transform GitHub repo HTML pages into their raw equivalents
    203                 $markdown_source = preg_replace( '#https?://github\.com/([^/]+/[^/]+)/blob/(.+)#', 'https://raw.githubusercontent.com/$1/$2', $markdown_source );
    204                 $markdown_source = add_query_arg( 'v', time(), $markdown_source );
    205                 $response = wp_remote_get( $markdown_source );
    206                 if ( is_wp_error( $response ) ) {
    207                         return $response;
    208                 } elseif ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
    209                         return new WP_Error( 'invalid-http-code', 'Markdown source returned non-200 http code.' );
    210                 }
    211 
    212                 $markdown = wp_remote_retrieve_body( $response );
    213                 // Strip YAML doc from the header
    214                 $markdown = preg_replace( '#^---(.+)---#Us', '', $markdown );
    215 
    216                 $title = null;
    217                 if ( preg_match( '/^#\s(.+)/', $markdown, $matches ) ) {
    218                         $title = $matches[1];
    219                         $markdown = preg_replace( '/^#\swp\s(.+)/', '', $markdown );
    220                 }
    221                 $markdown = trim( $markdown );
    222 
    223                 // Steal the first sentence as the excerpt
    224                 $excerpt = '';
    225                 if ( preg_match( '/^(.+)/', $markdown, $matches ) ) {
    226                         $excerpt = $matches[1];
    227                         $markdown = preg_replace( '/^(.+)/', '', $markdown );
    228                 }
    229 
    230                 // Transform to HTML and save the post
    231                 jetpack_require_lib( 'markdown' );
    232                 $parser = new \WPCom_GHF_Markdown_Parser;
    233                 $html = $parser->transform( $markdown );
    234                 $post_data = array(
    235                         'ID'           => $post_id,
    236                         'post_content' => wp_filter_post_kses( wp_slash( $html ) ),
    237                         'post_excerpt' => sanitize_text_field( wp_slash( $excerpt ) ),
    238                 );
    239                 if ( ! is_null( $title ) ) {
    240                         $post_data['post_title'] = sanitize_text_field( wp_slash( $title ) );
    241                 }
    242                 wp_update_post( $post_data );
    243                 return true;
    244         }
    245 
    246         /**
    247          * Retrieve the markdown source URL for a given post.
    248          */
    249         public static function get_markdown_source( $post_id ) {
    250                 $markdown_source = get_post_meta( $post_id, self::$meta_key, true );
    251                 if ( ! $markdown_source ) {
    252                         return new WP_Error( 'missing-markdown-source', 'Markdown source is missing for post.' );
    253                 }
    254 
    255                 return $markdown_source;
    256         }
    257 
    258110}
    259111
    260 DevHub_CLI::init();
    261 
     112DevHub_CLI::instance()->init();
  • wp-content/themes/pub/wporg-developer/inc/rest-api.php

     
     1<?php
     2
     3use WordPressdotorg\Markdown\Importer;
     4
     5class DevHub_REST_API extends Importer {
     6        /**
     7         * Singleton instance.
     8         *
     9         * @var static
     10         */
     11        protected static $instance;
     12
     13        /**
     14         * Get the singleton instance, or create if needed.
     15         *
     16         * @return static
     17         */
     18        public static function instance() {
     19                if ( empty( static::$instance ) ) {
     20                        static::$instance = new static();
     21                }
     22
     23                return static::$instance;
     24        }
     25
     26        protected function get_base() {
     27                return home_url( 'rest-api/' );
     28        }
     29
     30        protected function get_manifest_url() {
     31                return 'https://raw.githubusercontent.com/WP-API/docs/master/bin/manifest.json';
     32        }
     33
     34        protected function get_post_type() {
     35                return 'rest-api-handbook';
     36        }
     37
     38        public function init() {
     39                add_action( 'init', array( $this, 'register_cron_jobs' ) );
     40                add_action( 'restapi_import_manifest', array( $this, 'import_manifest' ) );
     41                add_action( 'restapi_import_all_markdown', array( $this, 'import_all_markdown' ) );
     42        }
     43
     44        public function register_cron_jobs() {
     45                if ( ! wp_next_scheduled( 'restapi_import_manifest' ) ) {
     46                        wp_schedule_event( time(), '15_minutes', 'restapi_import_manifest' );
     47                }
     48                if ( ! wp_next_scheduled( 'restapi_import_all_markdown' ) ) {
     49                        wp_schedule_event( time(), '15_minutes', 'restapi_import_all_markdown' );
     50                }
     51        }
     52}
     53
     54DevHub_REST_API::instance()->init();
     55