admin = new WordPress_GitHub_Sync_Admin; } } /** * Init i18n files */ function l10n() { load_plugin_textdomain( self::$text_domain, false, plugin_basename( dirname( __FILE__ ) ) . '/languages/' ); } /** * Returns the repository to sync with */ function repository() { return get_option( "wpghs_repository" ); } /** * Returns the user's oauth token */ function oauth_token() { return get_option( "wpghs_oauth_token" ); } /** * Returns the GitHub host to sync with (for GitHub Enterprise support) */ function api_base() { return get_option( "wpghs_host" ); } /** * Returns the Webhook secret */ function secret() { return get_option( "wpghs_secret" ); } /** * Callback triggered on post save, used to initiate an outbound sync * * $post_id - (int) the post to sync */ function save_post_callback($post_id) { if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) return; if ( ! $this->oauth_token() || ! $this->repository() ) return $post = get_post($post_id); // Right now CPTs are not supported if ($post->post_type != "page" && $post->post_type != "post") return; // Not yet published if ($post->post_status != "publish") return; $post = new WordPress_GitHub_Sync_Post($post_id); $post->push(); } /** * Callback triggered on post delete, used to initiate an outbound sync * * $post_id - (int) the post to delete */ function delete_post_callback( $post_id ) { if ( ! $this->oauth_token() || ! $this->repository() ) return $post = get_post($post_id); // Right now CPTs are not supported if ($post->post_type != "page" && $post->post_type != "post") return; $post = new WordPress_GitHub_Sync_Post($post_id); $post->delete(); } /** * Webhook callback as trigered from GitHub push * Reads the payload and syncs posts as necessary */ function pull_posts() { # Prevent pushes on update $this->push_lock = true; $raw_data = file_get_contents('php://input'); $headers = $this->headers(); // validate secret $hash = hash_hmac( "sha1", $raw_data, $this->secret() ); if ( $headers["X-Hub-Signature"] != "sha1=" . $hash ) wp_die( __("Failed to validate secret.", WordPress_GitHub_Sync::$text_domain) ); $data = json_decode($raw_data); $nwo = $data->repository->owner->name . "/" . $data->repository->name; if ( $nwo != $this->repository() ) wp_die( $nwo . __(" is an invalid repository", WordPress_GitHub_Sync::$text_domain) ); $modified = $added = $removed = array(); foreach ($data->commits as $commit) { $modified = array_merge( $modified, $commit->modified ); $added = array_merge( $added, $commit->added ); $removed = array_merge( $removed, $commit->removed ); } // Added or Modified (pull) $to_pull = array_merge($modified, $added); foreach (array_unique($to_pull) as $path) { $post = new WordPress_GitHub_Sync_Post($path); $post->pull(); } // Removed foreach (array_unique($removed) as $path) { $post = new WordPress_GitHub_Sync_Post($path); wp_delete_post($post->id); } } /** * Cross-server header support * Returns an array of the request's headers */ function headers() { if (function_exists('getallheaders')) return getallheaders(); // Nginx and pre 5.4 workaround // http://www.php.net/manual/en/function.getallheaders.php $headers = array(); foreach ($_SERVER as $name => $value) { if (substr($name, 0, 5) == 'HTTP_') { $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; } } return $headers; } /** * Get posts to export, set and kick off cronjob */ function start_export() { global $wpdb; $posts = get_option( '_wpghs_posts_to_export' ); if ( ! $posts ) { $posts = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_status = 'publish' AND post_type IN ('post', 'page' )" ); } else { delete_option( '_wpghs_posts_to_export' ); } wp_schedule_single_event(time(), 'wpghs_export', array($posts)); $this->log( __("Starting export to GitHub", WordPress_GitHub_Sync::$text_domain ) ); spawn_cron(); update_option( '_wpghs_export_started', 'yes' ); } /** * Export posts * * Runs as cronjob */ function export_posts($posts) { $i = 0; while(!empty($posts) && $i < 50) { $post_id = array_shift($posts); $this->log( __("Exporting Post ID: ", WordPress_GitHub_Sync::$text_domain ) . $post_id ); $post = new WordPress_GitHub_Sync_Post($post_id); $result = $post->push(); if ( is_wp_error( $result ) ) { update_option( '_wpghs_posts_to_export', $posts ); update_option( '_wpghs_export_error', $result->get_error_message() ); $this->log( __("Error exporting to GitHub. Error:", WordPress_GitHub_Sync::$text_domain ) ); $this->log( $result->get_error_message() ); die(); } usleep(500000); $i++; } if (!empty($posts)) { $nonce = wp_hash( time() ); update_option( '_wpghs_export_nonce', $nonce ); // Request page that will continue export wp_remote_post( add_query_arg( 'github', 'sync', site_url( 'index.php' ) ), array( 'body' => array( 'posts' => $posts, 'nonce' => $nonce, ), 'blocking' => false, ) ); } else { update_option( '_wpghs_export_complete', 'yes' ); $this->log( __('Export to GitHub completed successfully.', WordPress_GitHub_Sync::$text_domain ) ); } die(); } /** * Receives the remaining posts * * Kicks off exporting the next batch */ function continue_export() { if ( ! isset( $_GET['github'] ) || 'sync' !== $_GET['github'] ) { return; } if ( !array_key_exists('nonce', $_POST) || "" === get_option( '_wpghs_export_nonce') || get_option( '_wpghs_export_nonce') !== $_POST['nonce'] ) { return; } delete_option( '_wpghs_export_nonce' ); if ( !array_key_exists('posts', $_POST) || !is_array($_POST['posts']) ) { return; } $posts = $_POST['posts']; $this->export_posts($posts); } /** * Write to debug.log if WP_DEBUG is enabled * @source http://www.stumiller.me/sending-output-to-the-wordpress-debug-log/ */ function log($log) { if ( true === WP_DEBUG ) { if ( is_array( $log ) || is_object( $log ) ) { error_log( print_r( $log, true ) ); } else { error_log( $log ); } } } } $wpghs = new WordPress_GitHub_Sync;