Changeset 1713007
- Timestamp:
- 08/14/2017 08:18:41 AM (8 years ago)
- Location:
- writing-on-github/trunk
- Files:
-
- 39 added
- 8 deleted
- 19 edited
Legend:
- Unmodified
- Added
- Removed
-
writing-on-github/trunk/lib/admin.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Admin { 11 11 12 /** 13 * Hook into GitHub API 14 */ 15 public function __construct() { 16 add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); 17 add_action( 'admin_init', array( $this, 'register_settings' ) ); 18 add_action( 'current_screen', array( $this, 'trigger_cron' ) ); 19 } 20 21 /** 22 * Callback to render the settings page view 23 */ 24 public function settings_page() { 25 include dirname( dirname( __FILE__ ) ) . '/views/options.php'; 26 } 27 28 /** 29 * Callback to register the plugin's options 30 */ 31 public function register_settings() { 32 add_settings_section( 33 'general', 34 'General Settings', 35 array( $this, 'section_callback' ), 36 Writing_On_GitHub::$text_domain 37 ); 38 39 register_setting( Writing_On_GitHub::$text_domain, 'wogh_host' ); 40 add_settings_field( 'wogh_host', __( 'GitHub hostname', 'writing-on-github' ), array( $this, 'field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 41 'default' => 'https://api.github.com', 42 'name' => 'wogh_host', 43 'help_text' => __( 'The GitHub host to use. This only needs to be changed to support a GitHub Enterprise installation.', 'writing-on-github' ), 44 ) 45 ); 46 47 register_setting( Writing_On_GitHub::$text_domain, 'wogh_repository' ); 48 add_settings_field( 'wogh_repository', __( 'Repository', 'writing-on-github' ), array( $this, 'field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 49 'default' => '', 50 'name' => 'wogh_repository', 51 'help_text' => __( 'The GitHub repository to commit to, with owner (<code>[OWNER]/[REPOSITORY]</code>), e.g., <code>github/hubot.github.com</code>. The repository should contain an initial commit, which is satisfied by including a README when you create the repository on GitHub.', 'writing-on-github' ), 52 ) 53 ); 54 55 register_setting( Writing_On_GitHub::$text_domain, 'wogh_branch' ); 56 add_settings_field( 'wogh_branch', __( 'Branch', 'writing-on-github' ), array( $this, 'field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 57 'default' => 'master', 58 'name' => 'wogh_branch', 59 'help_text' => __( 'The GitHub branch to commit to, default is master.', 'writing-on-github' ), 60 ) 61 ); 62 63 register_setting( Writing_On_GitHub::$text_domain, 'wogh_oauth_token' ); 64 add_settings_field( 'wogh_oauth_token', __( 'Oauth Token', 'writing-on-github' ), array( $this, 'field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 65 'default' => '', 66 'name' => 'wogh_oauth_token', 67 'help_text' => __( "A <a href='https://github.com/settings/tokens/new'>personal oauth token</a> with <code>public_repo</code> scope.", 'writing-on-github' ), 68 ) 69 ); 70 71 register_setting( Writing_On_GitHub::$text_domain, 'wogh_secret' ); 72 add_settings_field( 'wogh_secret', __( 'Webhook Secret', 'writing-on-github' ), array( $this, 'field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 73 'default' => '', 74 'name' => 'wogh_secret', 75 'help_text' => __( "The webhook's secret phrase. This should be password strength, as it is used to verify the webhook's payload.", 'writing-on-github' ), 76 ) 77 ); 78 79 register_setting( Writing_On_GitHub::$text_domain, 'wogh_default_user' ); 80 add_settings_field( 'wogh_default_user', __( 'Default Import User', 'writing-on-github' ), array( &$this, 'user_field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 81 'default' => '', 82 'name' => 'wogh_default_user', 83 'help_text' => __( 'The fallback user for import, in case Writing On GitHub cannot find the committer in the database.', 'writing-on-github' ), 84 ) 85 ); 12 /** 13 * plugin file name rel plugin dir. 14 * @var string 15 */ 16 protected $plugin_file; 17 18 /** 19 * Hook into GitHub API 20 */ 21 public function __construct( $plugin_file ) { 22 $this->plugin_file = $plugin_file; 23 24 add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); 25 add_action( 'admin_init', array( $this, 'register_settings' ) ); 26 add_action( 'current_screen', array( $this, 'trigger_cron' ) ); 27 add_filter( 'plugin_action_links', array($this, 'settings_links'), 10, 2 ); 28 } 29 30 /** 31 * settings link 32 * @param string[] $links 33 * @param string $file 34 * @return string[] 35 */ 36 public function settings_links( $links, $file ) { 37 if ( $file != $this->plugin_file ) { 38 return $links; 39 } 40 41 $settings_link = '<a href="options-general.php?page=' . 42 Writing_On_GitHub::$text_domain . '">' . __( 'Settings', 'writing-on-github' ) . '</a>'; 43 44 array_push( $links, $settings_link ); 45 46 return $links; 47 } 48 49 /** 50 * Callback to render the settings page view 51 */ 52 public function settings_page() { 53 include dirname( dirname( __FILE__ ) ) . '/views/options.php'; 54 } 55 56 /** 57 * Callback to register the plugin's options 58 */ 59 public function register_settings() { 60 add_settings_section( 61 'general', 62 'General Settings', 63 array( $this, 'section_callback' ), 64 Writing_On_GitHub::$text_domain 65 ); 66 67 register_setting( Writing_On_GitHub::$text_domain, 'wogh_host' ); 68 add_settings_field( 'wogh_host', __( 'GitHub hostname', 'writing-on-github' ), array( $this, 'field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 69 'default' => 'https://api.github.com', 70 'name' => 'wogh_host', 71 'help_text' => __( 'The GitHub host to use. This only needs to be changed to support a GitHub Enterprise installation.', 'writing-on-github' ), 72 ) 73 ); 74 75 register_setting( Writing_On_GitHub::$text_domain, 'wogh_repository' ); 76 add_settings_field( 'wogh_repository', __( 'Repository', 'writing-on-github' ), array( $this, 'field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 77 'default' => '', 78 'name' => 'wogh_repository', 79 'help_text' => __( 'The GitHub repository to commit to, with owner (<code>[OWNER]/[REPOSITORY]</code>), e.g., <code>github/hubot.github.com</code>. The repository should contain an initial commit, which is satisfied by including a README when you create the repository on GitHub.', 'writing-on-github' ), 80 ) 81 ); 82 83 register_setting( Writing_On_GitHub::$text_domain, 'wogh_branch' ); 84 add_settings_field( 'wogh_branch', __( 'Branch', 'writing-on-github' ), array( $this, 'field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 85 'default' => 'master', 86 'name' => 'wogh_branch', 87 'help_text' => __( 'The GitHub branch to commit to, default is master.', 'writing-on-github' ), 88 ) 89 ); 90 91 register_setting( Writing_On_GitHub::$text_domain, 'wogh_oauth_token' ); 92 add_settings_field( 'wogh_oauth_token', __( 'Oauth Token', 'writing-on-github' ), array( $this, 'field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 93 'default' => '', 94 'name' => 'wogh_oauth_token', 95 'help_text' => __( "A <a href='https://github.com/settings/tokens/new'>personal oauth token</a> with <code>public_repo</code> scope.", 'writing-on-github' ), 96 ) 97 ); 98 99 register_setting( Writing_On_GitHub::$text_domain, 'wogh_secret' ); 100 add_settings_field( 'wogh_secret', __( 'Webhook Secret', 'writing-on-github' ), array( $this, 'field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 101 'default' => '', 102 'name' => 'wogh_secret', 103 'help_text' => __( "The webhook's secret phrase. This should be password strength, as it is used to verify the webhook's payload.", 'writing-on-github' ), 104 ) 105 ); 106 107 register_setting( Writing_On_GitHub::$text_domain, 'wogh_default_user' ); 108 add_settings_field( 'wogh_default_user', __( 'Default Import User', 'writing-on-github' ), array( &$this, 'user_field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 109 'default' => '', 110 'name' => 'wogh_default_user', 111 'help_text' => __( 'The fallback user for import, in case Writing On GitHub cannot find the committer in the database.', 'writing-on-github' ), 112 ) 113 ); 86 114 87 115 register_setting( Writing_On_GitHub::$text_domain, 'wogh_ignore_author' ); … … 93 121 ); 94 122 95 // register_setting( Writing_On_GitHub::$text_domain, 'wogh_ignore_metas' ); 96 // add_settings_field( 'wogh_ignore_metas', __( 'Ignore post metas', 'writing-on-github' ), array( &$this, 'textarea_field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 97 // 'default' => '', 98 // 'name' => 'wogh_ignore_metas', 99 // 'help_text' => __( 'These meta keys will be ignored and cannot be imported and exported. One meta key per line.', 'writing-on-github' ), 100 // ) 101 // ); 102 } 103 104 /** 105 * Callback to render an individual options field 106 * 107 * @param array $args Field arguments. 108 */ 109 public function field_callback( $args ) { 110 include dirname( dirname( __FILE__ ) ) . '/views/setting-field.php'; 111 } 112 113 /** 114 * Callback to render the default import user field. 115 * 116 * @param array $args Field arguments. 117 */ 118 public function user_field_callback( $args ) { 119 include dirname( dirname( __FILE__ ) ) . '/views/user-setting-field.php'; 120 } 121 122 /** 123 * Callback to render the textarea field. 124 * 125 * @param array $args Field arguments. 126 */ 127 public function textarea_field_callback( $args ) { 128 include dirname( dirname( __FILE__ ) ) . '/views/textarea-setting-field.php'; 129 } 123 register_setting( Writing_On_GitHub::$text_domain, 'wogh_dont_export_content' ); 124 add_settings_field( 'wogh_dont_export_content', __( 'Don\'t export content', 'writing-on-github' ), array( &$this, 'checkbox_field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 125 'default' => '', 126 'name' => 'wogh_dont_export_content', 127 'help_text' => __( 'Do not export post content to github, only export meta.', 'writing-on-github' ), 128 ) 129 ); 130 131 // register_setting( Writing_On_GitHub::$text_domain, 'wogh_ignore_metas' ); 132 // add_settings_field( 'wogh_ignore_metas', __( 'Ignore post metas', 'writing-on-github' ), array( &$this, 'textarea_field_callback' ), Writing_On_GitHub::$text_domain, 'general', array( 133 // 'default' => '', 134 // 'name' => 'wogh_ignore_metas', 135 // 'help_text' => __( 'These meta keys will be ignored and cannot be imported and exported. One meta key per line.', 'writing-on-github' ), 136 // ) 137 // ); 138 } 139 140 /** 141 * Callback to render an individual options field 142 * 143 * @param array $args Field arguments. 144 */ 145 public function field_callback( $args ) { 146 include dirname( dirname( __FILE__ ) ) . '/views/setting-field.php'; 147 } 148 149 /** 150 * Callback to render the default import user field. 151 * 152 * @param array $args Field arguments. 153 */ 154 public function user_field_callback( $args ) { 155 include dirname( dirname( __FILE__ ) ) . '/views/user-setting-field.php'; 156 } 157 158 /** 159 * Callback to render the textarea field. 160 * 161 * @param array $args Field arguments. 162 */ 163 public function textarea_field_callback( $args ) { 164 include dirname( dirname( __FILE__ ) ) . '/views/textarea-setting-field.php'; 165 } 130 166 131 167 /** … … 138 174 } 139 175 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 176 /** 177 * Displays settings messages from background processes 178 */ 179 public function section_callback() { 180 if ( get_current_screen()->id !== 'settings_page_' . Writing_On_GitHub::$text_domain ) { 181 return; 182 } 183 184 if ( 'yes' === get_option( '_wogh_export_started' ) ) { ?> 185 <div class="updated"> 186 <p><?php esc_html_e( 'Export to GitHub started.', 'writing-on-github' ); ?></p> 187 </div><?php 188 delete_option( '_wogh_export_started' ); 189 } 190 191 if ( $message = get_option( '_wogh_export_error' ) ) { ?> 192 <div class="error"> 193 <p><?php esc_html_e( 'Export to GitHub failed with error:', 'writing-on-github' ); ?> <?php echo esc_html( $message );?></p> 194 </div><?php 195 delete_option( '_wogh_export_error' ); 196 } 197 198 if ( 'yes' === get_option( '_wogh_export_complete' ) ) { ?> 199 <div class="updated"> 200 <p><?php esc_html_e( 'Export to GitHub completed successfully.', 'writing-on-github' );?></p> 201 </div><?php 202 delete_option( '_wogh_export_complete' ); 203 } 204 205 if ( 'yes' === get_option( '_wogh_import_started' ) ) { ?> 206 <div class="updated"> 207 <p><?php esc_html_e( 'Import from GitHub started.', 'writing-on-github' ); ?></p> 208 </div><?php 209 delete_option( '_wogh_import_started' ); 210 } 211 212 if ( $message = get_option( '_wogh_import_error' ) ) { ?> 213 <div class="error"> 214 <p><?php esc_html_e( 'Import from GitHub failed with error:', 'writing-on-github' ); ?> <?php echo esc_html( $message );?></p> 215 </div><?php 216 delete_option( '_wogh_import_error' ); 217 } 218 219 if ( 'yes' === get_option( '_wogh_import_complete' ) ) { ?> 220 <div class="updated"> 221 <p><?php esc_html_e( 'Import from GitHub completed successfully.', 'writing-on-github' );?></p> 222 </div><?php 223 delete_option( '_wogh_import_complete' ); 224 } 225 } 226 227 /** 228 * Add options menu to admin navbar 229 */ 230 public function add_admin_menu() { 231 add_options_page( 232 __( 'Writing On GitHub', 'writing-on-github' ), 233 __( 'Writing On GitHub', 'writing-on-github' ), 234 'manage_options', 235 Writing_On_GitHub::$text_domain, 236 array( $this, 'settings_page' ) 237 ); 238 } 239 240 /** 241 * Admin callback to trigger import/export because WordPress admin routing lol 242 */ 243 public function trigger_cron() { 244 if ( ! current_user_can( 'manage_options' ) ) { 245 return; 246 } 247 248 if ( get_current_screen()->id !== 'settings_page_' . Writing_On_GitHub::$text_domain ) { 249 return; 250 } 251 252 if ( ! isset( $_GET['action'] ) ) { 253 return; 254 } 255 256 if ( 'export' === $_GET['action'] ) { 257 Writing_On_GitHub::$instance->start_export(); 258 } 223 259 224 260 if ( 'force_export' === $_GET['action'] ) { … … 226 262 } 227 263 228 229 230 231 232 233 234 264 if ( 'import' === $_GET['action'] ) { 265 Writing_On_GitHub::$instance->start_import(); 266 } 267 268 wp_redirect( admin_url( 'options-general.php?page=writing-on-github' ) ); 269 die; 270 } 235 271 } -
writing-on-github/trunk/lib/api.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Api { 11 11 12 13 14 15 16 17 12 /** 13 * Application container. 14 * 15 * @var Writing_On_GitHub 16 */ 17 protected $app; 18 18 19 20 21 22 23 24 19 /** 20 * GitHub fetch client. 21 * 22 * @var Writing_On_GitHub_Fetch_Client 23 */ 24 protected $fetch; 25 25 26 27 28 29 30 31 26 /** 27 * Github persist client. 28 * 29 * @var Writing_On_GitHub_Persist_Client 30 */ 31 protected $persist; 32 32 33 34 35 36 37 38 39 40 33 /** 34 * Instantiates a new Api object. 35 * 36 * @param Writing_On_GitHub $app Application container. 37 */ 38 public function __construct( Writing_On_GitHub $app ) { 39 $this->app = $app; 40 } 41 41 42 43 44 45 46 47 48 49 50 42 /** 43 * Lazy-load fetch client. 44 * 45 * @return Writing_On_GitHub_Fetch_Client 46 */ 47 public function fetch() { 48 if ( ! $this->fetch ) { 49 $this->fetch = new Writing_On_GitHub_Fetch_Client( $this->app ); 50 } 51 51 52 53 52 return $this->fetch; 53 } 54 54 55 56 57 58 59 60 61 62 63 55 /** 56 * Lazy-load persist client. 57 * 58 * @return Writing_On_GitHub_Persist_Client 59 */ 60 public function persist() { 61 if ( ! $this->persist ) { 62 $this->persist = new Writing_On_GitHub_Persist_Client( $this->app ); 63 } 64 64 65 66 65 return $this->persist; 66 } 67 67 } -
writing-on-github/trunk/lib/blob.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Blob { 11 11 12 /** 13 * Complete blob content. 14 * 15 * @var string 16 */ 17 protected $content; 18 19 /** 20 * Blob sha. 21 * 22 * @var string 23 */ 24 protected $sha; 25 26 /** 27 * Blob path. 28 * 29 * @var string 30 */ 31 protected $path; 32 33 /** 34 * Post id. 35 * 36 * @var int 37 */ 38 protected $id; 39 40 /** 41 * Whether the blob has frontmatter. 42 * 43 * @var boolean 44 */ 45 protected $frontmatter = false; 46 47 /** 48 * Instantiates a new Blob object. 49 * 50 * @param stdClass $data Raw blob data. 51 */ 52 public function __construct( stdClass $data ) { 53 $this->interpret_data( $data ); 54 } 55 56 public function id() { 57 return $this->id; 58 } 59 60 public function set_id($id) { 61 $this->id = $id; 62 } 63 64 /** 65 * Returns the raw blob content. 66 * 67 * @return string 68 */ 69 public function content() { 70 return $this->content; 71 } 72 73 /** 74 * Set's the blob's content. 75 * 76 * @param string $content Raw blob content. 77 * @param bool $base64 Whether the content is base64 encoded. 78 * 79 * @return $this 80 */ 81 public function set_content( $content, $base64 = false ) { 82 if ( $base64 ) { 83 $content = base64_decode( $content ); 84 } 85 86 $this->frontmatter = '---' === substr( $this->content = $content, 0, 3 ) ? true : false; 87 88 return $this; 89 } 90 /** 91 * Returns the blob sha. 92 * 93 * @return string 94 */ 95 public function sha() { 96 return $this->sha; 97 } 98 99 /** 100 * Return's the blob path. 101 * 102 * @return string 103 */ 104 public function path() { 105 return $this->path; 106 } 107 108 /** 109 * Whether the blob has frontmatter. 110 * 111 * @return bool 112 */ 113 public function has_frontmatter() { 114 return $this->frontmatter; 115 } 116 117 /** 118 * Returns the formatted/filtered blob content used for import. 119 * 120 * @return string 121 */ 122 public function content_import() { 123 $content = $this->content(); 124 125 if ( $this->has_frontmatter() ) { 126 // Break out content. 127 preg_match( '/(^---(.*?)---$)?(.*)/ms', $content, $matches ); 128 $content = array_pop( $matches ); 129 } 130 131 if ( function_exists( 'wpmarkdown_markdown_to_html' ) ) { 132 $content = wpmarkdown_markdown_to_html( $content ); 133 } 134 135 /** 136 * Filters the content for import. 137 */ 138 return apply_filters( 'wogh_content_import', trim( $content ) ); 139 } 140 141 /** 142 * Returns the blob meta. 143 * 144 * @return array 145 */ 146 public function meta() { 147 $meta = array(); 148 149 if ( $this->has_frontmatter() ) { 150 // Break out meta, if present. 151 preg_match( '/(^---(.*?)---$)?(.*)/ms', $this->content(), $matches ); 152 array_pop( $matches ); 153 154 $meta = spyc_load( $matches[2] ); 12 /** 13 * Complete blob content. 14 * 15 * @var string 16 */ 17 protected $content; 18 19 /** 20 * Blob sha. 21 * 22 * @var string 23 */ 24 protected $sha; 25 26 /** 27 * Blob path. 28 * 29 * @var string 30 */ 31 protected $path; 32 33 /** 34 * Post id. 35 * 36 * @var int 37 */ 38 protected $id; 39 40 /** 41 * Whether the blob has frontmatter. 42 * 43 * @var boolean 44 */ 45 protected $frontmatter = false; 46 47 /** 48 * The front matter of github post 49 * @var string 50 */ 51 protected $front_matter = ''; 52 53 /** 54 * Content without front matter 55 * @var string 56 */ 57 protected $post_content; 58 59 /** 60 * Instantiates a new Blob object. 61 * 62 * @param stdClass $data Raw blob data. 63 */ 64 public function __construct( stdClass $data ) { 65 $this->interpret_data( $data ); 66 } 67 68 public function id() { 69 return $this->id; 70 } 71 72 public function set_id($id) { 73 $this->id = $id; 74 } 75 76 /** 77 * Returns the raw blob content. 78 * 79 * @return string 80 */ 81 public function content() { 82 return $this->content; 83 } 84 85 /** 86 * Set's the blob's content. 87 * 88 * @param string $content Raw blob content. 89 * @param bool $base64 Whether the content is base64 encoded. 90 * 91 * @return $this 92 */ 93 public function set_content( $content, $base64 = false ) { 94 if ( $base64 ) { 95 $content = base64_decode( $content ); 96 } 97 98 // remove whitespace from the beginning of content, 99 // To prevent blank lines before yml 100 $this->content = ltrim( $content ); 101 102 $this->frontmatter = '---' === substr( $this->content, 0, 3 ); 103 104 return $this; 105 } 106 /** 107 * Returns the blob sha. 108 * 109 * @return string 110 */ 111 public function sha() { 112 return $this->sha; 113 } 114 115 /** 116 * Return's the blob path. 117 * 118 * @return string 119 */ 120 public function path() { 121 return $this->path; 122 } 123 124 /** 125 * Whether the blob has frontmatter. 126 * 127 * @return bool 128 */ 129 public function has_frontmatter() { 130 return $this->frontmatter; 131 } 132 133 /** 134 * The front matter of github post 135 * @return string 136 */ 137 public function front_matter() { 138 return $this->front_matter; 139 } 140 141 /** 142 * Content without front matter 143 * @return string 144 */ 145 public function post_content() { 146 if ( ! $this->post_content ) { 147 $this->content_import(); 148 } 149 return $this->post_content; 150 } 151 152 /** 153 * Returns the formatted/filtered blob content used for import. 154 * 155 * @return string 156 */ 157 public function content_import() { 158 $this->post_content = $content = $this->content(); 159 160 if ( $this->has_frontmatter() ) { 161 // Break out content. 162 preg_match( '/(^---(.*?)---$(\r\n|\n|\r)?)?(.*)/ms', $content, $matches ); 163 $this->front_matter = $matches[1]; 164 $this->post_content = $content = array_pop( $matches ); 165 } 166 167 if ( function_exists( 'wpmarkdown_markdown_to_html' ) ) { 168 $content = wpmarkdown_markdown_to_html( $content ); 169 } 170 171 /** 172 * Filters the content for import. 173 */ 174 return apply_filters( 'wogh_content_import', trim( $content ) ); 175 } 176 177 /** 178 * Returns the blob meta. 179 * 180 * @return array 181 */ 182 public function meta() { 183 $meta = array(); 184 185 if ( $this->has_frontmatter() ) { 186 // Break out meta, if present. 187 preg_match( '/(^---(.*?)---$)?(.*)/ms', $this->content(), $matches ); 188 array_pop( $matches ); 189 190 $meta = spyc_load( $matches[2] ); 155 191 if ( 'yes' == get_option('wogh_ignore_author') ) { 156 192 unset($meta['author']); 157 193 } 158 159 //$meta['link'] = str_replace( home_url(), '', $meta['link'] );160 161 162 163 164 165 166 167 168 169 170 171 172 //$data = new stdClass;173 174 //$data->mode = '100644';175 //$data->type = 'blob';176 177 //$data->path = $this->path();178 179 //if ( $this->sha() ) {180 //$data->sha = $this->sha();181 //} else {182 //$data->content = $this->content();183 //}184 185 //return $data;186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 isset( $data->content ) ? trim( $data->content ): '',216 217 218 194 // if ( isset( $meta['link'] ) ) { 195 // $meta['link'] = str_replace( home_url(), '', $meta['link'] ); 196 // } 197 } 198 199 return $meta; 200 } 201 202 /** 203 * Formats the blob into an API call body. 204 * 205 * @return stdClass 206 */ 207 // public function to_body() { 208 // $data = new stdClass; 209 210 // $data->mode = '100644'; 211 // $data->type = 'blob'; 212 213 // $data->path = $this->path(); 214 215 // if ( $this->sha() ) { 216 // $data->sha = $this->sha(); 217 // } else { 218 // $data->content = $this->content(); 219 // } 220 221 // return $data; 222 // } 223 224 225 /** 226 * Formats the blob into an API call body. 227 * 228 * @return stdClass 229 */ 230 public function to_body() { 231 $data = new stdClass; 232 233 // $data->mode = '100644'; 234 // $data->type = 'blob'; 235 236 $data->path = $this->path(); 237 $data->content = base64_encode( $this->content() ); 238 $data->sha = $this->sha; 239 240 return $data; 241 } 242 243 /** 244 * Interprets the blob's data into properties. 245 */ 246 protected function interpret_data( $data ) { 247 $this->sha = isset( $data->sha ) ? $data->sha : ''; 248 $this->path = isset( $data->path ) ? $data->path : ''; 249 250 $this->set_content( 251 isset( $data->content ) ? $data->content : '', 252 isset( $data->encoding ) && 'base64' === $data->encoding ? true : false 253 ); 254 } 219 255 } -
writing-on-github/trunk/lib/cli.php
r1709881 r1713007 10 10 class Writing_On_GitHub_CLI extends WP_CLI_Command { 11 11 12 13 14 15 16 17 12 /** 13 * Application container. 14 * 15 * @var Writing_On_GitHub 16 */ 17 protected $app; 18 18 19 20 21 22 23 24 19 /** 20 * Grab the Application container on instantiation. 21 */ 22 public function __construct() { 23 $this->app = Writing_On_GitHub::$instance; 24 } 25 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 26 /** 27 * Exports an individual post 28 * all your posts to GitHub 29 * 30 * ## OPTIONS 31 * 32 * <post_id|all> 33 * : The post ID to export or 'all' for full site 34 * 35 * <user_id> 36 * : The user ID you'd like to save the commit as 37 * 38 * ## EXAMPLES 39 * 40 * wp wogh export all 1 41 * wp wogh export 1 1 42 * 43 * @synopsis <post_id|all> <user_id> 44 * 45 * @param array $args Command arguments. 46 */ 47 public function export( $args ) { 48 list( $post_id, $user_id ) = $args; 49 49 50 51 52 50 if ( ! is_numeric( $user_id ) ) { 51 WP_CLI::error( __( 'Invalid user ID', 'writing-on-github' ) ); 52 } 53 53 54 54 $this->app->export()->set_user( $user_id ); 55 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 56 if ( 'all' === $post_id ) { 57 WP_CLI::line( __( 'Starting full export to GitHub.', 'writing-on-github' ) ); 58 $this->app->controller()->export_all(); 59 } elseif ( is_numeric( $post_id ) ) { 60 WP_CLI::line( 61 sprintf( 62 __( 'Exporting post ID to GitHub: %d', 'writing-on-github' ), 63 $post_id 64 ) 65 ); 66 $this->app->controller()->export_post( (int) $post_id ); 67 } else { 68 WP_CLI::error( __( 'Invalid post ID', 'writing-on-github' ) ); 69 } 70 } 71 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 72 /** 73 * Imports the post in your GitHub repo 74 * into your WordPress blog 75 * 76 * ## OPTIONS 77 * 78 * <user_id> 79 * : The user ID you'd like to save the commit as 80 * 81 * ## EXAMPLES 82 * 83 * wp wogh import 1 84 * 85 * @synopsis <user_id> 86 * 87 * @param array $args Command arguments. 88 */ 89 public function import( $args ) { 90 list( $user_id ) = $args; 91 91 92 93 94 92 if ( ! is_numeric( $user_id ) ) { 93 WP_CLI::error( __( 'Invalid user ID', 'writing-on-github' ) ); 94 } 95 95 96 96 update_option( '_wogh_export_user_id', (int) $user_id ); 97 97 98 98 WP_CLI::line( __( 'Starting import from GitHub.', 'writing-on-github' ) ); 99 99 100 101 100 $this->app->controller()->import_master(); 101 } 102 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 103 /** 104 * Fetches the provided sha or the repository's 105 * master branch and caches it. 106 * 107 * ## OPTIONS 108 * 109 * <user_id> 110 * : The user ID you'd like to save the commit as 111 * 112 * ## EXAMPLES 113 * 114 * wp wogh prime --branch=master 115 * wp wogh prime --sha=<commit_sha> 116 * 117 * @synopsis [--sha=<commit_sha>] [--branch] 118 * 119 * @param array $args Command arguments. 120 * @param array $assoc_args Command associated arguments. 121 */ 122 public function prime( $args, $assoc_args ) { 123 if ( isset( $assoc_args['branch'] ) ) { 124 WP_CLI::line( __( 'Starting branch import.', 'writing-on-github' ) ); 125 125 126 126 $commit = $this->app->api()->fetch()->master(); 127 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 128 if ( is_wp_error( $commit ) ) { 129 WP_CLI::error( 130 sprintf( 131 __( 'Failed to import and cache branch with error: %s', 'writing-on-github' ), 132 $commit->get_error_message() 133 ) 134 ); 135 } else { 136 WP_CLI::success( 137 sprintf( 138 __( 'Successfully imported and cached commit %s from branch.', 'writing-on-github' ), 139 $commit->sha() 140 ) 141 ); 142 } 143 } else if ( isset( $assoc_args['sha'] ) ) { 144 WP_CLI::line( 'Starting sha import.' ); 145 145 146 146 $commit = $this->app->api()->fetch()->commit( $assoc_args['sha'] ); 147 147 148 149 150 151 152 153 154 155 156 157 148 WP_CLI::success( 149 sprintf( 150 __( 'Successfully imported and cached commit %s.', 'writing-on-github' ), 151 $commit->sha() 152 ) 153 ); 154 } else { 155 WP_CLI::error( 'Invalid fetch.' ); 156 } 157 } 158 158 } -
writing-on-github/trunk/lib/client/base.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Base_Client { 11 11 12 const HOST_OPTION_KEY = 'wogh_host'; 13 const TOKEN_OPTION_KEY = 'wogh_oauth_token'; 14 const REPO_OPTION_KEY = 'wogh_repository'; 15 const BRANCH_OPTION_KEY = 'wogh_branch'; 16 17 /** 18 * Application container. 19 * 20 * @var Writing_On_GitHub 21 */ 22 protected $app; 23 24 /** 25 * Instantiates a new Api object. 26 * 27 * @param Writing_On_GitHub $app Application container. 28 */ 29 public function __construct( Writing_On_GitHub $app ) { 30 $this->app = $app; 31 } 32 33 /** 34 * Generic GitHub API interface and response handler 35 * 36 * @param string $method HTTP method. 37 * @param string $endpoint API endpoint. 38 * @param array $body Request body. 39 * 40 * @return stdClass|WP_Error 41 */ 42 protected function call( $method, $endpoint, $body = array() ) { 43 if ( is_wp_error( $error = $this->can_call() ) ) { 44 return $error; 45 } 46 47 $args = array( 48 'method' => $method, 49 'headers' => array( 50 'Authorization' => 'token ' . $this->oauth_token(), 51 ), 52 ); 53 54 if ( 'GET' !== $method ) { 55 $args['body'] = function_exists( 'wp_json_encode' ) ? 56 wp_json_encode( $body ) : 57 json_encode( $body ); 58 } 59 60 $tmpbody = isset($args['body']) ? $args['body'] : ''; 61 error_log("writing-on-github-call $method $endpoint $tmpbody"); 62 63 $response = wp_remote_request( $endpoint, $args ); 64 $status = wp_remote_retrieve_header( $response, 'status' ); 65 $body = json_decode( wp_remote_retrieve_body( $response ) ); 66 67 if ( '2' !== substr( $status, 0, 1 ) && '3' !== substr( $status, 0, 1 ) ) { 68 return new WP_Error( 69 strtolower( str_replace( ' ', '_', $status ) ), 70 sprintf( 71 __( 'Method %s to endpoint %s failed with error: %s', 'writing-on-github' ), 72 $method, 73 $endpoint, 74 $body && $body->message ? $body->message : 'Unknown error' 75 ) 76 ); 77 } 78 79 return $body; 80 } 81 82 /** 83 * Validates whether the Api object can make a call. 84 * 85 * @return true|WP_Error 86 */ 87 protected function can_call() { 88 if ( ! $this->oauth_token() ) { 89 return new WP_Error( 90 'missing_token', 91 __( 'Writing On GitHub needs an auth token. Please update your settings.', 'writing-on-github' ) 92 ); 93 } 94 95 $repo = $this->repository(); 96 97 if ( ! $repo ) { 98 return new WP_Error( 99 'missing_repository', 100 __( 'Writing On GitHub needs a repository. Please update your settings.', 'writing-on-github' ) 101 ); 102 } 103 104 $parts = explode( '/', $repo ); 105 106 if ( 2 !== count( $parts ) ) { 107 return new WP_Error( 108 'malformed_repository', 109 __( 'Writing On GitHub needs a properly formed repository. Please update your settings.', 'writing-on-github' ) 110 ); 111 } 112 113 return true; 114 } 115 116 /** 117 * Returns the repository to sync with 118 * 119 * @return string 120 */ 121 public function repository() { 122 return (string) get_option( self::REPO_OPTION_KEY ); 123 } 124 125 /** 126 * Returns the user's oauth token 127 * 128 * @return string 129 */ 130 public function oauth_token() { 131 return (string) get_option( self::TOKEN_OPTION_KEY ); 132 } 133 134 /** 135 * Returns the GitHub host to sync with (for GitHub Enterprise support) 136 */ 137 public function api_base() { 138 return get_option( self::HOST_OPTION_KEY ); 139 } 140 141 public function branch() { 142 $branch = get_option( self::BRANCH_OPTION_KEY ); 143 return $branch ? $branch : 'master'; 144 } 145 146 /** 147 * API endpoint for the master branch reference 148 */ 149 public function reference_endpoint() { 150 $url = $this->api_base() . '/repos/'; 151 $url = $url . $this->repository() . '/git/refs/heads/' . $this->branch(); 152 153 return $url; 154 } 155 156 /** 157 * Api to get and create commits 158 */ 159 public function commit_endpoint() { 160 $url = $this->api_base() . '/repos/'; 161 $url = $url . $this->repository() . '/git/commits'; 162 163 return $url; 164 } 165 166 /** 167 * Api to compare commits 168 */ 169 public function compare_endpoint() { 170 $url = $this->api_base() . '/repos/'; 171 $url = $url . $this->repository() . '/compare'; 172 173 return $url; 174 } 175 176 /** 177 * Api to get and create trees 178 */ 179 public function tree_endpoint() { 180 $url = $this->api_base() . '/repos/'; 181 $url = $url . $this->repository() . '/git/trees'; 182 183 return $url; 184 } 185 186 /** 187 * Builds the proper blob API endpoint for a given post 188 * 189 * Returns String the relative API call path 190 */ 191 public function blob_endpoint() { 192 $url = $this->api_base() . '/repos/'; 193 $url = $url . $this->repository() . '/git/blobs'; 194 195 return $url; 196 } 197 198 /** 199 * Builds the proper content API endpoint for a given post 200 * 201 * Returns String the relative API call path 202 */ 203 public function content_endpoint( $path = false ) { 204 $url = $this->api_base() . '/repos/'; 205 $url = $url . $this->repository() . '/contents'; 206 207 if ( ! empty($path) ) { 208 $url .= '/' . $path; 209 } 210 211 return $url; 212 } 12 const HOST_OPTION_KEY = 'wogh_host'; 13 const TOKEN_OPTION_KEY = 'wogh_oauth_token'; 14 const REPO_OPTION_KEY = 'wogh_repository'; 15 const BRANCH_OPTION_KEY = 'wogh_branch'; 16 17 /** 18 * Application container. 19 * 20 * @var Writing_On_GitHub 21 */ 22 protected $app; 23 24 /** 25 * Instantiates a new Api object. 26 * 27 * @param Writing_On_GitHub $app Application container. 28 */ 29 public function __construct( Writing_On_GitHub $app ) { 30 $this->app = $app; 31 } 32 33 /** 34 * Generic GitHub API interface and response handler 35 * 36 * @param string $method HTTP method. 37 * @param string $endpoint API endpoint. 38 * @param array $body Request body. 39 * 40 * @return stdClass|WP_Error 41 */ 42 protected function call( $method, $endpoint, $body = array() ) { 43 if ( is_wp_error( $error = $this->can_call() ) ) { 44 /* @var WP_Error $error */ 45 return $error; 46 } 47 48 $args = array( 49 'method' => $method, 50 'headers' => array( 51 'Authorization' => 'token ' . $this->oauth_token(), 52 ), 53 ); 54 55 if ( 'GET' !== $method ) { 56 $args['body'] = json_encode( $body ); 57 } 58 59 // $tmpbody = isset( $args['body'] ) ? $args['body'] : ''; 60 // error_log( "writing-on-github-call $method $endpoint $tmpbody" ); 61 62 $response = wp_remote_request( $endpoint, $args ); 63 $status = wp_remote_retrieve_header( $response, 'status' ); 64 $body = json_decode( wp_remote_retrieve_body( $response ) ); 65 66 if ( '2' !== substr( $status, 0, 1 ) && '3' !== substr( $status, 0, 1 ) ) { 67 return new WP_Error( 68 strtolower( str_replace( ' ', '_', $status ) ), 69 sprintf( 70 __( 'Method %s to endpoint %s failed with error: %s', 'writing-on-github' ), 71 $method, 72 $endpoint, 73 ( $body && $body->message ) ? $body->message : 'Unknown error' 74 ) 75 ); 76 } 77 78 return $body; 79 } 80 81 /** 82 * Validates whether the Api object can make a call. 83 * 84 * @return true|WP_Error 85 */ 86 protected function can_call() { 87 if ( ! $this->oauth_token() ) { 88 return new WP_Error( 89 'missing_token', 90 __( 'Writing On GitHub needs an auth token. Please update your settings.', 'writing-on-github' ) 91 ); 92 } 93 94 $repo = $this->repository(); 95 96 if ( ! $repo ) { 97 return new WP_Error( 98 'missing_repository', 99 __( 'Writing On GitHub needs a repository. Please update your settings.', 'writing-on-github' ) 100 ); 101 } 102 103 $parts = explode( '/', $repo ); 104 105 if ( 2 !== count( $parts ) ) { 106 return new WP_Error( 107 'malformed_repository', 108 __( 'Writing On GitHub needs a properly formed repository. Please update your settings.', 'writing-on-github' ) 109 ); 110 } 111 112 return true; 113 } 114 115 /** 116 * Returns the repository to sync with 117 * 118 * @return string 119 */ 120 public function repository() { 121 return (string) get_option( self::REPO_OPTION_KEY ); 122 } 123 124 /** 125 * Returns the user's oauth token 126 * 127 * @return string 128 */ 129 public function oauth_token() { 130 return (string) get_option( self::TOKEN_OPTION_KEY ); 131 } 132 133 /** 134 * Returns the GitHub host to sync with (for GitHub Enterprise support) 135 */ 136 public function api_base() { 137 return get_option( self::HOST_OPTION_KEY ); 138 } 139 140 public function branch() { 141 $branch = get_option( self::BRANCH_OPTION_KEY ); 142 return $branch ? $branch : 'master'; 143 } 144 145 /** 146 * API endpoint for the master branch reference 147 */ 148 public function reference_endpoint() { 149 $url = $this->api_base() . '/repos/'; 150 $url = $url . $this->repository() . '/git/refs/heads/' . $this->branch(); 151 152 return $url; 153 } 154 155 /** 156 * Api to get and create commits 157 */ 158 public function commit_endpoint() { 159 $url = $this->api_base() . '/repos/'; 160 $url = $url . $this->repository() . '/git/commits'; 161 162 return $url; 163 } 164 165 /** 166 * Api to compare commits 167 */ 168 public function compare_endpoint() { 169 $url = $this->api_base() . '/repos/'; 170 $url = $url . $this->repository() . '/compare'; 171 172 return $url; 173 } 174 175 /** 176 * Api to get and create trees 177 */ 178 public function tree_endpoint() { 179 $url = $this->api_base() . '/repos/'; 180 $url = $url . $this->repository() . '/git/trees'; 181 182 return $url; 183 } 184 185 /** 186 * Builds the proper blob API endpoint for a given post 187 * 188 * Returns String the relative API call path 189 */ 190 public function blob_endpoint() { 191 $url = $this->api_base() . '/repos/'; 192 $url = $url . $this->repository() . '/git/blobs'; 193 194 return $url; 195 } 196 197 /** 198 * Builds the proper content API endpoint for a given post 199 * 200 * Returns String the relative API call path 201 */ 202 public function content_endpoint( $path = false ) { 203 $url = $this->api_base() . '/repos/'; 204 $url = $url . $this->repository() . '/contents'; 205 206 if ( ! empty($path) ) { 207 $url .= '/' . $path; 208 } 209 210 return $url; 211 } 213 212 } -
writing-on-github/trunk/lib/client/fetch.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Fetch_Client extends Writing_On_GitHub_Base_Client { 11 11 12 13 14 15 16 17 * @return array[Writing_On_GitHub_File_Info]|WP_Error18 19 20 21 22 23 12 /** 13 * Compare a commit by sha with master from the GitHub API 14 * 15 * @param string $sha Sha for commit to retrieve. 16 * 17 * @return Writing_On_GitHub_File_Info[]|WP_Error 18 */ 19 public function compare( $sha ) { 20 // https://api.github.com/repos/litefeel/testwpsync/compare/861f87e8851b8debb78db548269d29f8da4d94ac...master 21 $endpoint = $this->compare_endpoint(); 22 $branch = $this->branch(); 23 $data = $this->call( 'GET', "$endpoint/$sha...$branch" ); 24 24 25 26 27 25 if ( is_wp_error( $data ) ) { 26 return $data; 27 } 28 28 29 30 31 32 33 29 $files = array(); 30 foreach ($data->files as $file) { 31 $file->path = $file->filename; 32 $files[] = new Writing_On_GitHub_File_Info($file); 33 } 34 34 35 36 35 return $files; 36 } 37 37 38 39 40 41 42 43 44 45 46 47 48 49 38 /** 39 * Calls the content API to get the post's contents and metadata 40 * 41 * Returns Object the response from the API 42 * 43 * @param Writing_On_GitHub_Post $post Post to retrieve remote contents for. 44 * 45 * @return mixed 46 */ 47 public function remote_contents( $post ) { 48 return $this->call( 'GET', $this->content_endpoint( $post->github_path() ) ); 49 } 50 50 51 public function exists( $path ) {52 $result = $this->call( 'GET', $this->content_endpoint( $path ) );53 if ( is_wp_error( $result ) ) {54 return false;55 }56 return true;57 }58 51 59 /**60 * Retrieves a tree by sha recursively from the GitHub API61 *62 * @param string $sha Commit sha to retrieve tree from.63 *64 * @return Writing_On_GitHub_Tree|WP_Error65 */66 public function tree_recursive( $sha = '_default' ) {67 52 68 if ( '_default' === $sha ) { 69 $sha = $this->branch(); 70 } 53 public function exists( $path ) { 54 $result = $this->call( 'GET', $this->content_endpoint( $path ) ); 55 if ( is_wp_error( $result ) ) { 56 return false; 57 } 58 return true; 59 } 71 60 72 $data = $this->call( 'GET', $this->tree_endpoint() . '/' . $sha . '?recursive=1' ); 61 /** 62 * Retrieves a tree by sha recursively from the GitHub API 63 * 64 * @param string $sha Commit sha to retrieve tree from. 65 * 66 * @return Writing_On_GitHub_File_Info[]|WP_Error 67 */ 68 public function tree_recursive( $sha = '_default' ) { 73 69 74 if ( is_wp_error( $data )) {75 return $data;76 70 if ( '_default' === $sha ) { 71 $sha = $this->branch(); 72 } 77 73 78 $files = array();74 $data = $this->call( 'GET', $this->tree_endpoint() . '/' . $sha . '?recursive=1' ); 79 75 80 foreach ( $data->tree as $index => $thing ) { 81 // We need to remove the trees because 82 // the recursive tree includes both 83 // the subtrees as well the subtrees' blobs. 84 if ( 'blob' === $thing->type ) { 85 $thing->status = ''; 86 $files[] = new Writing_On_GitHub_File_Info( $thing ); 87 } 88 } 76 if ( is_wp_error( $data ) ) { 77 return $data; 78 } 89 79 90 return $files; 91 } 80 $files = array(); 92 81 93 /** 94 * Retrieves the blob data for a given sha 95 * 96 * @param stdClass $blob Tree blob data. 97 * 98 * @return Writing_On_GitHub_Blob|WP_Error 99 */ 100 public function blob( $blob ) { 101 // if ( $cache = $this->app->cache()->fetch_blob( $blob->sha ) ) { 102 // return $cache; 103 // } 82 foreach ( $data->tree as $index => $thing ) { 83 // We need to remove the trees because 84 // the recursive tree includes both 85 // the subtrees as well the subtrees' blobs. 86 if ( 'blob' === $thing->type ) { 87 $thing->status = ''; 88 $files[] = new Writing_On_GitHub_File_Info( $thing ); 89 } 90 } 104 91 105 $data = $this->call( 'GET', $this->blob_endpoint() . '/' . $blob->sha ); 92 return $files; 93 } 106 94 107 if ( is_wp_error( $data ) ) { 108 return $data; 109 } 95 /** 96 * Retrieves the blob data for a given sha 97 * 98 * @param Writing_On_GitHub_File_Info $fileinfo 99 * 100 * @return Writing_On_GitHub_Blob|WP_Error 101 */ 102 public function blob( Writing_On_GitHub_File_Info $fileinfo ) { 103 $data = $this->call( 'GET', $this->blob_endpoint() . '/' . $fileinfo->sha ); 110 104 111 $data->path = $blob->path; 112 $obj = new Writing_On_GitHub_Blob( $data ); 105 if ( is_wp_error( $data ) ) { 106 return $data; 107 } 113 108 114 return $obj; 115 // return $this->app->cache()->set_blob( $obj->sha(), $obj ); 116 } 109 $data->path = $fileinfo->path; 110 return new Writing_On_GitHub_Blob( $data ); 111 } 112 113 /** 114 * Get blob by path 115 * @param string $path 116 * @return Writing_On_GitHub_Blob|WP_Error 117 */ 118 public function blob_by_path( $path ) { 119 $result = $this->call( 'GET', $this->content_endpoint( $path ) ); 120 if ( is_wp_error( $result ) ) { 121 return $result; 122 } 123 124 return new Writing_On_GitHub_Blob( $result ); 125 } 117 126 } -
writing-on-github/trunk/lib/client/persist.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Persist_Client extends Writing_On_GitHub_Base_Client { 11 11 12 13 14 15 16 17 18 19 12 /** 13 * Get the data for the current user. 14 * 15 * @return array 16 */ 17 protected function export_user() { 18 $user_id = get_current_user_id(); 19 $user = get_userdata( $user_id ); 20 20 21 22 23 24 25 26 21 if ( $user ) { 22 return array( 23 'name' => $user->display_name, 24 'email' => $user->user_email, 25 ); 26 } 27 27 28 29 28 return false; 29 } 30 30 31 32 33 34 35 36 37 38 39 40 31 /** 32 * Delete the file. 33 * 34 * @return array 35 */ 36 public function delete_file( $path, $sha, $message ) { 37 $body = new stdClass(); 38 $body->message = $message; 39 $body->sha = $sha; 40 $body->branch = $this->branch(); 41 41 42 43 44 42 if ( $author = $this->export_user() ) { 43 $body->author = $author; 44 } 45 45 46 47 46 return $this->call( 'DELETE', $this->content_endpoint( $path ), $body ); 47 } 48 48 49 50 51 52 53 54 55 56 57 58 49 /** 50 * Create the file. 51 * 52 * @return array 53 */ 54 public function create_file( $blob, $message ) { 55 $body = $blob->to_body(); 56 $body->message = $message; 57 $body->branch = $this->branch(); 58 unset($body->sha); 59 59 60 61 62 60 if ( $author = $this->export_user() ) { 61 $body->author = $author; 62 } 63 63 64 65 64 return $this->call( 'PUT', $this->content_endpoint( $blob->path() ), $body ); 65 } 66 66 67 68 69 70 71 72 73 74 75 67 /** 68 * Update the file. 69 * 70 * @return array 71 */ 72 public function update_file( $blob, $message ) { 73 $body = $blob->to_body(); 74 $body->message = $message; 75 $body->branch = $this->branch(); 76 76 77 78 79 77 if ( $author = $this->export_user() ) { 78 $body->author = $author; 79 } 80 80 81 82 81 return $this->call( 'PUT', $this->content_endpoint( $blob->path() ), $body ); 82 } 83 83 } -
writing-on-github/trunk/lib/controller.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Controller { 11 11 12 /** 13 * Application container. 14 * 15 * @var Writing_On_GitHub 16 */ 17 public $app; 18 19 /** 20 * Instantiates a new Controller object 21 * 22 * @param Writing_On_GitHub $app Applicatio container. 23 */ 24 public function __construct( Writing_On_GitHub $app ) { 25 $this->app = $app; 26 } 27 28 /** 29 * Webhook callback as triggered from GitHub push. 30 * 31 * Reads the Webhook payload and syncs posts as necessary. 32 * 33 * @return boolean 34 */ 35 public function pull_posts() { 36 $this->set_ajax(); 37 if ( ! $this->app->semaphore()->is_open() ) { 38 return $this->app->response()->error( new WP_Error( 39 'semaphore_locked', 40 sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::pull_posts()' ) 41 ) ); 42 } 43 44 if ( ! $this->app->request()->is_secret_valid() ) { 45 return $this->app->response()->error( new WP_Error( 46 'invalid_headers', 47 __( 'Failed to validate secret.', 'writing-on-github' ) 48 ) ); 49 } 50 51 // ping 52 if ( $this->app->request()->is_ping() ) { 53 return $this->app->response()->success( __( 'Wordpress is ready.', 'writing-on-github' ) ); 54 } 55 56 // push 57 if ( ! $this->app->request()->is_push() ) { 58 return $this->app->response()->error( new WP_Error( 59 'invalid_headers', 60 __( 'Failed to validate webhook event.', 'writing-on-github' ) 61 ) ); 62 } 63 $payload = $this->app->request()->payload(); 64 65 if ( ! $payload->should_import() ) { 66 return $this->app->response()->error( new WP_Error( 67 'invalid_payload', 68 sprintf( 69 __( "%s won't be imported.", 'writing-on-github' ), 70 strtolower( $payload->get_commit_id() ) ? : '[Missing Commit ID]' 71 ) 72 ) ); 73 } 74 75 $this->app->semaphore()->lock(); 76 remove_action( 'save_post', array( $this, 'export_post' ) ); 77 remove_action( 'delete_post', array( $this, 'delete_post' ) ); 78 79 $result = $this->app->import()->payload( $payload ); 80 81 $this->app->semaphore()->unlock(); 82 83 if ( is_wp_error( $result ) ) { 84 return $this->app->response()->error( $result ); 85 } 86 87 return $this->app->response()->success( $result ); 88 } 89 90 /** 91 * Imports posts from the current master branch. 92 * 93 * @return boolean 94 */ 95 public function import_master( $user_id ) { 96 if ( ! $this->app->semaphore()->is_open() ) { 97 return $this->app->response()->error( new WP_Error( 98 'semaphore_locked', 99 sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::import_master()' ) 100 ) ); 101 } 102 103 $this->app->semaphore()->lock(); 104 remove_action( 'save_post', array( $this, 'export_post' ) ); 105 remove_action( 'save_post', array( $this, 'delete_post' ) ); 106 107 if ( $user_id ) { 108 wp_set_current_user( $user_id ); 109 } 110 111 $result = $this->app->import()->master(); 112 113 $this->app->semaphore()->unlock(); 114 115 if ( is_wp_error( $result ) ) { 116 update_option( '_wogh_import_error', $result->get_error_message() ); 117 118 return $this->app->response()->error( $result ); 119 } 120 121 update_option( '_wogh_import_complete', 'yes' ); 122 123 return $this->app->response()->success( $result ); 124 } 125 126 /** 127 * Export all the posts in the database to GitHub. 128 * 12 /** 13 * Application container. 14 * 15 * @var Writing_On_GitHub 16 */ 17 public $app; 18 19 /** 20 * Instantiates a new Controller object 21 * 22 * @param Writing_On_GitHub $app Applicatio container. 23 */ 24 public function __construct( Writing_On_GitHub $app ) { 25 $this->app = $app; 26 } 27 28 /** 29 * Webhook callback as triggered from GitHub push. 30 * 31 * Reads the Webhook payload and syncs posts as necessary. 32 * 33 * @return boolean 34 */ 35 public function pull_posts() { 36 $this->set_ajax(); 37 if ( ! $this->app->semaphore()->is_open() ) { 38 return $this->app->response()->error( new WP_Error( 39 'semaphore_locked', 40 sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::pull_posts()' ) 41 ) ); 42 } 43 44 if ( ! $this->app->request()->is_secret_valid() ) { 45 return $this->app->response()->error( new WP_Error( 46 'invalid_headers', 47 __( 'Failed to validate secret.', 'writing-on-github' ) 48 ) ); 49 } 50 51 // ping 52 if ( $this->app->request()->is_ping() ) { 53 return $this->app->response()->success( __( 'Wordpress is ready.', 'writing-on-github' ) ); 54 } 55 56 // push 57 if ( ! $this->app->request()->is_push() ) { 58 return $this->app->response()->error( new WP_Error( 59 'invalid_headers', 60 __( 'Failed to validate webhook event.', 'writing-on-github' ) 61 ) ); 62 } 63 $payload = $this->app->request()->payload(); 64 65 if ( ! $payload->should_import() ) { 66 return $this->app->response()->error( new WP_Error( 67 'invalid_payload', 68 sprintf( 69 __( "%s won't be imported.", 'writing-on-github' ), 70 strtolower( $payload->get_commit_id() ) ? : '[Missing Commit ID]' 71 ) 72 ) ); 73 } 74 75 $this->app->semaphore()->lock(); 76 remove_action( 'save_post', array( $this, 'export_post' ) ); 77 remove_action( 'delete_post', array( $this, 'delete_post' ) ); 78 79 $result = $this->app->import()->payload( $payload ); 80 81 $this->app->semaphore()->unlock(); 82 83 if ( is_wp_error( $result ) ) { 84 /* @var WP_Error $result */ 85 return $this->app->response()->error( $result ); 86 } 87 88 return $this->app->response()->success( $result ); 89 } 90 91 /** 92 * Imports posts from the current master branch. 93 * 94 * @return boolean 95 */ 96 public function import_master( $user_id = 0 ) { 97 if ( ! $this->app->semaphore()->is_open() ) { 98 return $this->app->response()->error( new WP_Error( 99 'semaphore_locked', 100 sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::import_master()' ) 101 ) ); 102 } 103 104 $this->app->semaphore()->lock(); 105 remove_action( 'save_post', array( $this, 'export_post' ) ); 106 remove_action( 'save_post', array( $this, 'delete_post' ) ); 107 108 if ( $user_id ) { 109 wp_set_current_user( $user_id ); 110 } 111 112 $result = $this->app->import()->master(); 113 114 $this->app->semaphore()->unlock(); 115 116 if ( is_wp_error( $result ) ) { 117 /* @var WP_Error $result */ 118 update_option( '_wogh_import_error', $result->get_error_message() ); 119 120 return $this->app->response()->error( $result ); 121 } 122 123 update_option( '_wogh_import_complete', 'yes' ); 124 125 return $this->app->response()->success( $result ); 126 } 127 128 /** 129 * Export all the posts in the database to GitHub. 130 * 129 131 * @param int $user_id 130 132 * @param boolean $force 131 133 * @return boolean 132 134 */ 133 public function export_all( $user_id, $force = false ) { 134 if ( ! $this->app->semaphore()->is_open() ) { 135 return $this->app->response()->error( new WP_Error( 136 'semaphore_locked', 137 sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::export_all()' ) 138 ) ); 139 } 140 141 $this->app->semaphore()->lock(); 142 143 if ( $user_id ) { 144 wp_set_current_user( $user_id ); 145 } 146 147 $result = $this->app->export()->full($force); 148 $this->app->semaphore()->unlock(); 149 150 // Maybe move option updating out of this class/upgrade message display? 151 if ( is_wp_error( $result ) ) { 152 update_option( '_wogh_export_error', $result->get_error_message() ); 153 154 return $this->app->response()->error( $result ); 155 } else { 156 update_option( '_wogh_export_complete', 'yes' ); 157 update_option( '_wogh_fully_exported', 'yes' ); 158 159 return $this->app->response()->success( $result ); 160 } 161 } 162 163 /** 164 * Exports a single post to GitHub by ID. 165 * 166 * Called on the save_post hook. 167 * 168 * @param int $post_id Post ID. 169 * 170 * @return boolean 171 */ 172 public function export_post( $post_id ) { 173 if ( wp_is_post_revision( $post_id ) ) { 174 return; 175 } 176 177 if ( ! $this->app->semaphore()->is_open() ) { 178 return $this->app->response()->error( new WP_Error( 179 'semaphore_locked', 180 sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::export_post()' ) 181 ) ); 182 } 183 184 $this->app->semaphore()->lock(); 185 $result = $this->app->export()->update( $post_id ); 186 $this->app->semaphore()->unlock(); 187 188 if ( is_wp_error( $result ) ) { 189 return $this->app->response()->error( $result ); 190 } 191 192 return $this->app->response()->success( $result ); 193 } 194 195 /** 196 * Removes the post from the tree. 197 * 198 * Called the delete_post hook. 199 * 200 * @param int $post_id Post ID. 201 * 202 * @return boolean 203 */ 204 public function delete_post( $post_id ) { 205 if ( wp_is_post_revision( $post_id ) ) { 206 return; 207 } 208 209 if ( ! $this->app->semaphore()->is_open() ) { 210 return $this->app->response()->error( new WP_Error( 211 'semaphore_locked', 212 sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::delete_post()' ) 213 ) ); 214 } 215 216 $this->app->semaphore()->lock(); 217 $result = $this->app->export()->delete( $post_id ); 218 $this->app->semaphore()->unlock(); 219 220 if ( is_wp_error( $result ) ) { 221 return $this->app->response()->error( $result ); 222 } 223 224 return $this->app->response()->success( $result ); 225 } 226 227 /** 228 * Indicates we're running our own AJAX hook 229 * and thus should respond with JSON, rather 230 * than just returning data. 231 */ 232 protected function set_ajax() { 233 if ( ! defined( 'WOGH_AJAX' ) ) { 234 define( 'WOGH_AJAX', true ); 235 } 236 } 135 public function export_all( $user_id = 0, $force = false ) { 136 if ( ! $this->app->semaphore()->is_open() ) { 137 return $this->app->response()->error( new WP_Error( 138 'semaphore_locked', 139 sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::export_all()' ) 140 ) ); 141 } 142 143 $this->app->semaphore()->lock(); 144 145 if ( $user_id ) { 146 wp_set_current_user( $user_id ); 147 } 148 149 $result = $this->app->export()->full($force); 150 $this->app->semaphore()->unlock(); 151 152 // Maybe move option updating out of this class/upgrade message display? 153 if ( is_wp_error( $result ) ) { 154 /* @var WP_Error $result */ 155 update_option( '_wogh_export_error', $result->get_error_message() ); 156 157 return $this->app->response()->error( $result ); 158 } else { 159 update_option( '_wogh_export_complete', 'yes' ); 160 update_option( '_wogh_fully_exported', 'yes' ); 161 162 return $this->app->response()->success( $result ); 163 } 164 } 165 166 /** 167 * Exports a single post to GitHub by ID. 168 * 169 * Called on the save_post hook. 170 * 171 * @param int $post_id Post ID. 172 * 173 * @return boolean 174 */ 175 public function export_post( $post_id ) { 176 if ( wp_is_post_revision( $post_id ) ) { 177 return; 178 } 179 180 if ( ! $this->app->semaphore()->is_open() ) { 181 return $this->app->response()->error( new WP_Error( 182 'semaphore_locked', 183 sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::export_post()' ) 184 ) ); 185 } 186 187 $this->app->semaphore()->lock(); 188 $result = $this->app->export()->update( $post_id ); 189 $this->app->semaphore()->unlock(); 190 191 if ( is_wp_error( $result ) ) { 192 /* @var WP_Error $result */ 193 return $this->app->response()->error( $result ); 194 } 195 196 return $this->app->response()->success( $result ); 197 } 198 199 /** 200 * Removes the post from the tree. 201 * 202 * Called the delete_post hook. 203 * 204 * @param int $post_id Post ID. 205 * 206 * @return boolean 207 */ 208 public function delete_post( $post_id ) { 209 if ( wp_is_post_revision( $post_id ) ) { 210 return; 211 } 212 213 if ( ! $this->app->semaphore()->is_open() ) { 214 return $this->app->response()->error( new WP_Error( 215 'semaphore_locked', 216 sprintf( __( '%s : Semaphore is locked, import/export already in progress.', 'writing-on-github' ), 'Controller::delete_post()' ) 217 ) ); 218 } 219 220 $this->app->semaphore()->lock(); 221 $result = $this->app->export()->delete( $post_id ); 222 $this->app->semaphore()->unlock(); 223 224 if ( is_wp_error( $result ) ) { 225 /* @var WP_Error $result */ 226 return $this->app->response()->error( $result ); 227 } 228 229 return $this->app->response()->success( $result ); 230 } 231 232 /** 233 * Indicates we're running our own AJAX hook 234 * and thus should respond with JSON, rather 235 * than just returning data. 236 */ 237 protected function set_ajax() { 238 if ( ! defined( 'WOGH_AJAX' ) ) { 239 define( 'WOGH_AJAX', true ); 240 } 241 } 237 242 } -
writing-on-github/trunk/lib/database.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Database { 11 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 12 /** 13 * Application container. 14 * 15 * @var Writing_On_GitHub 16 */ 17 protected $app; 18 19 /** 20 * Currently whitelisted post types. 21 * 22 * @var array 23 */ 24 protected $whitelisted_post_types = array( 'post', 'page' ); 25 26 /** 27 * Currently whitelisted post statuses. 28 * 29 * @var array 30 */ 31 protected $whitelisted_post_statuses = array( 'publish' ); 32 33 /** 34 * Instantiates a new Database object. 35 * 36 * @param Writing_On_GitHub $app Application container. 37 */ 38 public function __construct( Writing_On_GitHub $app ) { 39 $this->app = $app; 40 } 41 42 /** 43 * Queries the database for all of the supported posts. 44 * 45 45 * @param bool $force 46 * 46 47 * @return Writing_On_GitHub_Post[]|WP_Error 47 48 */ 48 public function fetch_all_supported( $force = false ) { 49 $args = array( 50 'post_type' => $this->get_whitelisted_post_types(), 51 'post_status' => $this->get_whitelisted_post_statuses(), 52 'nopaging' => true, 53 'fields' => 'ids', 54 ); 55 56 $query = new WP_Query( apply_filters( 'wogh_pre_fetch_all_supported', $args ) ); 57 58 $post_ids = $query->get_posts(); 59 60 if ( ! $post_ids ) { 61 return new WP_Error( 62 'no_results', 63 __( 'Querying for supported posts returned no results.', 'writing-on-github' ) 64 ); 65 } 66 67 $results = array(); 68 foreach ( $post_ids as $post_id ) { 69 // Do not export posts that have already been exported 70 if ( $force || ! get_post_meta( $post_id, '_wogh_sha', true ) || 71 ! get_post_meta( $post_id, '_wogh_github_path', true) ) { 72 73 $results[] = new Writing_On_GitHub_Post( $post_id, $this->app->api() ); 74 } 75 } 76 77 return $results; 78 } 79 80 /** 81 * Queries a post and returns it if it's supported. 82 * 83 * @param int $post_id Post ID to fetch. 84 * 85 * @return WP_Error|Writing_On_GitHub_Post 86 */ 87 public function fetch_by_id( $post_id ) { 88 $post = new Writing_On_GitHub_Post( $post_id, $this->app->api() ); 89 90 if ( ! $this->is_post_supported( $post ) ) { 91 return new WP_Error( 92 'unsupported_post', 93 sprintf( 94 __( 95 'Post ID %s is not supported by WOGH. See wiki to find out how to add support.', 96 'writing-on-github' 97 ), 98 $post_id 99 ) 100 ); 101 } 102 103 return $post; 104 } 105 106 /** 107 * Saves an array of Post objects to the database 108 * and associates their author as well as their latest 109 * 110 * @param Writing_On_GitHub_Post[] $posts Array of Posts to save. 111 * @param string $email Author email. 112 * 113 * @return string|WP_Error 114 */ 115 public function save_posts( array $posts ) { 116 117 /** 118 * Whether an error has occurred. 119 * 120 * @var WP_Error|false $error 121 */ 122 $error = false; 123 124 foreach ( $posts as $post ) { 125 $args = apply_filters( 'wogh_pre_import_args', $this->post_args( $post ), $post ); 126 127 remove_filter('content_save_pre', 'wp_filter_post_kses'); 128 $post_id = $post->is_new() ? 129 wp_insert_post( $args, true ) : 130 wp_update_post( $args, true ); 131 add_filter('content_save_pre', 'wp_filter_post_kses'); 132 133 if ( is_wp_error( $post_id ) ) { 134 if ( ! $error ) { 135 $error = $post_id; 136 } else { 137 $error->add( $post_id->get_error_code(), $post_id->get_error_message() ); 138 } 139 140 // Abort saving if updating the post fails. 141 continue; 142 } 143 144 // $this->set_revision_author( $post_id, $user_id ); 145 146 if ( $post->is_new() ) { 147 $author = false; 148 $meta = $post->get_meta(); 149 if ( ! empty( $meta ) && ! empty( $meta['author'] ) ) { 150 $author = $meta['author']; 151 } 152 $user = $this->fetch_commit_user( $author ); 153 $user_id = ! is_wp_error( $user ) ? $user->ID : 0; 154 $this->set_post_author( $post_id, $user_id ); 155 } 156 157 $post->set_post( get_post( $post_id ) ); 158 159 $meta = apply_filters( 'wogh_pre_import_meta', $post->get_meta(), $post ); 160 161 unset( $meta['tags'] ); 162 unset( $meta['categories'] ); 163 unset( $meta['author'] ); 164 unset( $meta['post_date'] ); 165 unset( $meta['post_excerpt'] ); 166 unset( $meta['permalink'] ); 167 unset( $meta['link'] ); 168 169 foreach ( $meta as $key => $value ) { 170 update_post_meta( $post_id, $key, $value ); 171 } 172 } 173 174 if ( $error ) { 175 return $error; 176 } 177 178 return __( 'Successfully saved posts.', 'writing-on-github' ); 179 } 180 181 protected function post_args( $post ) { 182 $args = $post->get_args(); 183 $meta = $post->get_meta(); 184 185 // prevent backslash loss 186 $args['post_content'] = addslashes($args['post_content']); 187 188 // update tags 189 if ( isset( $meta['tags'] ) && $meta['tags'] ) { 190 $args['tags_input'] = $meta['tags']; 191 } 192 193 // update categories 194 if ( isset( $meta['categories'] ) && $meta['categories'] ) { 195 $categories = $meta['categories']; 196 if (!is_array($categories)) { 197 $categories = array($categories); 198 } 199 $terms = get_terms(array( 200 'taxonomy' => 'category', 201 'fields' => 'id=>name', 202 'hide_empty' => 0, 203 'name' => $categories 204 ) 205 ); 206 $map = array(); 207 foreach ($categories as $name) { 208 $map[$name] = 1; 209 } 210 211 $ids = array(); 212 if (!empty($terms)) { 213 foreach ($terms as $id => $name) { 214 $ids[] = $id; 215 unset($map[$name]); 216 } 217 } 218 219 // create new terms 220 if (!empty($map)) { 221 foreach ($map as $name => $value) { 222 $term = wp_insert_term($name, 'category', array('parent' => 0)); 223 // array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 224 $ids[] = $term['term_id']; 225 } 226 } 227 228 $args['post_category'] = $ids; 229 } 230 231 return $args; 232 } 233 234 /** 235 * Deletes a post from the database based on its GitHub path. 236 * 237 * @param string $path Path of Post to delete. 238 * 239 * @return string|WP_Error 240 */ 241 public function delete_post_by_path( $path ) { 242 $query = new WP_Query( array( 243 'meta_key' => '_wogh_github_path', 244 'meta_value' => $path, 245 'meta_compare' => '=', 246 'posts_per_page' => 1, 247 'fields' => 'ids', 248 ) ); 249 250 $post_id = $query->get_posts(); 251 $post_id = array_pop( $post_id ); 252 253 if ( ! $post_id ) { 254 $parts = explode( '/', $path ); 255 $filename = array_pop( $parts ); 256 $directory = $parts ? array_shift( $parts ) : ''; 257 258 if ( false !== strpos( $directory, 'post' ) ) { 259 preg_match( '/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.*)\.md/', $filename, $matches ); 260 $title = $matches[4]; 261 262 $query = new WP_Query( array( 263 'name' => $title, 264 'posts_per_page' => 1, 265 'post_type' => $this->get_whitelisted_post_types(), 266 'fields' => 'ids', 267 ) ); 268 269 $post_id = $query->get_posts(); 270 $post_id = array_pop( $post_id ); 271 } 272 273 if ( ! $post_id ) { 274 preg_match( '/(.*)\.md/', $filename, $matches ); 275 $title = $matches[1]; 276 277 $query = new WP_Query( array( 278 'name' => $title, 279 'posts_per_page' => 1, 280 'post_type' => $this->get_whitelisted_post_types(), 281 'fields' => 'ids', 282 ) ); 283 284 $post_id = $query->get_posts(); 285 $post_id = array_pop( $post_id ); 286 } 287 } 288 289 if ( ! $post_id ) { 290 return new WP_Error( 291 'path_not_found', 292 sprintf( 293 __( 'Post not found for path %s.', 'writing-on-github' ), 294 $path 295 ) 296 ); 297 } 298 299 $result = wp_delete_post( $post_id ); 300 301 // If deleting fails... 302 if ( false === $result ) { 303 $post = get_post( $post_id ); 304 305 // ...and the post both exists and isn't in the trash... 306 if ( $post && 'trash' !== $post->post_status ) { 307 // ... then something went wrong. 308 return new WP_Error( 309 'db_error', 310 sprintf( 311 __( 'Failed to delete post ID %d.', 'writing-on-github' ), 312 $post_id 313 ) 314 ); 315 } 316 } 317 318 return sprintf( 319 __( 'Successfully deleted post ID %d.', 'writing-on-github' ), 320 $post_id 321 ); 322 } 323 324 public function delete_post( $post_id ) { 325 $result = wp_delete_post( $post_id ); 326 327 // If deleting fails... 328 if ( false === $result ) { 329 $post = get_post( $post_id ); 330 331 // ...and the post both exists and isn't in the trash... 332 if ( $post && 'trash' !== $post->post_status ) { 333 // ... then something went wrong. 334 return new WP_Error( 335 'db_error', 336 sprintf( 337 __( 'Failed to delete post ID %d.', 'writing-on-github' ), 338 $post_id 339 ) 340 ); 341 } 342 } 343 344 return sprintf( 345 __( 'Successfully deleted post ID %d.', 'writing-on-github' ), 346 $post_id 347 ); 348 } 349 350 /** 351 * Returns the list of post type permitted. 352 * 353 * @return array 354 */ 355 protected function get_whitelisted_post_types() { 356 return apply_filters( 'wogh_whitelisted_post_types', $this->whitelisted_post_types ); 357 } 358 359 /** 360 * Returns the list of post status permitted. 361 * 362 * @return array 363 */ 364 protected function get_whitelisted_post_statuses() { 365 return apply_filters( 'wogh_whitelisted_post_statuses', $this->whitelisted_post_statuses ); 366 } 367 368 /** 369 * Formats a whitelist array for a query. 370 * 371 * @param array $whitelist Whitelisted posts to format into query. 372 * 373 * @return string Whitelist formatted for query 374 */ 375 protected function format_for_query( $whitelist ) { 376 foreach ( $whitelist as $key => $value ) { 377 $whitelist[ $key ] = "'$value'"; 378 } 379 380 return implode( ', ', $whitelist ); 381 } 382 383 /** 384 * Verifies that both the post's status & type 385 * are currently whitelisted 386 * 387 * @param Writing_On_GitHub_Post $post Post to verify. 388 * 389 * @return boolean True if supported, false if not. 390 */ 391 protected function is_post_supported( Writing_On_GitHub_Post $post ) { 392 if ( wp_is_post_revision( $post->id ) ) { 393 return false; 394 } 395 396 // We need to allow trashed posts to be queried, but they are not whitelisted for export. 397 if ( ! in_array( $post->status(), $this->get_whitelisted_post_statuses() ) && 'trash' !== $post->status() ) { 398 return false; 399 } 400 401 if ( ! in_array( $post->type(), $this->get_whitelisted_post_types() ) ) { 402 return false; 403 } 404 405 if ( $post->has_password() ) { 406 return false; 407 } 408 409 return apply_filters( 'wogh_is_post_supported', true, $post ); 410 } 411 412 /** 413 * Retrieves the commit user for a provided display name 414 * 415 * Searches for a user with provided display name or returns 416 * the default user saved in the database. 417 * 418 * @param string $display_name User display name to search for. 419 * 420 * @return WP_Error|WP_User 421 */ 422 protected function fetch_commit_user( $display_name ) { 423 // If we can't find a user and a default hasn't been set, 424 // we're just going to set the revision author to 0. 425 $user = false; 426 427 if ( ! empty( $display_name ) ) { 428 $search_string = esc_attr( $display_name ); 429 $query = new WP_User_Query( array( 430 'search' => "{$search_string}", 431 'search_columns' => array( 432 'display_name', 433 'user_nicename', 434 'user_login', 435 ) 436 ) ); 437 $users = $query->get_results(); 438 $user = empty($users) ? false : $users[0]; 439 } 440 441 if ( ! $user ) { 442 // Use the default user. 443 $user = get_user_by( 'id', (int) get_option( 'wogh_default_user' ) ); 444 } 445 446 if ( ! $user ) { 447 return new WP_Error( 448 'user_not_found', 449 sprintf( 450 __( 'Commit user not found for email %s', 'writing-on-github' ), 451 $email 452 ) 453 ); 454 } 455 456 return $user; 457 } 458 459 /** 460 * Sets the author latest revision 461 * of the provided post ID to the provided user. 462 * 463 * @param int $post_id Post ID to update revision author. 464 * @param int $user_id User ID for revision author. 465 * 466 * @return string|WP_Error 467 */ 468 protected function set_revision_author( $post_id, $user_id ) { 469 $revision = wp_get_post_revisions( $post_id ); 470 471 if ( ! $revision ) { 472 $new_revision = wp_save_post_revision( $post_id ); 473 474 if ( ! $new_revision || is_wp_error( $new_revision ) ) { 475 return new WP_Error( 'db_error', 'There was a problem saving a new revision.' ); 476 } 477 478 // `wp_save_post_revision` returns the ID, whereas `get_post_revision` returns the whole object 479 // in order to be consistent, let's make sure we have the whole object before continuing. 480 $revision = get_post( $new_revision ); 481 482 if ( ! $revision ) { 483 return new WP_Error( 'db_error', 'There was a problem retrieving the newly recreated revision.' ); 484 } 485 } else { 486 $revision = array_shift( $revision ); 487 } 488 489 return $this->set_post_author( $revision->ID, $user_id ); 490 } 491 492 /** 493 * Updates the user ID for the provided post ID. 494 * 495 * Bypassing triggering any hooks, including creating new revisions. 496 * 497 * @param int $post_id Post ID to update. 498 * @param int $user_id User ID to update to. 499 * 500 * @return string|WP_Error 501 */ 502 protected function set_post_author( $post_id, $user_id ) { 503 global $wpdb; 504 505 $result = $wpdb->update( 506 $wpdb->posts, 507 array( 508 'post_author' => (int) $user_id, 509 ), 510 array( 511 'ID' => (int) $post_id, 512 ), 513 array( '%d' ), 514 array( '%d' ) 515 ); 516 517 if ( false === $result ) { 518 return new WP_Error( 'db_error', $wpdb->last_error ); 519 } 520 521 if ( 0 === $result ) { 522 return sprintf( 523 __( 'No change for post ID %d.', 'writing-on-github' ), 524 $post_id 525 ); 526 } 527 528 clean_post_cache( $post_id ); 529 530 return sprintf( 531 __( 'Successfully updated post ID %d.', 'writing-on-github' ), 532 $post_id 533 ); 534 } 535 536 /** 537 * Update the provided post's blob sha. 538 * 539 * @param Writing_On_GitHub_Post $post Post to update. 540 * @param string $sha Sha to update to. 541 * 542 * @return bool|int 543 */ 544 public function set_post_sha( $post, $sha ) { 545 return update_post_meta( $post->id, '_wogh_sha', $sha ); 546 } 49 public function fetch_all_supported( $force = false ) { 50 $args = array( 51 'post_type' => $this->get_whitelisted_post_types(), 52 'post_status' => $this->get_whitelisted_post_statuses(), 53 'nopaging' => true, 54 'fields' => 'ids', 55 ); 56 57 $query = new WP_Query( apply_filters( 'wogh_pre_fetch_all_supported', $args ) ); 58 59 $post_ids = $query->get_posts(); 60 61 if ( ! $post_ids ) { 62 return new WP_Error( 63 'no_results', 64 __( 'Querying for supported posts returned no results.', 'writing-on-github' ) 65 ); 66 } 67 68 /* @var Writing_On_GitHub_Post[] $results */ 69 $results = array(); 70 foreach ( $post_ids as $post_id ) { 71 // Do not export posts that have already been exported 72 if ( $force || ! get_post_meta( $post_id, '_wogh_sha', true ) || 73 ! get_post_meta( $post_id, '_wogh_github_path', true ) ) { 74 75 $results[] = new Writing_On_GitHub_Post( $post_id, $this->app->api() ); 76 } 77 } 78 79 return $results; 80 } 81 82 /** 83 * Queries a post and returns it if it's supported. 84 * 85 * @param int $post_id Post ID to fetch. 86 * 87 * @return WP_Error|Writing_On_GitHub_Post 88 */ 89 public function fetch_by_id( $post_id ) { 90 $post = new Writing_On_GitHub_Post( $post_id, $this->app->api() ); 91 92 if ( ! $this->is_post_supported( $post ) ) { 93 return new WP_Error( 94 'unsupported_post', 95 sprintf( 96 __( 97 'Post ID %s is not supported by WOGH. See wiki to find out how to add support.', 98 'writing-on-github' 99 ), 100 $post_id 101 ) 102 ); 103 } 104 105 return $post; 106 } 107 108 /** 109 * Save an post to database 110 * and associates their author as well as their latest 111 * 112 * @param Writing_On_GitHub_Post $post [description] 113 * @return WP_Error|true 114 */ 115 public function save_post( Writing_On_GitHub_Post $post ) { 116 $args = apply_filters( 'wogh_pre_import_args', $this->post_args( $post ), $post ); 117 118 remove_filter( 'content_save_pre', 'wp_filter_post_kses' ); 119 $post_id = $post->is_new() ? 120 wp_insert_post( $args, true ) : 121 wp_update_post( $args, true ); 122 add_filter( 'content_save_pre', 'wp_filter_post_kses' ); 123 124 if ( is_wp_error( $post_id ) ) { 125 /* @var WP_Error $post_id */ 126 return $post_id; 127 } 128 129 if ( $post->is_new() ) { 130 $author = false; 131 $meta = $post->get_meta(); 132 if ( ! empty( $meta ) && ! empty( $meta['author'] ) ) { 133 $author = $meta['author']; 134 } 135 $user = $this->fetch_commit_user( $author ); 136 $user_id = is_wp_error( $user ) ? 0 : $user->ID; 137 $this->set_post_author( $post_id, $user_id ); 138 } 139 140 $post->set_post( get_post( $post_id ) ); 141 142 $meta = apply_filters( 'wogh_pre_import_meta', $post->get_meta(), $post ); 143 144 update_post_meta( $post_id, '_wogh_sha', $meta['_wogh_sha'] ); 145 146 // unset( $meta['tags'] ); 147 // unset( $meta['categories'] ); 148 // unset( $meta['author'] ); 149 // unset( $meta['post_date'] ); 150 // unset( $meta['post_excerpt'] ); 151 // unset( $meta['permalink'] ); 152 // unset( $meta['link'] ); 153 154 // foreach ( $meta as $key => $value ) { 155 // update_post_meta( $post_id, $key, $value ); 156 // } 157 return true; 158 } 159 160 protected function post_args( $post ) { 161 $args = $post->get_args(); 162 $meta = $post->get_meta(); 163 164 // prevent backslash loss 165 $args['post_content'] = addslashes( $args['post_content'] ); 166 167 // update tags 168 if ( ! empty( $meta['tags'] ) ) { 169 $args['tags_input'] = $meta['tags']; 170 } 171 172 // update categories 173 if ( ! empty( $meta['categories'] ) ) { 174 $categories = $meta['categories']; 175 if ( ! is_array( $categories ) ) { 176 $categories = array( $categories ); 177 } 178 $terms = get_terms( array( 179 'taxonomy' => 'category', 180 'fields' => 'id=>name', 181 'hide_empty' => 0, 182 'name' => $categories 183 ) 184 ); 185 $map = array(); 186 foreach ( $categories as $name ) { 187 $map[$name] = 1; 188 } 189 190 $ids = array(); 191 if ( ! empty( $terms ) ) { 192 foreach ( $terms as $id => $name ) { 193 $ids[] = $id; 194 unset( $map[$name] ); 195 } 196 } 197 198 // create new terms 199 if ( ! empty( $map ) ) { 200 foreach ( $map as $name => $value ) { 201 $term = wp_insert_term( $name, 'category', array( 'parent' => 0 ) ); 202 // array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 203 $ids[] = $term['term_id']; 204 } 205 } 206 207 $args['post_category'] = $ids; 208 } 209 210 return $args; 211 } 212 213 private function get_post_id_by_filename( $filename, $pattern ) { 214 preg_match( $pattern , $filename, $matches ); 215 $title = $matches[4]; 216 217 $query = new WP_Query( array( 218 'name' => $title, 219 'posts_per_page' => 1, 220 'post_type' => $this->get_whitelisted_post_types(), 221 'fields' => 'ids', 222 ) ); 223 224 $post_id = $query->get_posts(); 225 $post_id = array_pop( $post_id ); 226 return $post_id; 227 } 228 229 /** 230 * Deletes a post from the database based on its GitHub path. 231 * 232 * @param string $path Path of Post to delete. 233 * 234 * @return string|WP_Error 235 */ 236 public function delete_post_by_path( $path ) { 237 $query = new WP_Query( array( 238 'meta_key' => '_wogh_github_path', 239 'meta_value' => $path, 240 'meta_compare' => '=', 241 'posts_per_page' => 1, 242 'fields' => 'ids', 243 ) ); 244 245 $post_id = $query->get_posts(); 246 $post_id = array_pop( $post_id ); 247 248 if ( ! $post_id ) { 249 $parts = explode( '/', $path ); 250 $filename = array_pop( $parts ); 251 $directory = $parts ? array_shift( $parts ) : ''; 252 253 if ( false !== strpos( $directory, 'post' ) ) { 254 $post_id = get_post_id_by_filename( $filename, '/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.*)\.md/' ); 255 } 256 257 if ( ! $post_id ) { 258 $post_id = get_post_id_by_filename( $filename, '/(.*)\.md/' ); 259 } 260 } 261 262 if ( ! $post_id ) { 263 return new WP_Error( 264 'path_not_found', 265 sprintf( 266 __( 'Post not found for path %s.', 'writing-on-github' ), 267 $path 268 ) 269 ); 270 } 271 272 $result = wp_delete_post( $post_id ); 273 274 // If deleting fails... 275 if ( false === $result ) { 276 $post = get_post( $post_id ); 277 278 // ...and the post both exists and isn't in the trash... 279 if ( $post && 'trash' !== $post->post_status ) { 280 // ... then something went wrong. 281 return new WP_Error( 282 'db_error', 283 sprintf( 284 __( 'Failed to delete post ID %d.', 'writing-on-github' ), 285 $post_id 286 ) 287 ); 288 } 289 } 290 291 return sprintf( 292 __( 'Successfully deleted post ID %d.', 'writing-on-github' ), 293 $post_id 294 ); 295 } 296 297 public function delete_post( $post_id ) { 298 $result = wp_delete_post( $post_id ); 299 300 // If deleting fails... 301 if ( false === $result ) { 302 $post = get_post( $post_id ); 303 304 // ...and the post both exists and isn't in the trash... 305 if ( $post && 'trash' !== $post->post_status ) { 306 // ... then something went wrong. 307 return new WP_Error( 308 'db_error', 309 sprintf( 310 __( 'Failed to delete post ID %d.', 'writing-on-github' ), 311 $post_id 312 ) 313 ); 314 } 315 } 316 317 return sprintf( 318 __( 'Successfully deleted post ID %d.', 'writing-on-github' ), 319 $post_id 320 ); 321 } 322 323 /** 324 * Returns the list of post type permitted. 325 * 326 * @return array 327 */ 328 protected function get_whitelisted_post_types() { 329 return apply_filters( 'wogh_whitelisted_post_types', $this->whitelisted_post_types ); 330 } 331 332 /** 333 * Returns the list of post status permitted. 334 * 335 * @return array 336 */ 337 protected function get_whitelisted_post_statuses() { 338 return apply_filters( 'wogh_whitelisted_post_statuses', $this->whitelisted_post_statuses ); 339 } 340 341 /** 342 * Formats a whitelist array for a query. 343 * 344 * @param array $whitelist Whitelisted posts to format into query. 345 * 346 * @return string Whitelist formatted for query 347 */ 348 protected function format_for_query( $whitelist ) { 349 foreach ( $whitelist as $key => $value ) { 350 $whitelist[ $key ] = "'$value'"; 351 } 352 353 return implode( ', ', $whitelist ); 354 } 355 356 /** 357 * Verifies that both the post's status & type 358 * are currently whitelisted 359 * 360 * @param Writing_On_GitHub_Post $post Post to verify. 361 * 362 * @return boolean True if supported, false if not. 363 */ 364 protected function is_post_supported( Writing_On_GitHub_Post $post ) { 365 if ( wp_is_post_revision( $post->id ) ) { 366 return false; 367 } 368 369 // We need to allow trashed posts to be queried, but they are not whitelisted for export. 370 if ( ! in_array( $post->status(), $this->get_whitelisted_post_statuses() ) && 'trash' !== $post->status() ) { 371 return false; 372 } 373 374 if ( ! in_array( $post->type(), $this->get_whitelisted_post_types() ) ) { 375 return false; 376 } 377 378 if ( $post->has_password() ) { 379 return false; 380 } 381 382 return apply_filters( 'wogh_is_post_supported', true, $post ); 383 } 384 385 /** 386 * Retrieves the commit user for a provided display name 387 * 388 * Searches for a user with provided display name or returns 389 * the default user saved in the database. 390 * 391 * @param string $display_name User display name to search for. 392 * 393 * @return WP_Error|WP_User 394 */ 395 protected function fetch_commit_user( $display_name ) { 396 // If we can't find a user and a default hasn't been set, 397 // we're just going to set the revision author to 0. 398 $user = false; 399 400 if ( ! empty( $display_name ) ) { 401 $search_string = esc_attr( $display_name ); 402 $query = new WP_User_Query( array( 403 'search' => "{$search_string}", 404 'search_columns' => array( 405 'display_name', 406 'user_nicename', 407 'user_login', 408 ) 409 ) ); 410 $users = $query->get_results(); 411 $user = empty($users) ? false : $users[0]; 412 } 413 414 if ( ! $user ) { 415 // Use the default user. 416 $user = get_user_by( 'id', (int) get_option( 'wogh_default_user' ) ); 417 } 418 419 if ( ! $user ) { 420 return new WP_Error( 421 'user_not_found', 422 sprintf( 423 __( 'Commit user not found for email %s', 'writing-on-github' ), 424 $email 425 ) 426 ); 427 } 428 429 return $user; 430 } 431 432 // /** 433 // * Sets the author latest revision 434 // * of the provided post ID to the provided user. 435 // * 436 // * @param int $post_id Post ID to update revision author. 437 // * @param int $user_id User ID for revision author. 438 // * 439 // * @return string|WP_Error 440 // */ 441 // protected function set_revision_author( $post_id, $user_id ) { 442 // $revision = wp_get_post_revisions( $post_id ); 443 444 // if ( ! $revision ) { 445 // $new_revision = wp_save_post_revision( $post_id ); 446 447 // if ( ! $new_revision || is_wp_error( $new_revision ) ) { 448 // return new WP_Error( 'db_error', 'There was a problem saving a new revision.' ); 449 // } 450 451 // // `wp_save_post_revision` returns the ID, whereas `get_post_revision` returns the whole object 452 // // in order to be consistent, let's make sure we have the whole object before continuing. 453 // $revision = get_post( $new_revision ); 454 455 // if ( ! $revision ) { 456 // return new WP_Error( 'db_error', 'There was a problem retrieving the newly recreated revision.' ); 457 // } 458 // } else { 459 // $revision = array_shift( $revision ); 460 // } 461 462 // return $this->set_post_author( $revision->ID, $user_id ); 463 // } 464 465 /** 466 * Updates the user ID for the provided post ID. 467 * 468 * Bypassing triggering any hooks, including creating new revisions. 469 * 470 * @param int $post_id Post ID to update. 471 * @param int $user_id User ID to update to. 472 * 473 * @return string|WP_Error 474 */ 475 protected function set_post_author( $post_id, $user_id ) { 476 global $wpdb; 477 478 $result = $wpdb->update( 479 $wpdb->posts, 480 array( 481 'post_author' => (int) $user_id, 482 ), 483 array( 484 'ID' => (int) $post_id, 485 ), 486 array( '%d' ), 487 array( '%d' ) 488 ); 489 490 if ( false === $result ) { 491 return new WP_Error( 'db_error', $wpdb->last_error ); 492 } 493 494 if ( 0 === $result ) { 495 return sprintf( 496 __( 'No change for post ID %d.', 'writing-on-github' ), 497 $post_id 498 ); 499 } 500 501 clean_post_cache( $post_id ); 502 503 return sprintf( 504 __( 'Successfully updated post ID %d.', 'writing-on-github' ), 505 $post_id 506 ); 507 } 508 509 // * 510 // * Update the provided post's blob sha. 511 // * 512 // * @param Writing_On_GitHub_Post $post Post to update. 513 // * @param string $sha Sha to update to. 514 // * 515 // * @return bool|int 516 517 // public function set_post_sha( $post, $sha ) { 518 // return update_post_meta( $post->id, '_wogh_sha', $sha ); 519 // } 547 520 } -
writing-on-github/trunk/lib/export.php
r1709881 r1713007 11 11 class Writing_On_GitHub_Export { 12 12 13 /** 14 * Application container. 15 * 16 * @var Writing_On_GitHub 17 */ 18 protected $app; 19 20 /** 21 * Initializes a new export manager. 22 * 23 * @param Writing_On_GitHub $app Application container. 24 */ 25 public function __construct( Writing_On_GitHub $app ) { 26 $this->app = $app; 27 } 28 29 /** 30 * Updates all of the current posts in the database on master. 31 * 32 * @param bool $force_all 13 /** 14 * Application container. 15 * 16 * @var Writing_On_GitHub 17 */ 18 protected $app; 19 20 /** 21 * Initializes a new export manager. 22 * 23 * @param Writing_On_GitHub $app Application container. 24 */ 25 public function __construct( Writing_On_GitHub $app ) { 26 $this->app = $app; 27 } 28 29 /** 30 * Updates all of the current posts in the database on master. 31 * 32 * @param bool $force 33 * 33 34 * @return string|WP_Error 34 35 */ 35 public function full( $force_all = false ) { 36 $posts = $this->app->database()->fetch_all_supported($force_all); 37 38 if ( is_wp_error( $posts ) ) { 39 return $posts; 40 } 41 42 $error = false; 36 public function full( $force = false ) { 37 $posts = $this->app->database()->fetch_all_supported( $force ); 38 39 if ( is_wp_error( $posts ) ) { 40 /* @var WP_Error $posts */ 41 return $posts; 42 } 43 44 $error = ''; 43 45 44 46 foreach ( $posts as $post ) { 45 47 $result = $this->update( $post->id() ); 46 48 if ( is_wp_error( $result ) ) { 47 if ( $error ) { 48 $error->add( $result->get_error_code(), $result->get_error_message() ); 49 } else { 50 $error = $result; 51 } 49 /* @var WP_Error $result */ 50 $error = wogh_append_error( $error, $result ); 52 51 } 53 52 } 54 53 55 54 if ( is_wp_error( $error ) ) { 55 /* @var WP_Error $error */ 56 56 return $error; 57 57 } 58 58 59 59 return __( 'Export to GitHub completed successfully.', 'writing-on-github' ); 60 } 61 62 63 /** 64 * Check if it exists in github 65 * @param int $post_id 66 * @return boolean 67 */ 68 protected function github_path( $post_id ) { 69 $github_path = get_post_meta( $post_id, '_wogh_github_path', true ); 70 71 if ( $github_path && $this->app->api()->fetch()->exists( $github_path ) ) { 72 return $github_path; 73 } 74 75 return false; 76 } 77 78 /** 79 * Updates the provided post ID in master. 80 * 81 * @param int $post_id Post ID to update. 82 * 83 * @return string|WP_Error 84 */ 85 public function update( $post_id ) { 86 $post = $this->app->database()->fetch_by_id( $post_id ); 87 88 if ( is_wp_error( $post ) ) { 89 return $post; 90 } 91 92 if ( 'trash' === $post->status() ) { 93 return $this->delete( $post_id ); 94 } 95 96 if ( $old_github_path = $this->github_path( $post->id() ) ) { 97 error_log("old_github_path: $old_github_path"); 98 $post->set_old_github_path($old_github_path); 99 } 100 101 $result = $this->new_posts( array( $post ) ); 102 103 if ( is_wp_error( $result ) ) { 104 return $result; 105 } 106 107 return __( 'Export to GitHub completed successfully.', 'writing-on-github' ); 108 } 109 110 /** 111 * Updates GitHub-created posts with latest WordPress data. 112 * 113 * @param array<Writing_On_GitHub_Post> $posts Array of Posts to create. 114 * 115 * @return string|WP_Error 116 */ 117 public function new_posts( array $posts ) { 118 $error = false; 119 120 $persist = $this->app->api()->persist(); 121 122 foreach ( $posts as $post ) { 123 $result = $this->new_post( $post, $persist ); 124 if ( is_wp_error( $result ) ) { 125 if ( $error ) { 126 $error->add( $result->get_error_code(), $result->get_error_message() ); 127 } else { 128 $error = $result; 129 } 130 } 131 } 132 133 134 135 // $result = $this->app->api()->persist()->commit( $master ); 136 137 if ( is_wp_error( $error ) ) { 138 return $error; 139 } 140 141 // return $this->update_shas( $posts ); 142 return true; 143 } 144 145 protected function new_post( $post, $persist ) { 146 $github_path = $post->github_path(); 147 $old_github_path = $post->old_github_path(); 148 $blob = $post->to_blob(); 149 $result = false; 150 151 if ( $old_github_path && $old_github_path != $github_path ) { 152 // rename 153 $message = apply_filters( 154 'wogh_commit_msg_move_post', 155 sprintf( 156 'Move %s to %s via WordPress at %s (%s)', 157 $old_github_path, $github_path, 158 site_url(), 159 get_bloginfo( 'name' ) 160 ) 161 ) . $this->get_commit_msg_tag(); 162 163 $result = $persist->delete_file( $post->old_github_path(), $blob->sha(), $message ); 164 if ( is_wp_error( $result ) ) { 165 return $result; 166 } 167 168 $result = $persist->create_file( $blob, $message ); 169 if ( is_wp_error( $result ) ) { 170 return $result; 171 } 172 } elseif ( ! $old_github_path ) { 173 // create new 174 $message = apply_filters( 175 'wogh_commit_msg_new_post', 176 sprintf( 177 'Create new post %s from WordPress at %s (%s)', 178 $github_path, 179 site_url(), 180 get_bloginfo( 'name' ) 181 ) 182 ) . $this->get_commit_msg_tag(); 183 $result = $persist->create_file( $blob, $message ); 184 if ( is_wp_error( $result ) ) { 185 return $result; 186 } 187 } elseif ( $old_github_path && $old_github_path == $github_path ) { 188 // update 189 $message = apply_filters( 190 'wogh_commit_msg_update_post', 191 sprintf( 192 'Update post %s from WordPress at %s (%s)', 193 $github_path, 194 site_url(), 195 get_bloginfo( 'name' ) 196 ) 197 ) . $this->get_commit_msg_tag(); 198 $result = $persist->update_file( $blob, $message ); 199 if ( is_wp_error( $result ) ) { 200 return $result; 201 } 202 } 203 204 $sha = $result->content->sha; 205 $post->set_sha($sha); 206 $post->set_old_github_path($github_path); 207 208 return true; 209 } 210 211 /** 212 * Deletes a provided post ID from master. 213 * 214 * @param int $post_id Post ID to delete. 215 * 216 * @return string|WP_Error 217 */ 218 public function delete( $post_id ) { 219 $post = $this->app->database()->fetch_by_id( $post_id ); 220 221 if ( is_wp_error( $post ) ) { 222 return $post; 223 } 224 225 $github_path = get_post_meta( $post_id, '_wogh_github_path', true ); 226 227 $message = apply_filters( 228 'wogh_commit_msg_delete', 229 sprintf( 230 'Deleting %s via WordPress at %s (%s)', 231 $github_path, 232 site_url(), 233 get_bloginfo( 'name' ) 234 ), 235 $post 236 ) . $this->get_commit_msg_tag(); 237 238 $result = $this->app->api()->persist()->delete_file( $github_path, $post->sha(), $message ); 239 240 if ( is_wp_error( $result ) ) { 241 return $result; 242 } 243 244 return __( 'Export to GitHub completed successfully.', 'writing-on-github' ); 245 } 246 247 248 /** 249 * Saves the export user to the database. 250 * 251 * @param int $user_id User ID to export with. 252 * 253 * @return bool 254 */ 255 public function set_user( $user_id ) { 256 return update_option( self::EXPORT_USER_OPTION, (int) $user_id ); 257 } 258 259 /** 260 * Gets the commit message tag. 261 * 262 * @return string 263 */ 264 protected function get_commit_msg_tag() { 265 $tag = apply_filters( 'wogh_commit_msg_tag', 'wogh' ); 266 267 if ( ! $tag ) { 268 throw new Exception( __( 'Commit message tag not set. Filter `wogh_commit_msg_tag` misconfigured.', 'writing-on-github' ) ); 269 } 270 271 return ' - ' . $tag; 272 } 60 } 61 62 63 /** 64 * Check if it exists in github 65 * @param int $post_id 66 * @return boolean 67 */ 68 protected function github_path( $post_id ) { 69 $github_path = get_post_meta( $post_id, '_wogh_github_path', true ); 70 71 if ( $github_path && $this->app->api()->fetch()->exists( $github_path ) ) { 72 return $github_path; 73 } 74 75 return false; 76 } 77 78 /** 79 * Updates the provided post ID in master. 80 * 81 * @param int $post_id Post ID to update. 82 * 83 * @return string|WP_Error 84 */ 85 public function update( $post_id ) { 86 $post = $this->app->database()->fetch_by_id( $post_id ); 87 88 if ( is_wp_error( $post ) ) { 89 /* @var WP_Error $post */ 90 return $post; 91 } 92 93 if ( 'trash' === $post->status() ) { 94 return $this->delete( $post_id ); 95 } 96 97 if ( $old_github_path = $this->github_path( $post->id() ) ) { 98 error_log("old_github_path: $old_github_path"); 99 $post->set_old_github_path($old_github_path); 100 } 101 102 $result = $this->export_post( $post ); 103 104 if ( is_wp_error( $result ) ) { 105 /* @var WP_Error $result */ 106 return $result; 107 } 108 109 return __( 'Export to GitHub completed successfully.', 'writing-on-github' ); 110 } 111 112 /** 113 * Post to blob 114 * @param Writing_On_GitHub_Post $post 115 * @return WP_Error|Writing_On_GitHub_Blob 116 */ 117 protected function post_to_blob( Writing_On_GitHub_Post $post ) { 118 if ( ! $post->get_blob() 119 && $post->old_github_path() 120 && wogh_is_dont_export_content() ) { 121 122 123 $blob = $this->app->api()->fetch()->blob_by_path( $post->old_github_path() ); 124 125 if ( is_wp_error( $blob ) ) { 126 /** @var WP_Error $blob */ 127 return $blob; 128 } 129 130 $post->set_blob( $blob ); 131 } 132 133 return $post->to_blob(); 134 } 135 136 /** 137 * Export post to github 138 * @param Writing_On_GitHub_Post $post 139 * @return WP_Error|true 140 */ 141 public function export_post( Writing_On_GitHub_Post $post ) { 142 // check blob 143 $blob = $this->post_to_blob( $post ); 144 if ( is_wp_error( $blob ) ) { 145 /** @var WP_Error $blob */ 146 return $blob; 147 } 148 149 $result = false; 150 151 $persist = $this->app->api()->persist(); 152 $github_path = $post->github_path(); 153 $old_github_path = $post->old_github_path(); 154 155 if ( $old_github_path && $old_github_path != $github_path ) { 156 // rename 157 $message = apply_filters( 158 'wogh_commit_msg_move_post', 159 sprintf( 160 'Move %s to %s via WordPress at %s (%s)', 161 $old_github_path, $github_path, 162 site_url(), 163 get_bloginfo( 'name' ) 164 ) 165 ) . $this->get_commit_msg_tag(); 166 167 $result = $persist->delete_file( $post->old_github_path(), $blob->sha(), $message ); 168 if ( is_wp_error( $result ) ) { 169 return $result; 170 } 171 172 $result = $persist->create_file( $blob, $message ); 173 if ( is_wp_error( $result ) ) { 174 return $result; 175 } 176 } elseif ( ! $old_github_path ) { 177 // create new 178 $message = apply_filters( 179 'wogh_commit_msg_new_post', 180 sprintf( 181 'Create new post %s from WordPress at %s (%s)', 182 $github_path, 183 site_url(), 184 get_bloginfo( 'name' ) 185 ) 186 ) . $this->get_commit_msg_tag(); 187 $result = $persist->create_file( $blob, $message ); 188 if ( is_wp_error( $result ) ) { 189 return $result; 190 } 191 } elseif ( $old_github_path && $old_github_path == $github_path ) { 192 // update 193 $sha = wogh_git_sha( $blob->content() ); 194 if ( $sha === $blob->sha() ) { 195 // don't export when has not changed 196 return true; 197 } 198 $message = apply_filters( 199 'wogh_commit_msg_update_post', 200 sprintf( 201 'Update post %s from WordPress at %s (%s)', 202 $github_path, 203 site_url(), 204 get_bloginfo( 'name' ) 205 ) 206 ) . $this->get_commit_msg_tag(); 207 $result = $persist->update_file( $blob, $message ); 208 if ( is_wp_error( $result ) ) { 209 return $result; 210 } 211 } 212 213 $sha = $result->content->sha; 214 $post->set_sha( $sha ); 215 $post->set_old_github_path( $github_path ); 216 217 return true; 218 } 219 220 /** 221 * Deletes a provided post ID from master. 222 * 223 * @param int $post_id Post ID to delete. 224 * 225 * @return string|WP_Error 226 */ 227 public function delete( $post_id ) { 228 $post = $this->app->database()->fetch_by_id( $post_id ); 229 230 if ( is_wp_error( $post ) ) { 231 /* @var WP_Error $post */ 232 return $post; 233 } 234 235 $github_path = get_post_meta( $post_id, '_wogh_github_path', true ); 236 237 $message = apply_filters( 238 'wogh_commit_msg_delete', 239 sprintf( 240 'Deleting %s via WordPress at %s (%s)', 241 $github_path, 242 site_url(), 243 get_bloginfo( 'name' ) 244 ), 245 $post 246 ) . $this->get_commit_msg_tag(); 247 248 $result = $this->app->api()->persist()->delete_file( $github_path, $post->sha(), $message ); 249 250 if ( is_wp_error( $result ) ) { 251 /* @var WP_Error $result */ 252 return $result; 253 } 254 255 return __( 'Export to GitHub completed successfully.', 'writing-on-github' ); 256 } 257 258 259 /** 260 * Saves the export user to the database. 261 * 262 * @param int $user_id User ID to export with. 263 * 264 * @return bool 265 */ 266 public function set_user( $user_id ) { 267 return update_option( self::EXPORT_USER_OPTION, (int) $user_id ); 268 } 269 270 /** 271 * Gets the commit message tag. 272 * 273 * @return string 274 */ 275 protected function get_commit_msg_tag() { 276 $tag = apply_filters( 'wogh_commit_msg_tag', 'wogh' ); 277 278 if ( ! $tag ) { 279 throw new Exception( __( 'Commit message tag not set. Filter `wogh_commit_msg_tag` misconfigured.', 'writing-on-github' ) ); 280 } 281 282 return ' - ' . $tag; 283 } 273 284 } -
writing-on-github/trunk/lib/fileinfo.php
r1709881 r1713007 7 7 class Writing_On_GitHub_File_Info { 8 8 9 10 $this->sha= $data->sha;11 $this->path= $data->path;12 $this->status= $data->status;13 9 public function __construct( stdClass $data ) { 10 $this->sha = $data->sha; 11 $this->path = $data->path; 12 $this->status = $data->status; 13 } 14 14 15 16 17 15 public $sha; 16 public $path; 17 public $status; // added removed modified 18 18 } -
writing-on-github/trunk/lib/import.php
r1709881 r1713007 11 11 class Writing_On_GitHub_Import { 12 12 13 /** 14 * Application container. 15 * 16 * @var Writing_On_GitHub 17 */ 18 protected $app; 19 20 /** 21 * Initializes a new import manager. 22 * 23 * @param Writing_On_GitHub $app Application container. 24 */ 25 public function __construct( Writing_On_GitHub $app ) { 26 $this->app = $app; 27 } 28 29 /** 30 * Imports a payload. 31 * 32 * @param Writing_On_GitHub_Payload $payload GitHub payload object. 33 * 34 * @return string|WP_Error 35 */ 36 // public function payload( Writing_On_GitHub_Payload $payload ) { 37 // /** 38 // * Whether there's an error during import. 39 // * 40 // * @var false|WP_Error $error 41 // */ 42 // $error = false; 43 44 // $result = $this->commit( $this->app->api()->fetch()->commit( $payload->get_commit_id() ) ); 45 46 // if ( is_wp_error( $result ) ) { 47 // $error = $result; 48 // } 49 50 // $removed = array(); 51 // foreach ( $payload->get_commits() as $commit ) { 52 // $removed = array_merge( $removed, $commit->removed ); 53 // } 54 // foreach ( array_unique( $removed ) as $path ) { 55 // $result = $this->app->database()->delete_post_by_path( $path ); 56 57 // if ( is_wp_error( $result ) ) { 58 // if ( $error ) { 59 // $error->add( $result->get_error_code(), $result->get_error_message() ); 60 // } else { 61 // $error = $result; 62 // } 63 // } 64 // } 65 66 // if ( $error ) { 67 // return $error; 68 // } 69 70 // return __( 'Payload processed', 'writing-on-github' ); 71 // } 72 73 public function payload( Writing_On_GitHub_Payload $payload ) { 74 75 $result = $this->app->api()->fetch()->compare( $payload->get_before_commit_id() ); 76 77 if ( is_wp_error( $result ) ) { 78 return $result; 79 } 80 81 $result = $this->import_files( $result ); 82 83 if ( is_wp_error( $result ) ) { 84 return $files; 85 } 86 87 return __( 'Payload processed', 'writing-on-github' ); 88 } 89 90 /** 91 * import blob by files 92 * @param array $files [Writing_On_GitHub_File_Info] 93 * @return string|WP_ERROR 94 */ 95 protected function import_files( $files ) { 96 97 $error = false; 98 $delete_ids = false; 99 100 $result = $this->compare( $files, $delete_ids ); 101 102 if ( is_wp_error( $result ) ) { 103 return $result; 104 } 105 106 if ( $delete_ids ) { 107 foreach ($delete_ids as $id) { 108 $result = $this->app->database()->delete_post( $id ); 109 if ( is_wp_error( $result ) ) { 110 if ( $error ) { 111 $error->add( $result->get_error_code(), $result->get_error_message() ); 112 } else { 113 $error = $result; 114 } 115 } 116 } 117 } 118 119 return $error; 120 } 121 122 /** 123 * Imports the latest commit on the master branch. 124 * 125 * @return string|WP_Error 126 */ 127 public function master() { 128 $result = $this->app->api()->fetch()->tree_recursive(); 129 130 if ( is_wp_error( $result ) ) { 131 return $result; 132 } 133 134 $result = $this->import_files( $result ); 135 136 if ( is_wp_error( $result ) ) { 137 return $result; 138 } 139 140 return __( 'Payload processed', 'writing-on-github' ); 141 } 142 143 protected function compare( $files, &$delete_ids ) { 144 if ( is_wp_error( $files ) ) { 145 return $files; 146 } 147 148 $posts = array(); 149 $new = array(); 150 151 $idsmap = array(); 152 153 foreach ( $files as $file ) { 154 if ( ! $this->importable_file( $file ) ) { 155 continue; 156 } 157 158 $blob = $this->app->api()->fetch()->blob($file); 159 // network error ? 160 if ( is_wp_error($blob) ) { 161 continue; 162 } 163 164 165 if ( $this->importable_raw_file($blob) ) { 166 $this->import_raw_file($blob, $file->status == 'removed'); 167 continue; 168 } 169 170 if ( ! $this->importable_blob($blob) ) { 171 continue; 172 } 173 174 $post = $this->blob_to_post( $blob ); 175 176 if ( $file->status == 'removed' ) { 177 if ( $blob->id() ) { 178 $idsmap[$blob->id()] = true; 179 } 180 } elseif ( $post != false ) { 181 $posts[] = $post; 182 if ( $post->is_new() ) { 183 $new[] = $post; 184 } 185 } 186 } 187 188 foreach ($posts as $post) { 189 if ( $post->id() && isset( $idsmap[$post->id()] ) ) { 190 unset( $idsmap[$post->id()] ); 191 } 192 } 193 $delete_ids = array(); 194 foreach ($idsmap as $id => $value) { 195 $delete_ids[] = $id; 196 } 197 198 // $this->app->database()->save_posts( $posts, $commit->author_email() ); 199 200 $result = $this->app->database()->save_posts( $posts ); 201 202 if ( is_wp_error( $result ) ) { 203 return $result; 204 } 205 206 if ( $new ) { 207 $result = $this->app->export()->new_posts( $new ); 208 209 if ( is_wp_error( $result ) ) { 210 return $result; 211 } 212 } 213 214 return $posts; 215 } 216 217 /** 218 * Checks whether the provided blob should be imported. 219 * 220 * @param Writing_On_GitHub_Blob $blob Blob to validate. 221 * 222 * @return bool 223 */ 224 protected function importable_file( Writing_On_GitHub_File_Info $file ) { 225 226 // only _pages and _posts 227 if ( strncasecmp($file->path, '_pages/', strlen('_pages/') ) != 0 && 228 strncasecmp($file->path, '_posts/', strlen('_posts/') ) != 0 && 229 strncasecmp($file->path, 'images/', strlen('images/') ) != 0 ) { 230 return false; 231 } 232 233 234 // if ( ! $file->has_frontmatter() ) { 235 // return false; 236 // } 237 238 return true; 239 } 240 241 /** 242 * Checks whether the provided blob should be imported. 243 * 244 * @param Writing_On_GitHub_Blob $blob Blob to validate. 245 * 246 * @return bool 247 */ 248 protected function importable_blob( Writing_On_GitHub_Blob $blob ) { 249 // global $wpdb; 250 251 // // Skip the repo's readme. 252 // if ( 'readme' === strtolower( substr( $blob->path(), 0, 6 ) ) ) { 253 // return false; 254 // } 255 256 // // If the blob sha already matches a post, then move on. 257 // if ( ! is_wp_error( $this->app->database()->fetch_by_sha( $blob->sha() ) ) ) { 258 // return false; 259 // } 260 261 if ( ! $blob->has_frontmatter() ) { 262 return false; 263 } 264 265 return true; 266 } 267 268 protected function importable_raw_file( Writing_On_GitHub_Blob $blob ) { 269 if ( $blob->has_frontmatter() ) { 270 return false; 271 } 272 273 // only images 274 if ( strncasecmp($blob->path(), 'images/', strlen('images/') ) != 0) { 275 return false; 276 } 277 278 return true; 279 } 280 281 /** 282 * Imports a raw file content into file system. 283 * @param Writing_On_GitHub_Blob $blob 284 * @param bool $is_remove 285 */ 286 protected function import_raw_file( Writing_On_GitHub_Blob $blob, $is_remove ) { 287 $arr = wp_upload_dir(); 288 $path = $arr['basedir'] . '/writing-on-github/' . $blob->path(); 289 if ( $is_remove ) { 290 if ( file_exists($path) ) { 291 unlink($path); 292 } 293 } else { 294 $dirname = dirname($path); 295 if ( ! file_exists($dirname) ) { 296 wp_mkdir_p($dirname); 297 } 298 299 file_put_contents($path, $blob->content()); 300 } 301 } 302 303 /** 304 * Imports a single blob content into matching post. 305 * 306 * @param Writing_On_GitHub_Blob $blob Blob to transform into a Post. 307 * 308 * @return Writing_On_GitHub_Post 309 */ 310 protected function blob_to_post( Writing_On_GitHub_Blob $blob ) { 311 $args = array( 'post_content' => $blob->content_import() ); 312 $meta = $blob->meta(); 313 314 $id = false; 315 316 if ( $meta ) { 317 if ( array_key_exists( 'layout', $meta ) ) { 318 $args['post_type'] = $meta['layout']; 319 unset( $meta['layout'] ); 320 } 321 322 if ( array_key_exists( 'published', $meta ) ) { 323 $args['post_status'] = true === $meta['published'] ? 'publish' : 'draft'; 324 unset( $meta['published'] ); 325 } 326 327 if ( array_key_exists( 'post_title', $meta ) ) { 328 $args['post_title'] = $meta['post_title']; 329 unset( $meta['post_title'] ); 330 } 331 332 if ( array_key_exists( 'post_name', $meta ) ) { 333 $args['post_name'] = $meta['post_name']; 334 unset( $meta['post_name'] ); 335 } 336 337 if ( array_key_exists( 'ID', $meta ) ) { 338 $id = $args['ID'] = $meta['ID']; 339 $blob->set_id($id); 340 unset( $meta['ID'] ); 341 } 342 } 343 344 $meta['_wogh_sha'] = $blob->sha(); 345 346 if ( $id ) { 347 $old_sha = get_post_meta( $id, '_wogh_sha', true ); 348 $old_github_path = get_post_meta( $id, '_wogh_github_path', true ); 349 350 // dont save post when has same sha 351 if ( $old_sha && $old_sha == $meta['_wogh_sha'] && 352 $old_github_path && $old_github_path == $blob->path() ) { 353 return false; 354 } 355 } 356 357 $post = new Writing_On_GitHub_Post( $args, $this->app->api() ); 358 $post->set_old_github_path( $blob->path() ); 359 $post->set_meta( $meta ); 360 $blob->set_id( $post->id() ); 361 362 return $post; 363 } 13 /** 14 * Application container. 15 * 16 * @var Writing_On_GitHub 17 */ 18 protected $app; 19 20 /** 21 * Initializes a new import manager. 22 * 23 * @param Writing_On_GitHub $app Application container. 24 */ 25 public function __construct( Writing_On_GitHub $app ) { 26 $this->app = $app; 27 } 28 29 /** 30 * Imports a payload. 31 * @param Writing_On_GitHub_Payload $payload 32 * 33 * @return string|WP_Error 34 */ 35 public function payload( Writing_On_GitHub_Payload $payload ) { 36 37 $result = $this->app->api()->fetch()->compare( $payload->get_before_commit_id() ); 38 39 if ( is_wp_error( $result ) ) { 40 /* @var WP_Error $result */ 41 return $result; 42 } 43 44 if ( is_array( $result ) ) { 45 $result = $this->import_files( $result ); 46 } 47 48 if ( is_wp_error( $result ) ) { 49 return $files; 50 } 51 52 return __( 'Payload processed', 'writing-on-github' ); 53 } 54 55 /** 56 * import blob by files 57 * @param Writing_On_GitHub_File_Info[] $files 58 * 59 * @return true|WP_Error 60 */ 61 protected function import_files( $files ) { 62 63 $error = true; 64 65 foreach ( $files as $file ) { 66 if ( ! $this->importable_file( $file ) ) { 67 continue; 68 } 69 70 $blob = $this->app->api()->fetch()->blob( $file ); 71 // network error ? 72 if ( ! $blob instanceof Writing_On_GitHub_Blob ) { 73 continue; 74 } 75 76 $is_remove = 'removed' == $file->status; 77 78 $result = false; 79 if ( $this->importable_raw_file( $blob ) ) { 80 $result = $this->import_raw_file( $blob, $is_remove ); 81 } elseif ( $this->importable_post( $blob ) ) { 82 if ( $is_remove ) { 83 $result = $this->delete_post( $blob ); 84 } else { 85 $result = $this->import_post( $blob ); 86 } 87 } 88 89 if ( is_wp_error( $result ) ) { 90 /* @var WP_Error $result */ 91 $error = wogh_append_error( $error, $result ); 92 } 93 } 94 95 return $error; 96 } 97 98 /** 99 * Imports the latest commit on the master branch. 100 * 101 * @return string|WP_Error 102 */ 103 public function master() { 104 $result = $this->app->api()->fetch()->tree_recursive(); 105 106 if ( is_wp_error( $result ) ) { 107 /* @var WP_Error $result */ 108 return $result; 109 } 110 111 if ( is_array( $result ) ) { 112 $result = $this->import_files( $result ); 113 } 114 115 if ( is_wp_error( $result ) ) { 116 /* @var WP_Error $result */ 117 return $result; 118 } 119 120 return __( 'Payload processed', 'writing-on-github' ); 121 } 122 123 /** 124 * Checks whether the provided blob should be imported. 125 * 126 * @param Writing_On_GitHub_File_Info $file 127 * 128 * @return bool 129 */ 130 protected function importable_file( Writing_On_GitHub_File_Info $file ) { 131 132 $path = $file->path; 133 134 // only _pages, _posts and images 135 $prefixs = array( '_pages/', '_posts/', 'images/'); 136 foreach ($prefixs as $prefix) { 137 if ( ! strncasecmp($path, $prefix, strlen( $prefix ) ) ) { 138 return true; 139 } 140 } 141 return false; 142 } 143 144 /** 145 * Checks whether the provided blob should be imported. 146 * 147 * @param Writing_On_GitHub_Blob $blob Blob to validate. 148 * 149 * @return bool 150 */ 151 protected function importable_post( Writing_On_GitHub_Blob $blob ) { 152 // global $wpdb; 153 154 // // Skip the repo's readme. 155 // if ( 'readme' === strtolower( substr( $blob->path(), 0, 6 ) ) ) { 156 // return false; 157 // } 158 159 // // If the blob sha already matches a post, then move on. 160 // if ( ! is_wp_error( $this->app->database()->fetch_by_sha( $blob->sha() ) ) ) { 161 // return false; 162 // } 163 164 if ( ! $blob->has_frontmatter() ) { 165 return false; 166 } 167 168 return true; 169 } 170 171 /** 172 * Delete post 173 * @param Writing_On_GitHub_Blob $blob 174 * @return WP_Error|bool 175 */ 176 protected function delete_post( Writing_On_GitHub_Blob $blob ) { 177 $id = $blob->id(); 178 if ( empty( $id ) ) { 179 return false; 180 } 181 $result = $this->app->database()->delete_post( $id ); 182 if ( is_wp_error( $result ) ) { 183 /* @var WP_Error $result */ 184 return $result; 185 } 186 return true; 187 } 188 189 /** 190 * Imports a post into wordpress 191 * @param Writing_On_GitHub_Blob $blob 192 * @return WP_Error|bool 193 */ 194 protected function import_post( Writing_On_GitHub_Blob $blob ) { 195 $post = $this->blob_to_post( $blob ); 196 197 if ( ! $post instanceof Writing_On_GitHub_Post ) { 198 return false; 199 } 200 201 $result = $this->app->database()->save_post( $post ); 202 if ( is_wp_error( $result ) ) { 203 /** @var WP_Error $result */ 204 return $result; 205 } 206 207 if ( $post->is_new() || 208 ! wogh_equal_front_matter( $post, $blob ) ) { 209 210 $result = $this->app->export()->export_post( $post ); 211 212 if ( is_wp_error( $result ) ) { 213 /** @var WP_Error $result */ 214 return $result; 215 } 216 } 217 218 clean_post_cache( $post->id() ); 219 220 return true; 221 } 222 223 /** 224 * import raw file 225 * @param Writing_On_GitHub_Blob $blob 226 * @return bool 227 */ 228 protected function importable_raw_file( Writing_On_GitHub_Blob $blob ) { 229 if ( $blob->has_frontmatter() ) { 230 return false; 231 } 232 233 // only images 234 if ( strncasecmp($blob->path(), 'images/', strlen('images/') ) != 0) { 235 return false; 236 } 237 238 return true; 239 } 240 241 /** 242 * Imports a raw file content into file system. 243 * @param Writing_On_GitHub_Blob $blob 244 * @param bool $is_remove 245 */ 246 protected function import_raw_file( Writing_On_GitHub_Blob $blob, $is_remove ) { 247 $arr = wp_upload_dir(); 248 $path = $arr['basedir'] . '/writing-on-github/' . $blob->path(); 249 if ( $is_remove ) { 250 if ( file_exists($path) ) { 251 unlink($path); 252 } 253 } else { 254 $dirname = dirname($path); 255 if ( ! file_exists($dirname) ) { 256 wp_mkdir_p($dirname); 257 } 258 259 file_put_contents($path, $blob->content()); 260 } 261 return true; 262 } 263 264 /** 265 * Imports a single blob content into matching post. 266 * 267 * @param Writing_On_GitHub_Blob $blob Blob to transform into a Post. 268 * 269 * @return Writing_On_GitHub_Post|false 270 */ 271 protected function blob_to_post( Writing_On_GitHub_Blob $blob ) { 272 $args = array( 'post_content' => $blob->content_import() ); 273 $meta = $blob->meta(); 274 275 $id = false; 276 277 if ( ! empty( $meta ) ) { 278 if ( array_key_exists( 'layout', $meta ) ) { 279 $args['post_type'] = $meta['layout']; 280 unset( $meta['layout'] ); 281 } 282 283 if ( array_key_exists( 'published', $meta ) ) { 284 $args['post_status'] = true === $meta['published'] ? 'publish' : 'draft'; 285 unset( $meta['published'] ); 286 } 287 288 if ( array_key_exists( 'post_title', $meta ) ) { 289 $args['post_title'] = $meta['post_title']; 290 unset( $meta['post_title'] ); 291 } 292 293 if ( array_key_exists( 'post_name', $meta ) ) { 294 $args['post_name'] = $meta['post_name']; 295 unset( $meta['post_name'] ); 296 } 297 298 if ( array_key_exists( 'ID', $meta ) ) { 299 $id = $args['ID'] = $meta['ID']; 300 $blob->set_id($id); 301 unset( $meta['ID'] ); 302 } 303 } 304 305 $meta['_wogh_sha'] = $blob->sha(); 306 307 if ( $id ) { 308 $old_sha = get_post_meta( $id, '_wogh_sha', true ); 309 $old_github_path = get_post_meta( $id, '_wogh_github_path', true ); 310 311 // dont save post when has same sha 312 if ( $old_sha && $old_sha == $meta['_wogh_sha'] && 313 $old_github_path && $old_github_path == $blob->path() ) { 314 return false; 315 } 316 } 317 318 $post = new Writing_On_GitHub_Post( $args, $this->app->api() ); 319 $post->set_old_github_path( $blob->path() ); 320 $post->set_meta( $meta ); 321 $post->set_blob( $blob ); 322 $blob->set_id( $post->id() ); 323 324 return $post; 325 } 364 326 } -
writing-on-github/trunk/lib/payload.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Payload { 11 11 12 13 14 15 16 17 12 /** 13 * Application container. 14 * 15 * @var Writing_On_GitHub 16 */ 17 protected $app; 18 18 19 20 21 22 23 24 19 /** 20 * Payload data. 21 * 22 * @var stdClass 23 */ 24 protected $data; 25 25 26 27 28 29 30 31 32 33 34 35 26 /** 27 * Writing_On_GitHub_Payload constructor. 28 * 29 * @param Writing_On_GitHub $app Application container. 30 * @param string $raw_data Raw request data. 31 */ 32 public function __construct( Writing_On_GitHub $app, $raw_data ) { 33 $this->app = $app; 34 $this->data = json_decode( $raw_data ); 35 } 36 36 37 38 39 40 41 42 43 44 45 46 37 /** 38 * Returns whether payload should be imported. 39 * 40 * @return bool 41 */ 42 public function should_import() { 43 // @todo how do we get this without importing the whole api object just for this? 44 if ( strtolower( $this->data->repository->full_name ) !== strtolower( $this->app->api()->fetch()->repository() ) ) { 45 return false; 46 } 47 47 48 49 50 48 // The last term in the ref is the payload_branch name. 49 $refs = explode( '/', $this->data->ref ); 50 $payload_branch = array_pop( $refs ); 51 51 52 52 $branch = $this->app->api()->fetch()->branch(); 53 53 54 55 56 54 if ( $branch !== $payload_branch ) { 55 return false; 56 } 57 57 58 59 58 // We add a tag to commits we push out, so we shouldn't pull them in again. 59 $tag = apply_filters( 'wogh_commit_msg_tag', 'wogh' ); 60 60 61 62 63 61 if ( ! $tag ) { 62 throw new Exception( __( 'Commit message tag not set. Filter `wogh_commit_msg_tag` misconfigured.', 'writing-on-github' ) ); 63 } 64 64 65 66 67 65 if ( $tag === substr( $this->message(), -1 * strlen( $tag ) ) ) { 66 return false; 67 } 68 68 69 70 71 69 if ( ! $this->get_commit_id() ) { 70 return false; 71 } 72 72 73 74 73 return true; 74 } 75 75 76 77 78 76 public function get_before_commit_id() { 77 return $this->data->before ? $this->data->before : null; 78 } 79 79 80 81 82 83 84 85 86 87 80 /** 81 * Returns the sha of the head commit. 82 * 83 * @return string 84 */ 85 public function get_commit_id() { 86 return $this->data->head_commit ? $this->data->head_commit->id : null; 87 } 88 88 89 90 91 92 93 94 95 96 89 /** 90 * Returns the email address for the commit author. 91 * 92 * @return string 93 */ 94 public function get_author_email() { 95 return $this->data->head_commit->author->email; 96 } 97 97 98 99 100 101 102 103 104 105 98 /** 99 * Returns array commits for the payload. 100 * 101 * @return array 102 */ 103 public function get_commits() { 104 return $this->data->commits; 105 } 106 106 107 108 109 110 111 112 113 114 107 /** 108 * Returns the repository's full name. 109 * 110 * @return string 111 */ 112 public function get_repository_name() { 113 return $this->data->repository->full_name; 114 } 115 115 116 117 118 119 120 121 122 123 116 /** 117 * Returns the payload's commit message. 118 * 119 * @return string 120 */ 121 protected function message() { 122 return $this->data->head_commit->message; 123 } 124 124 } -
writing-on-github/trunk/lib/post.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Post { 11 11 12 /** 13 * Api object 14 * 15 * @var Writing_On_GitHub_Api 16 */ 17 public $api; 18 19 /** 20 * Post ID 21 * @var integer 22 */ 23 public $id = 0; 24 25 /** 26 * Post object 27 * @var WP_Post 28 */ 29 public $post; 30 31 /** 32 * Post args. 33 * 34 * @var array 35 */ 36 protected $args; 37 38 /** 39 * Post meta. 40 * 41 * @var array 42 */ 43 protected $meta; 44 45 /** 46 * Whether the post has been saved. 47 * 48 * @var bool 49 */ 50 protected $new = true; 51 52 53 protected $old_github_path; 54 55 /** 56 * Instantiates a new Post object 57 * 58 * @param int|array $id_or_args Either a post ID or an array of arguments. 59 * @param Writing_On_GitHub_Api $api API object. 60 * 61 * @todo remove database operations from this method 62 */ 63 public function __construct( $id_or_args, Writing_On_GitHub_Api $api ) { 64 $this->api = $api; 65 66 if ( is_numeric( $id_or_args ) ) { 67 $this->id = (int) $id_or_args; 68 $this->post = get_post( $this->id ); 69 $this->new = false; 70 } 71 72 if ( is_array( $id_or_args ) ) { 73 $this->args = $id_or_args; 74 75 if ( isset( $this->args['ID'] ) ) { 76 $this->post = get_post( $this->args['ID'] ); 77 78 if ( $this->post ) { 79 $this->id = $this->post->ID; 80 $this->new = false; 81 } else { 82 unset( $this->args['ID'] ); 83 } 84 } 85 } 86 } 87 88 public function id() { 89 return $this->id; 90 } 91 92 /** 93 * Returns the post type 94 */ 95 public function type() { 96 return $this->post->post_type; 97 } 98 99 /** 100 * Returns the post type 101 */ 102 public function status() { 103 return $this->post->post_status; 104 } 105 106 /** 107 * Returns the post name 108 */ 109 public function name() { 110 return $this->post->post_name; 111 } 112 113 /** 114 * Returns true if the post has a password 115 * @return bool 116 */ 117 public function has_password() { 118 return ! empty( $this->post->post_password ); 119 } 120 121 /** 122 * Combines the 2 content parts for GitHub 123 */ 124 public function github_content() { 125 $content = $this->front_matter() . $this->post_content(); 126 $ending = apply_filters( 'wogh_line_endings', "\n" ); 127 128 return preg_replace( '~(*BSR_ANYCRLF)\R~', $ending, $content ); 129 } 130 131 /** 132 * The post's YAML frontmatter 133 * 134 * Returns String the YAML frontmatter, ready to be written to the file 135 */ 136 public function front_matter() { 137 return "---\n" . spyc_dump( $this->meta() ) . "---\n"; 138 } 139 140 /** 141 * Returns the post_content 142 * 143 * Markdownify's the content if applicable 144 */ 145 public function post_content() { 146 $content = $this->post->post_content; 147 148 if ( function_exists( 'wpmarkdown_html_to_markdown' ) ) { 149 $content = wpmarkdown_html_to_markdown( $content ); 150 } else if ( class_exists( 'WPCom_Markdown' ) ) { 151 if ( WPCom_Markdown::get_instance()->is_markdown( $this->post->ID ) ) { 152 $content = $this->post->post_content_filtered; 153 } 154 } 155 156 return apply_filters( 'wogh_content_export', $content, $this ); 157 } 158 159 public function old_github_path() { 160 return $this->old_github_path; 161 } 162 163 public function set_old_github_path( $path ) { 164 $this->old_github_path = $path; 165 update_post_meta( $this->id, '_wogh_github_path', $path); 166 } 167 168 169 170 /** 171 * Retrieves or calculates the proper GitHub path for a given post 172 * 173 * Returns (string) the path relative to repo root 174 */ 175 public function github_path() { 176 $path = $this->github_directory() . $this->github_filename(); 177 178 return $path; 179 } 180 181 /** 182 * Get GitHub directory based on post 183 * 184 * @return string 185 */ 186 public function github_directory() { 187 if ( 'publish' !== $this->status() ) { 188 return apply_filters( 'wogh_directory_unpublished', '_drafts/', $this ); 189 } 190 191 $name = ''; 192 193 switch ( $this->type() ) { 194 case 'post': 195 $name = 'posts'; 196 break; 197 case 'page': 198 $name = 'pages'; 199 break; 200 default: 201 $obj = get_post_type_object( $this->type() ); 202 203 if ( $obj ) { 204 $name = strtolower( $obj->labels->name ); 205 } 206 207 if ( ! $name ) { 208 $name = ''; 209 } 210 } 211 212 if ( $name ) { 213 $name = '_' . $name . '/'; 214 } 215 216 return apply_filters( 'wogh_directory_published', $name, $this ); 217 } 218 219 /** 220 * Build GitHub filename based on post 221 */ 222 public function github_filename() { 223 if ( 'post' === $this->type() ) { 224 $filename = get_the_time( 'Y-m-d-', $this->id ) . $this->get_name() . '.md'; 225 } else { 226 $filename = $this->get_name() . '.md'; 227 } 228 229 return apply_filters( 'wogh_filename', $filename, $this ); 230 } 231 232 /** 233 * Returns a post slug we can use in the GitHub filename 234 * 235 * @return string 236 */ 237 protected function get_name() { 238 if ( '' !== $this->name() ) { 239 return $this->name(); 240 } 241 242 return sanitize_title( get_the_title( $this->post ) ); 243 } 244 245 /** 246 * is put on github 247 * @return boolean 248 */ 249 public function is_on_github() { 250 $sha = get_post_meta( $this->id, '_wogh_sha', true ); 251 $github_path = get_post_meta( $this->id, '_wogh_github_path', true ); 252 if ( $sha && $github_path ) { 253 return true; 254 } 255 return false; 256 } 257 258 /** 259 * Returns the URL for the post on GitHub. 260 * 261 * @return string 262 */ 263 public function github_view_url() { 264 $github_path = get_post_meta( $this->id, '_wogh_github_path', true ); 265 $repository = $this->api->fetch()->repository(); 266 $branch = $this->api->fetch()->branch(); 267 268 return "https://github.com/$repository/blob/$branch/$github_path"; 269 } 270 271 /** 272 * Returns the URL for the post on GitHub. 273 * 274 * @return string 275 */ 276 public function github_edit_url() { 277 $github_path = get_post_meta( $this->id, '_wogh_github_path', true ); 278 $repository = $this->api->fetch()->repository(); 279 $branch = $this->api->fetch()->branch(); 280 281 return "https://github.com/$repository/edit/$branch/$github_path"; 282 } 283 284 /** 285 * Retrieve post type directory from blob path. 286 * 287 * @param string $path Path string. 288 * 289 * @return string 290 */ 291 public function get_directory_from_path( $path ) { 292 $directory = explode( '/', $path ); 293 $directory = count( $directory ) > 0 ? $directory[0] : ''; 294 295 return $directory; 296 } 297 298 /** 299 * Determines the last author to modify the post 300 * 301 * Returns Array an array containing the author name and email 302 */ 303 public function last_modified_author() { 304 if ( $last_id = get_post_meta( $this->id, '_edit_last', true ) ) { 305 $user = get_userdata( $last_id ); 306 307 if ( $user ) { 308 return array( 'name' => $user->display_name, 'email' => $user->user_email ); 309 } 310 } 311 312 return array(); 313 } 314 315 /** 316 * The post's sha 317 * Cached as post meta, or will make a live call if need be 318 * 319 * Returns String the sha1 hash 320 */ 321 public function sha() { 322 $sha = get_post_meta( $this->id, '_wogh_sha', true ); 323 324 // If we've done a full export and we have no sha 325 // then we should try a live check to see if it exists. 326 // if ( ! $sha && 'yes' === get_option( '_wogh_fully_exported' ) ) { 327 328 // // @todo could we eliminate this by calling down the full tree and searching it 329 // $data = $this->api->fetch()->remote_contents( $this ); 330 331 // if ( ! is_wp_error( $data ) ) { 332 // update_post_meta( $this->id, '_wogh_sha', $data->sha ); 333 // $sha = $data->sha; 334 // } 335 // } 336 337 // if the sha still doesn't exist, then it's empty 338 if ( ! $sha || is_wp_error( $sha ) ) { 339 $sha = ''; 340 } 341 342 return $sha; 343 } 344 345 /** 346 * Save the sha to post 347 * 348 * @param string $sha 349 */ 350 public function set_sha( $sha ) { 351 update_post_meta( $this->id, '_wogh_sha', $sha ); 352 } 353 354 /** 355 * The post's metadata 356 * 357 * Returns Array the post's metadata 358 */ 359 public function meta() { 360 $meta = array( 361 'ID' => $this->id, 362 'post_title' => get_the_title( $this->post ), 363 'post_name' => $this->post->post_name, 364 'author' => ( $author = get_userdata( $this->post->post_author ) ) ? $author->display_name : '', 365 'post_date' => $this->post->post_date, 366 'post_excerpt' => $this->post->post_excerpt, 367 'layout' => get_post_type( $this->post ), 368 'link' => get_permalink( $this->post ), 369 'published' => 'publish' === $this->status() ? true : false, 370 'tags' => wp_get_post_tags( $this->id, array( 'fields' => 'names' ) ), 371 'categories' => wp_get_post_categories( $this->id, array( 'fields' => 'names' ) ) 372 ); 373 if ( empty($this->post->post_name) ) { 374 unset($meta['post_name']); 375 } 376 if ( empty($this->post->post_excerpt) ) { 377 unset($meta['post_excerpt']); 378 } 12 /** 13 * Api object 14 * 15 * @var Writing_On_GitHub_Api 16 */ 17 public $api; 18 19 /** 20 * Post ID 21 * @var integer 22 */ 23 public $id = 0; 24 25 /** 26 * Blob object 27 * @var Writing_On_GitHub_Blob 28 */ 29 public $blob; 30 31 /** 32 * Post object 33 * @var WP_Post 34 */ 35 public $post; 36 37 /** 38 * Post args. 39 * 40 * @var array 41 */ 42 protected $args; 43 44 /** 45 * Post meta. 46 * 47 * @var array 48 */ 49 protected $meta; 50 51 /** 52 * Whether the post has been saved. 53 * 54 * @var bool 55 */ 56 protected $new = true; 57 58 59 protected $old_github_path; 60 61 /** 62 * Instantiates a new Post object 63 * 64 * @param int|array $id_or_args Either a post ID or an array of arguments. 65 * @param Writing_On_GitHub_Api $api API object. 66 * 67 * @todo remove database operations from this method 68 */ 69 public function __construct( $id_or_args, Writing_On_GitHub_Api $api ) { 70 $this->api = $api; 71 72 if ( is_numeric( $id_or_args ) ) { 73 $this->id = (int) $id_or_args; 74 $this->post = get_post( $this->id ); 75 $this->new = false; 76 } 77 78 if ( is_array( $id_or_args ) ) { 79 $this->args = $id_or_args; 80 81 if ( isset( $this->args['ID'] ) ) { 82 $this->post = get_post( $this->args['ID'] ); 83 84 if ( $this->post ) { 85 $this->id = $this->post->ID; 86 $this->new = false; 87 } else { 88 unset( $this->args['ID'] ); 89 } 90 } 91 } 92 } 93 94 public function id() { 95 return $this->id; 96 } 97 98 /** 99 * Returns the post type 100 */ 101 public function type() { 102 return $this->post->post_type; 103 } 104 105 /** 106 * Returns the post type 107 */ 108 public function status() { 109 return $this->post->post_status; 110 } 111 112 /** 113 * Returns the post name 114 */ 115 public function name() { 116 return $this->post->post_name; 117 } 118 119 /** 120 * Returns true if the post has a password 121 * @return bool 122 */ 123 public function has_password() { 124 return ! empty( $this->post->post_password ); 125 } 126 127 /** 128 * Combines the 2 content parts for GitHub 129 */ 130 public function github_content() { 131 $use_blob = wogh_is_dont_export_content() && $this->blob; 132 $content = $use_blob ? 133 $this->blob->post_content() : 134 $this->post_content(); 135 136 return $this->front_matter() . $content; 137 // $content = $this->front_matter() . $content; 138 // $ending = apply_filters( 'wogh_line_endings', "\n" ); 139 140 // return preg_replace( '~(*BSR_ANYCRLF)\R~', $ending, $content ); 141 } 142 143 /** 144 * The post's YAML frontmatter 145 * 146 * Returns String the YAML frontmatter, ready to be written to the file 147 */ 148 public function front_matter() { 149 return "---\n" . spyc_dump( $this->meta() ) . "---\n"; 150 } 151 152 /** 153 * Returns the post_content 154 * 155 * Markdownify's the content if applicable 156 */ 157 public function post_content() { 158 $content = $this->post->post_content; 159 160 if ( function_exists( 'wpmarkdown_html_to_markdown' ) ) { 161 $content = wpmarkdown_html_to_markdown( $content ); 162 } else if ( class_exists( 'WPCom_Markdown' ) ) { 163 if ( WPCom_Markdown::get_instance()->is_markdown( $this->post->ID ) ) { 164 $content = $this->post->post_content_filtered; 165 } 166 } 167 168 return apply_filters( 'wogh_content_export', $content, $this ); 169 } 170 171 public function old_github_path() { 172 return $this->old_github_path; 173 } 174 175 public function set_old_github_path( $path ) { 176 $this->old_github_path = $path; 177 update_post_meta( $this->id, '_wogh_github_path', $path ); 178 } 179 180 181 /** 182 * Retrieves or calculates the proper GitHub path for a given post 183 * 184 * Returns (string) the path relative to repo root 185 */ 186 public function github_path() { 187 $path = $this->github_directory() . $this->github_filename(); 188 189 return $path; 190 } 191 192 /** 193 * Get GitHub directory based on post 194 * 195 * @return string 196 */ 197 public function github_directory() { 198 if ( 'publish' !== $this->status() ) { 199 return apply_filters( 'wogh_directory_unpublished', '_drafts/', $this ); 200 } 201 202 $name = ''; 203 204 switch ( $this->type() ) { 205 case 'post': 206 $name = 'posts'; 207 break; 208 case 'page': 209 $name = 'pages'; 210 break; 211 default: 212 $obj = get_post_type_object( $this->type() ); 213 214 if ( $obj ) { 215 $name = strtolower( $obj->labels->name ); 216 } 217 218 if ( ! $name ) { 219 $name = ''; 220 } 221 } 222 223 if ( $name ) { 224 $name = '_' . $name . '/'; 225 } 226 227 return apply_filters( 'wogh_directory_published', $name, $this ); 228 } 229 230 /** 231 * Build GitHub filename based on post 232 */ 233 public function github_filename() { 234 if ( 'post' === $this->type() ) { 235 $filename = get_the_time( 'Y-m-d-', $this->id ) . $this->get_name() . '.md'; 236 } else { 237 $filename = $this->get_name() . '.md'; 238 } 239 240 return apply_filters( 'wogh_filename', $filename, $this ); 241 } 242 243 /** 244 * Returns a post slug we can use in the GitHub filename 245 * 246 * @return string 247 */ 248 protected function get_name() { 249 if ( '' !== $this->name() ) { 250 return $this->name(); 251 } 252 253 return sanitize_title( get_the_title( $this->post ) ); 254 } 255 256 /** 257 * is put on github 258 * @return boolean 259 */ 260 public function is_on_github() { 261 $sha = get_post_meta( $this->id, '_wogh_sha', true ); 262 $github_path = get_post_meta( $this->id, '_wogh_github_path', true ); 263 if ( $sha && $github_path ) { 264 return true; 265 } 266 return false; 267 } 268 269 /** 270 * Returns the URL for the post on GitHub. 271 * 272 * @return string 273 */ 274 public function github_view_url() { 275 $github_path = get_post_meta( $this->id, '_wogh_github_path', true ); 276 $repository = $this->api->fetch()->repository(); 277 $branch = $this->api->fetch()->branch(); 278 279 return "https://github.com/$repository/blob/$branch/$github_path"; 280 } 281 282 /** 283 * Returns the URL for the post on GitHub. 284 * 285 * @return string 286 */ 287 public function github_edit_url() { 288 $github_path = get_post_meta( $this->id, '_wogh_github_path', true ); 289 $repository = $this->api->fetch()->repository(); 290 $branch = $this->api->fetch()->branch(); 291 292 return "https://github.com/$repository/edit/$branch/$github_path"; 293 } 294 295 /** 296 * Retrieve post type directory from blob path. 297 * 298 * @param string $path Path string. 299 * 300 * @return string 301 */ 302 public function get_directory_from_path( $path ) { 303 $directory = explode( '/', $path ); 304 $directory = count( $directory ) > 0 ? $directory[0] : ''; 305 306 return $directory; 307 } 308 309 /** 310 * Determines the last author to modify the post 311 * 312 * Returns Array an array containing the author name and email 313 */ 314 public function last_modified_author() { 315 if ( $last_id = get_post_meta( $this->id, '_edit_last', true ) ) { 316 $user = get_userdata( $last_id ); 317 318 if ( $user ) { 319 return array( 'name' => $user->display_name, 'email' => $user->user_email ); 320 } 321 } 322 323 return array(); 324 } 325 326 /** 327 * The post's sha 328 * Cached as post meta, or will make a live call if need be 329 * 330 * Returns String the sha1 hash 331 */ 332 public function sha() { 333 $sha = get_post_meta( $this->id, '_wogh_sha', true ); 334 335 // If we've done a full export and we have no sha 336 // then we should try a live check to see if it exists. 337 // if ( ! $sha && 'yes' === get_option( '_wogh_fully_exported' ) ) { 338 339 // // @todo could we eliminate this by calling down the full tree and searching it 340 // $data = $this->api->fetch()->remote_contents( $this ); 341 342 // if ( ! is_wp_error( $data ) ) { 343 // update_post_meta( $this->id, '_wogh_sha', $data->sha ); 344 // $sha = $data->sha; 345 // } 346 // } 347 348 // if the sha still doesn't exist, then it's empty 349 if ( ! $sha || is_wp_error( $sha ) ) { 350 $sha = ''; 351 } 352 353 return $sha; 354 } 355 356 /** 357 * Save the sha to post 358 * 359 * @param string $sha 360 */ 361 public function set_sha( $sha ) { 362 update_post_meta( $this->id, '_wogh_sha', $sha ); 363 } 364 365 /** 366 * The post's metadata 367 * 368 * Returns Array the post's metadata 369 */ 370 public function meta() { 371 $meta = array( 372 'ID' => $this->id, 373 'post_title' => get_the_title( $this->post ), 374 'post_name' => $this->post->post_name, 375 'author' => ( $author = get_userdata( $this->post->post_author ) ) ? $author->display_name : '', 376 'post_date' => $this->post->post_date, 377 'post_excerpt' => $this->post->post_excerpt, 378 'layout' => get_post_type( $this->post ), 379 'link' => get_permalink( $this->post ), 380 'published' => 'publish' === $this->status() ? true : false, 381 'tags' => wp_get_post_tags( $this->id, array( 'fields' => 'names' ) ), 382 'categories' => wp_get_post_categories( $this->id, array( 'fields' => 'names' ) ) 383 ); 384 if ( empty($this->post->post_name) ) { 385 unset($meta['post_name']); 386 } 387 if ( empty($this->post->post_excerpt) ) { 388 unset($meta['post_excerpt']); 389 } 379 390 if ( 'yes' == get_option('wogh_ignore_author') ) { 380 391 unset($meta['author']); 381 392 } 382 393 383 //convert traditional post_meta values, hide hidden values, skip already populated values 384 // foreach ( get_post_custom( $this->id ) as $key => $value ) { 385 386 // if ( '_' === substr( $key, 0, 1 ) || isset( $meta[ $key ] ) ) { 387 // continue; 388 // } 389 390 // $meta[ $key ] = $value; 391 392 // } 393 394 return apply_filters( 'wogh_post_meta', $meta, $this ); 395 } 396 397 /** 398 * Returns whether the Post has been saved in the DB yet. 399 * 400 * @return bool 401 */ 402 public function is_new() { 403 return $this->new; 404 } 405 406 /** 407 * Sets the Post's meta. 408 * 409 * @param array $meta 410 */ 411 public function set_meta( $meta ) { 412 $this->meta = $meta; 413 } 414 415 /** 416 * Returns the Post's arguments. 417 * 418 * @return array 419 */ 420 public function get_args() { 421 return $this->args; 422 } 423 424 /** 425 * Returns the Post's meta. 426 * 427 * @return array 428 */ 429 public function get_meta() { 430 return $this->meta; 431 } 432 433 /** 434 * Sets the Post's WP_Post object. 435 * 436 * @param WP_Post $post 437 * 438 * @return $this 439 */ 440 public function set_post( WP_Post $post ) { 441 $this->post = $post; 442 $this->id = $post->ID; 443 444 return $this; 445 } 446 447 /** 448 * Transforms the Post into a Blob. 449 * 450 * @return Writing_On_GitHub_Blob 451 */ 452 public function to_blob() { 453 $data = new stdClass; 454 455 $data->path = $this->github_path(); 456 $data->content = $this->github_content(); 457 $data->sha = $this->sha(); 458 459 return new Writing_On_GitHub_Blob( $data ); 460 } 394 //convert traditional post_meta values, hide hidden values, skip already populated values 395 // foreach ( get_post_custom( $this->id ) as $key => $value ) { 396 397 // if ( '_' === substr( $key, 0, 1 ) || isset( $meta[ $key ] ) ) { 398 // continue; 399 // } 400 401 // $meta[ $key ] = $value; 402 403 // } 404 405 return apply_filters( 'wogh_post_meta', $meta, $this ); 406 } 407 408 /** 409 * Returns whether the Post has been saved in the DB yet. 410 * 411 * @return bool 412 */ 413 public function is_new() { 414 return $this->new; 415 } 416 417 /** 418 * Sets the Post's meta. 419 * 420 * @param array $meta 421 */ 422 public function set_meta( $meta ) { 423 $this->meta = $meta; 424 } 425 426 /** 427 * Returns the Post's arguments. 428 * 429 * @return array 430 */ 431 public function get_args() { 432 return $this->args; 433 } 434 435 /** 436 * Returns the Post's meta. 437 * 438 * @return array 439 */ 440 public function get_meta() { 441 return $this->meta; 442 } 443 444 /** 445 * Get the blob 446 * @return Writing_On_GitHub_Blob 447 */ 448 public function get_blob() { 449 return $this->blob; 450 } 451 452 /** 453 * Set the blob 454 * @param Writing_On_GitHub_Blob $blob 455 */ 456 public function set_blob( Writing_On_GitHub_Blob $blob ) { 457 $this->blob = $blob; 458 } 459 460 /** 461 * Sets the Post's WP_Post object. 462 * 463 * @param WP_Post $post 464 * 465 * @return $this 466 */ 467 public function set_post( WP_Post $post ) { 468 $this->post = $post; 469 $this->id = $post->ID; 470 471 return $this; 472 } 473 474 /** 475 * Transforms the Post into a Blob. 476 * 477 * @return Writing_On_GitHub_Blob 478 */ 479 public function to_blob() { 480 $data = new stdClass; 481 482 $data->path = $this->github_path(); 483 $data->content = $this->github_content(); 484 $data->sha = $this->sha(); 485 486 return new Writing_On_GitHub_Blob( $data ); 487 } 461 488 } -
writing-on-github/trunk/lib/request.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Request { 11 11 12 13 14 15 16 17 12 /** 13 * Application container. 14 * 15 * @var Writing_On_GitHub 16 */ 17 protected $app; 18 18 19 20 21 22 23 24 19 /** 20 * Raw request data. 21 * 22 * @var string 23 */ 24 protected $raw_data; 25 25 26 27 28 29 30 26 /** 27 * Headers 28 * @var array 29 */ 30 protected $headers; 31 31 32 33 34 35 36 37 38 39 32 /** 33 * Writing_On_GitHub_Request constructor. 34 * 35 * @param Writing_On_GitHub $app Application container. 36 */ 37 public function __construct( Writing_On_GitHub $app ) { 38 $this->app = $app; 39 } 40 40 41 42 43 44 45 46 47 41 /** 42 * Validates the header's secret. 43 * 44 * @return true|WP_Error 45 */ 46 public function is_secret_valid() { 47 $headers = $this->headers(); 48 48 49 49 $this->raw_data = $this->read_raw_data(); 50 50 51 52 53 54 55 51 // Validate request secret. 52 $hash = hash_hmac( 'sha1', $this->raw_data, $this->secret() ); 53 if ( 'sha1=' . $hash !== $headers['X-Hub-Signature'] ) { 54 return false; 55 } 56 56 57 //[X-Hub-Signature] => sha1=3cf3da70de401f7dfff053392f60cc534efed3b458 59 60 57 // [X-Hub-Signature] => sha1=3cf3da70de401f7dfff053392f60cc534efed3b4 58 // [Content-Type] => application/json 59 // [X-Github-Delivery] => b2102500-0acf-11e7-8acb-fd86a3497c2f 60 // [X-Github-Event] => ping 61 61 62 63 62 return true; 63 } 64 64 65 66 67 68 69 70 65 /** 66 * Validates the ping event. 67 * @return boolean 68 */ 69 public function is_ping() { 70 $headers = $this->headers(); 71 71 72 73 74 72 $event = $headers['X-Github-Event']; 73 return 'ping' == $event; 74 } 75 75 76 77 78 79 80 81 76 /** 77 * Validates the push event. 78 * @return boolean 79 */ 80 public function is_push() { 81 $headers = $this->headers(); 82 82 83 84 85 83 $event = $headers['X-Github-Event']; 84 return 'push' == $event; 85 } 86 86 87 88 89 90 91 92 93 94 87 /** 88 * Returns a payload object for the given request. 89 * 90 * @return Writing_On_GitHub_Payload 91 */ 92 public function payload() { 93 return new Writing_On_GitHub_Payload( $this->app, $this->raw_data ); 94 } 95 95 96 97 98 99 100 101 102 103 104 if ( $this->headers) {105 106 96 /** 97 * Cross-server header support. 98 * 99 * Returns an array of the request's headers. 100 * 101 * @return array 102 */ 103 protected function headers() { 104 if ( ! empty( $this->headers ) ) { 105 return $this->headers; 106 } 107 107 108 108 if ( function_exists( 'getallheaders' ) ) { 109 109 110 111 112 113 114 115 116 117 118 119 120 121 122 110 $this->headers = getallheaders(); 111 return $this->headers; 112 } 113 /** 114 * Nginx and pre 5.4 workaround. 115 * @see http://www.php.net/manual/en/function.getallheaders.php 116 */ 117 $this->headers = array(); 118 foreach ( $_SERVER as $name => $value ) { 119 if ( 'HTTP_' === substr( $name, 0, 5 ) ) { 120 $this->headers[ str_replace( ' ', '-', ucwords( strtolower( str_replace( '_', ' ', substr( $name, 5 ) ) ) ) ) ] = $value; 121 } 122 } 123 123 124 125 124 return $this->headers; 125 } 126 126 127 128 129 130 131 132 133 134 127 /** 128 * Reads the raw data from STDIN. 129 * 130 * @return string 131 */ 132 protected function read_raw_data() { 133 return file_get_contents( 'php://input' ); 134 } 135 135 136 137 138 139 140 141 142 143 136 /** 137 * Returns the Webhook secret 138 * 139 * @return string 140 */ 141 protected function secret() { 142 return get_option( 'wogh_secret' ); 143 } 144 144 } -
writing-on-github/trunk/lib/response.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Response { 11 11 12 13 14 15 16 17 12 /** 13 * Application container. 14 * 15 * @var Writing_On_GitHub 16 */ 17 protected $app; 18 18 19 20 21 22 23 24 25 26 19 /** 20 * Writing_On_GitHub_Response constructor. 21 * 22 * @param Writing_On_GitHub $app Application container. 23 */ 24 public function __construct( Writing_On_GitHub $app ) { 25 $this->app = $app; 26 } 27 27 28 29 30 31 32 33 34 35 36 28 /** 29 * Writes to the log and returns the error response. 30 * 31 * @param WP_Error $error Error to respond with. 32 * 33 * @return false 34 */ 35 public function error( WP_Error $error ) { 36 global $wp_version; 37 37 38 38 $this->log( $error ); 39 39 40 41 42 43 44 45 46 47 40 if ( defined( 'DOING_AJAX' ) && DOING_AJAX && defined( 'WOGH_AJAX' ) && WOGH_AJAX ) { 41 /** 42 * WordPress 4.1.0 introduced allowing WP_Error objects to be 43 * passed directly into `wp_send_json_error`. This shims in 44 * compatibility for older versions. We're currently supporting 3.9+. 45 */ 46 if ( version_compare( $wp_version, '4.1.0', '<' ) ) { 47 $result = array(); 48 48 49 50 51 52 53 49 foreach ( $error->errors as $code => $messages ) { 50 foreach ( $messages as $message ) { 51 $result[] = array( 'code' => $code, 'message' => $message ); 52 } 53 } 54 54 55 56 55 $error = $result; 56 } 57 57 58 59 58 wp_send_json_error( $error ); 59 } 60 60 61 62 61 return false; 62 } 63 63 64 65 66 67 68 69 70 71 72 64 /** 65 * Writes to the log and returns the success response. 66 * 67 * @param string $success Success message to respond with. 68 * 69 * @return true 70 */ 71 public function success( $success ) { 72 $this->log( $success ); 73 73 74 75 76 74 if ( defined( 'DOING_AJAX' ) && DOING_AJAX && defined( 'WOGH_AJAX' ) && WOGH_AJAX ) { 75 wp_send_json_success( $success ); 76 } 77 77 78 79 78 return true; 79 } 80 80 81 82 83 84 85 86 87 88 89 90 91 81 /** 82 * Writes a log message. 83 * 84 * Can extract a message from WP_Error object. 85 * 86 * @param string|WP_Error $msg Message to log. 87 */ 88 protected function log( $msg ) { 89 if ( is_wp_error( $msg ) ) { 90 $msg = $msg->get_error_message(); 91 } 92 92 93 94 93 Writing_On_GitHub::write_log( $msg ); 94 } 95 95 } -
writing-on-github/trunk/lib/semaphore.php
r1709881 r1713007 10 10 class Writing_On_GitHub_Semaphore { 11 11 12 13 14 15 12 /** 13 * Sempahore's option key. 14 */ 15 const KEY = 'wogh_semaphore_lock'; 16 16 17 18 19 20 17 /** 18 * Option key when semaphore is locked. 19 */ 20 const VALUE_LOCKED = 'yes'; 21 21 22 23 24 25 22 /** 23 * Option key when semaphore is unlocked. 24 */ 25 const VALUE_UNLOCKED = 'no'; 26 26 27 28 29 30 31 32 27 /** 28 * Clean up the old values on instantiation. 29 */ 30 public function __construct() { 31 delete_option( self::KEY ); 32 } 33 33 34 35 36 37 38 39 40 41 42 43 44 45 34 /** 35 * Checks if the Semaphore is open. 36 * 37 * Fails to report it's open if the the Api class can't make a call 38 * or the push lock has been enabled. 39 * 40 * @return bool 41 */ 42 public function is_open() { 43 if ( self::VALUE_LOCKED === get_transient( self::KEY ) ) { 44 return false; 45 } 46 46 47 48 47 return true; 48 } 49 49 50 51 52 53 54 55 50 /** 51 * Enables the push lock. 52 */ 53 public function lock() { 54 set_transient( self::KEY, self::VALUE_LOCKED, MINUTE_IN_SECONDS ); 55 } 56 56 57 58 59 60 61 62 57 /** 58 * Disables the push lock. 59 */ 60 public function unlock() { 61 set_transient( self::KEY, self::VALUE_UNLOCKED, MINUTE_IN_SECONDS ); 62 } 63 63 } -
writing-on-github/trunk/readme.txt
r1709881 r1713007 1 1 === Writing On GitHub === 2 3 [](https://travis-ci.org/litefeel/writing-on-github) 4 [](https://scrutinizer-ci.com/g/litefeel/writing-on-github/?branch=master) 5 2 6 Contributors: litefeel 3 7 Tags: github, git, version control, content, collaboration, publishing -
writing-on-github/trunk/writing-on-github.php
r1709881 r1713007 13 13 // If the functions have already been autoloaded, don't reload. 14 14 // This fixes function duplication during unit testing. 15 if ( defined( 'WRITING_ON_GITHUB_TEST' ) && WRITING_ON_GITHUB_TEST ) { 16 $path = dirname( __FILE__ ) . '/vendor/autoload_52.php'; 17 include_once $path;15 $path = dirname( __FILE__ ) . '/vendor/autoload.php'; 16 if ( file_exists( $path ) ) { 17 require_once( $path ); 18 18 } 19 19 20 21 require_once(dirname(__FILE__) . '/Spyc.php');22 require_once(dirname(__FILE__) . '/lib/cache.php');23 require_once(dirname(__FILE__) . '/lib/database.php');24 require_once(dirname(__FILE__) . '/lib/admin.php');25 require_once(dirname(__FILE__) . '/lib/payload.php');26 require_once(dirname(__FILE__) . '/lib/post.php');27 // require_once(dirname(__FILE__) . '/lib/cli.php');28 require_once(dirname(__FILE__) . '/lib/controller.php');29 require_once(dirname(__FILE__) . '/lib/export.php');30 require_once(dirname(__FILE__) . '/lib/semaphore.php');31 require_once(dirname(__FILE__) . '/lib/request.php');32 require_once(dirname(__FILE__) . '/lib/client/base.php');33 require_once(dirname(__FILE__) . '/lib/client/fetch.php');34 require_once(dirname(__FILE__) . '/lib/client/persist.php');35 require_once(dirname(__FILE__) . '/lib/import.php');36 require_once(dirname(__FILE__) . '/lib/api.php');37 require_once(dirname(__FILE__) . '/lib/fileinfo.php');38 require_once(dirname(__FILE__) . '/lib/blob.php');39 require_once(dirname(__FILE__) . '/lib/response.php');40 // require_once(dirname(__FILE__) . '/views/setting-field.php');41 // require_once(dirname(__FILE__) . '/views/options.php');42 // require_once(dirname(__FILE__) . '/views/user-setting-field.php');43 44 20 add_action( 'plugins_loaded', array( new Writing_On_GitHub, 'boot' ) ); 45 21 46 22 class Writing_On_GitHub { 47 23 48 /** 49 * Object instance 50 * @var self 51 */ 52 public static $instance; 53 54 /** 55 * Language text domain 56 * @var string 57 */ 58 public static $text_domain = 'writing-on-github'; 59 60 /** 61 * Controller object 62 * @var Writing_On_GitHub_Controller 63 */ 64 public $controller; 65 66 /** 67 * Controller object 68 * @var Writing_On_GitHub_Admin 69 */ 70 public $admin; 71 72 /** 73 * CLI object. 74 * 75 * @var Writing_On_GitHub_CLI 76 */ 77 protected $cli; 78 79 /** 80 * Request object. 81 * 82 * @var Writing_On_GitHub_Request 83 */ 84 protected $request; 85 86 /** 87 * Response object. 88 * 89 * @var Writing_On_GitHub_Response 90 */ 91 protected $response; 92 93 /** 94 * Api object. 95 * 96 * @var Writing_On_GitHub_Api 97 */ 98 protected $api; 99 100 /** 101 * Import object. 102 * 103 * @var Writing_On_GitHub_Import 104 */ 105 protected $import; 106 107 /** 108 * Export object. 109 * 110 * @var Writing_On_GitHub_Export 111 */ 112 protected $export; 113 114 /** 115 * Semaphore object. 116 * 117 * @var Writing_On_GitHub_Semaphore 118 */ 119 protected $semaphore; 120 121 /** 122 * Database object. 123 * 124 * @var Writing_On_GitHub_Database 125 */ 126 protected $database; 127 128 /** 129 * Cache object. 130 * 131 * @var Writing_On_GitHub_Cache 132 */ 133 protected $cache; 134 135 /** 136 * Called at load time, hooks into WP core 137 */ 138 public function __construct() { 139 self::$instance = $this; 140 141 if ( is_admin() ) { 142 $this->admin = new Writing_On_GitHub_Admin; 143 } 144 145 $this->controller = new Writing_On_GitHub_Controller( $this ); 146 147 if ( defined( 'WP_CLI' ) && WP_CLI ) { 148 WP_CLI::add_command( 'wogh', $this->cli() ); 149 } 150 } 151 152 /** 153 * Attaches the plugin's hooks into WordPress. 154 */ 155 public function boot() { 156 register_activation_hook( __FILE__, array( $this, 'activate' ) ); 157 add_action( 'admin_notices', array( $this, 'activation_notice' ) ); 158 159 add_action( 'init', array( $this, 'l10n' ) ); 160 161 // Controller actions. 162 add_action( 'save_post', array( $this->controller, 'export_post' ) ); 163 add_action( 'delete_post', array( $this->controller, 'delete_post' ) ); 164 add_action( 'wp_ajax_nopriv_wogh_push_request', array( $this->controller, 'pull_posts' ) ); 24 /** 25 * Object instance 26 * @var self 27 */ 28 public static $instance; 29 30 /** 31 * Language text domain 32 * @var string 33 */ 34 public static $text_domain = 'writing-on-github'; 35 36 /** 37 * Controller object 38 * @var Writing_On_GitHub_Controller 39 */ 40 public $controller; 41 42 /** 43 * Controller object 44 * @var Writing_On_GitHub_Admin 45 */ 46 public $admin; 47 48 /** 49 * CLI object. 50 * 51 * @var Writing_On_GitHub_CLI 52 */ 53 protected $cli; 54 55 /** 56 * Request object. 57 * 58 * @var Writing_On_GitHub_Request 59 */ 60 protected $request; 61 62 /** 63 * Response object. 64 * 65 * @var Writing_On_GitHub_Response 66 */ 67 protected $response; 68 69 /** 70 * Api object. 71 * 72 * @var Writing_On_GitHub_Api 73 */ 74 protected $api; 75 76 /** 77 * Import object. 78 * 79 * @var Writing_On_GitHub_Import 80 */ 81 protected $import; 82 83 /** 84 * Export object. 85 * 86 * @var Writing_On_GitHub_Export 87 */ 88 protected $export; 89 90 /** 91 * Semaphore object. 92 * 93 * @var Writing_On_GitHub_Semaphore 94 */ 95 protected $semaphore; 96 97 /** 98 * Database object. 99 * 100 * @var Writing_On_GitHub_Database 101 */ 102 protected $database; 103 104 /** 105 * Called at load time, hooks into WP core 106 */ 107 public function __construct() { 108 self::$instance = $this; 109 110 if ( is_admin() ) { 111 $this->admin = new Writing_On_GitHub_Admin( plugin_basename( __FILE__ ) ); 112 } 113 114 $this->controller = new Writing_On_GitHub_Controller( $this ); 115 116 if ( defined( 'WP_CLI' ) && WP_CLI ) { 117 WP_CLI::add_command( 'wogh', $this->cli() ); 118 } 119 } 120 121 /** 122 * Attaches the plugin's hooks into WordPress. 123 */ 124 public function boot() { 125 register_activation_hook( __FILE__, array( $this, 'activate' ) ); 126 add_action( 'admin_notices', array( $this, 'activation_notice' ) ); 127 128 add_action( 'init', array( $this, 'l10n' ) ); 129 130 // Controller actions. 131 add_action( 'save_post', array( $this->controller, 'export_post' ) ); 132 add_action( 'delete_post', array( $this->controller, 'delete_post' ) ); 133 add_action( 'wp_ajax_nopriv_wogh_push_request', array( $this->controller, 'pull_posts' ) ); 165 134 add_action( 'wogh_export', array( $this->controller, 'export_all' ), 10, 2 ); 166 add_action( 'wogh_import', array( $this->controller, 'import_master' ), 10, 1 ); 167 add_filter( 'get_edit_post_link', array( $this, 'edit_post_link' ), 10, 3 ); 168 169 // add_filter( 'wogh_post_meta', array( $this, 'ignore_post_meta' ), 10, 1 ); 170 // add_filter( 'wogh_pre_import_meta', array( $this, 'ignore_post_meta' ), 10, 1 ); 171 add_filter( 'the_content', array( $this, 'the_content' ) ); 172 173 do_action( 'wogh_boot', $this ); 174 } 175 176 public function edit_post_link($link, $postID, $context) { 177 if ( ! wp_is_post_revision( $postID ) ) { 178 $post = new Writing_On_GitHub_Post( $postID, Writing_On_GitHub::$instance->api() ); 179 if ( $post->is_on_github() ) { 180 return $post->github_edit_url(); 181 } 182 } 183 184 return $link; 185 } 186 187 public function ignore_post_meta($meta) { 188 $ignore_meta_keys = get_option('wogh_ignore_metas'); 189 if (empty($ignore_meta_keys)) { 190 return $meta; 191 } 192 193 $keys = preg_split("/\\r\\n|\\r|\\n/", $ignore_meta_keys); 194 if (empty($keys)) { 195 return $meta; 196 } 197 foreach ($keys as $key => $value) { 198 $keys[$key] = trim($value); 199 } 200 201 foreach ($meta as $key => $value) { 202 if (in_array($key, $keys)) { 203 unset($meta[$key]); 204 } 205 } 206 207 return $meta; 208 } 209 210 public function the_content($content) { 211 $arr = wp_upload_dir(); 212 $baseurl = $arr['baseurl'] . '/writing-on-github'; 213 214 $content = preg_replace_callback( 215 '/(<img [^>]*?src=[\'"])\s*(\/images\/[^\s#]\S+)\s*([\'"][^>]*?>)/', 216 function($matchs) use ($baseurl) { 217 $url = $baseurl . $matchs[2]; 218 return "${matchs[1]}$url${matchs[3]}"; 219 }, 220 $content 221 ); 222 223 $content = preg_replace_callback( 224 '/(<a [^>]*?href=[\'"])\s*(\/images\/[^\s#]\S+)\s*([\'"][^>]*?>)/', 225 function($matchs) use ($baseurl) { 226 $url = $baseurl . $matchs[2]; 227 return "${matchs[1]}$url${matchs[3]}"; 228 }, 229 $content 230 ); 231 return $content; 232 } 233 234 /** 235 * Init i18n files 236 */ 237 public function l10n() { 238 load_plugin_textdomain( self::$text_domain ); 239 } 240 241 /** 242 * Sets and kicks off the export cronjob 243 */ 244 public function start_export( $force = false ) { 245 $this->start_cron( 'export', $force ); 246 } 247 248 /** 249 * Sets and kicks off the import cronjob 250 */ 251 public function start_import() { 252 $this->start_cron( 'import' ); 253 } 254 255 /** 256 * Enables the admin notice on initial activation 257 */ 258 public function activate() { 259 if ( 'yes' !== get_option( '_wogh_fully_exported' ) ) { 260 set_transient( '_wogh_activated', 'yes' ); 261 } 262 } 263 264 /** 265 * Displays the activation admin notice 266 */ 267 public function activation_notice() { 268 if ( ! get_transient( '_wogh_activated' ) ) { 269 return; 270 } 271 272 delete_transient( '_wogh_activated' ); 273 274 ?><div class="updated"> 275 <p> 276 <?php 277 printf( 278 __( 'To set up your site to sync with GitHub, update your <a href="%s">settings</a> and click "Export to GitHub."', 'writing-on-github' ), 279 admin_url( 'options-general.php?page=' . static::$text_domain) 280 ); 281 ?> 282 </p> 283 </div><?php 284 } 285 286 /** 287 * Get the Controller object. 288 * 289 * @return Writing_On_GitHub_Controller 290 */ 291 public function controller() { 292 return $this->controller; 293 } 294 295 /** 296 * Lazy-load the CLI object. 297 * 298 * @return Writing_On_GitHub_CLI 299 */ 300 public function cli() { 301 if ( ! $this->cli ) { 302 $this->cli = new Writing_On_GitHub_CLI; 303 } 304 305 return $this->cli; 306 } 307 308 /** 309 * Lazy-load the Request object. 310 * 311 * @return Writing_On_GitHub_Request 312 */ 313 public function request() { 314 if ( ! $this->request ) { 315 $this->request = new Writing_On_GitHub_Request( $this ); 316 } 317 318 return $this->request; 319 } 320 321 /** 322 * Lazy-load the Response object. 323 * 324 * @return Writing_On_GitHub_Response 325 */ 326 public function response() { 327 if ( ! $this->response ) { 328 $this->response = new Writing_On_GitHub_Response( $this ); 329 } 330 331 return $this->response; 332 } 333 334 /** 335 * Lazy-load the Api object. 336 * 337 * @return Writing_On_GitHub_Api 338 */ 339 public function api() { 340 if ( ! $this->api ) { 341 $this->api = new Writing_On_GitHub_Api( $this ); 342 } 343 344 return $this->api; 345 } 346 347 /** 348 * Lazy-load the Import object. 349 * 350 * @return Writing_On_GitHub_Import 351 */ 352 public function import() { 353 if ( ! $this->import ) { 354 $this->import = new Writing_On_GitHub_Import( $this ); 355 } 356 357 return $this->import; 358 } 359 360 /** 361 * Lazy-load the Export object. 362 * 363 * @return Writing_On_GitHub_Export 364 */ 365 public function export() { 366 if ( ! $this->export ) { 367 $this->export = new Writing_On_GitHub_Export( $this ); 368 } 369 370 return $this->export; 371 } 372 373 /** 374 * Lazy-load the Semaphore object. 375 * 376 * @return Writing_On_GitHub_Semaphore 377 */ 378 public function semaphore() { 379 if ( ! $this->semaphore ) { 380 $this->semaphore = new Writing_On_GitHub_Semaphore; 381 } 382 383 return $this->semaphore; 384 } 385 386 /** 387 * Lazy-load the Database object. 388 * 389 * @return Writing_On_GitHub_Database 390 */ 391 public function database() { 392 if ( ! $this->database ) { 393 $this->database = new Writing_On_GitHub_Database( $this ); 394 } 395 396 return $this->database; 397 } 398 399 /** 400 * Lazy-load the Cache object. 401 * 402 * @return Writing_On_GitHub_Cache 403 */ 404 public function cache() { 405 if ( ! $this->cache ) { 406 $this->cache = new Writing_On_GitHub_Cache; 407 } 408 409 return $this->cache; 410 } 411 412 /** 413 * Print to WP_CLI if in CLI environment or 414 * write to debug.log if WP_DEBUG is enabled 415 * @source http://www.stumiller.me/sending-output-to-the-wordpress-debug-log/ 416 * 417 * @param mixed $msg 418 * @param string $write 419 */ 420 public static function write_log( $msg, $write = 'line' ) { 421 if ( defined( 'WP_CLI' ) && WP_CLI ) { 422 if ( is_array( $msg ) || is_object( $msg ) ) { 423 WP_CLI::print_value( $msg ); 424 } else { 425 WP_CLI::$write( $msg ); 426 } 427 } elseif ( true === WP_DEBUG ) { 428 if ( is_array( $msg ) || is_object( $msg ) ) { 429 error_log( print_r( $msg, true ) ); 430 } else { 431 error_log( $msg ); 432 } 433 } 434 } 435 436 /** 437 * Kicks of an import or export cronjob. 438 * 135 add_action( 'wogh_import', array( $this->controller, 'import_master' ), 10, 1 ); 136 add_filter( 'get_edit_post_link', array( $this, 'edit_post_link' ), 10, 3 ); 137 138 // add_filter( 'wogh_post_meta', array( $this, 'ignore_post_meta' ), 10, 1 ); 139 // add_filter( 'wogh_pre_import_meta', array( $this, 'ignore_post_meta' ), 10, 1 ); 140 add_filter( 'the_content', array( $this, 'the_content' ) ); 141 142 do_action( 'wogh_boot', $this ); 143 } 144 145 public function edit_post_link($link, $postID, $context) { 146 if ( ! wp_is_post_revision( $postID ) ) { 147 $post = new Writing_On_GitHub_Post( $postID, Writing_On_GitHub::$instance->api() ); 148 if ( $post->is_on_github() ) { 149 return $post->github_edit_url(); 150 } 151 } 152 153 return $link; 154 } 155 156 public function ignore_post_meta($meta) { 157 $ignore_meta_keys = get_option('wogh_ignore_metas'); 158 if (empty($ignore_meta_keys)) { 159 return $meta; 160 } 161 162 $keys = preg_split("/\\r\\n|\\r|\\n/", $ignore_meta_keys); 163 if (empty($keys)) { 164 return $meta; 165 } 166 foreach ($keys as $key => $value) { 167 $keys[$key] = trim($value); 168 } 169 170 foreach ($meta as $key => $value) { 171 if (in_array($key, $keys)) { 172 unset($meta[$key]); 173 } 174 } 175 176 return $meta; 177 } 178 179 public function the_content($content) { 180 $arr = wp_upload_dir(); 181 $baseurl = $arr['baseurl'] . '/writing-on-github'; 182 183 $content = preg_replace_callback( 184 '/(<img [^>]*?src=[\'"])\s*(\/images\/[^\s#]\S+)\s*([\'"][^>]*?>)/', 185 function($matchs) use ($baseurl) { 186 $url = $baseurl . $matchs[2]; 187 return "${matchs[1]}$url${matchs[3]}"; 188 }, 189 $content 190 ); 191 192 $content = preg_replace_callback( 193 '/(<a [^>]*?href=[\'"])\s*(\/images\/[^\s#]\S+)\s*([\'"][^>]*?>)/', 194 function($matchs) use ($baseurl) { 195 $url = $baseurl . $matchs[2]; 196 return "${matchs[1]}$url${matchs[3]}"; 197 }, 198 $content 199 ); 200 return $content; 201 } 202 203 /** 204 * Init i18n files 205 */ 206 public function l10n() { 207 load_plugin_textdomain( self::$text_domain ); 208 } 209 210 /** 211 * Sets and kicks off the export cronjob 212 */ 213 public function start_export( $force = false ) { 214 $this->start_cron( 'export', $force ); 215 } 216 217 /** 218 * Sets and kicks off the import cronjob 219 */ 220 public function start_import() { 221 $this->start_cron( 'import' ); 222 } 223 224 /** 225 * Enables the admin notice on initial activation 226 */ 227 public function activate() { 228 if ( 'yes' !== get_option( '_wogh_fully_exported' ) ) { 229 set_transient( '_wogh_activated', 'yes' ); 230 } 231 } 232 233 /** 234 * Displays the activation admin notice 235 */ 236 public function activation_notice() { 237 if ( ! get_transient( '_wogh_activated' ) ) { 238 return; 239 } 240 241 delete_transient( '_wogh_activated' ); 242 243 ?><div class="updated"> 244 <p> 245 <?php 246 printf( 247 __( 'To set up your site to sync with GitHub, update your <a href="%s">settings</a> and click "Export to GitHub."', 'writing-on-github' ), 248 admin_url( 'options-general.php?page=' . static::$text_domain) 249 ); 250 ?> 251 </p> 252 </div><?php 253 } 254 255 /** 256 * Get the Controller object. 257 * 258 * @return Writing_On_GitHub_Controller 259 */ 260 public function controller() { 261 return $this->controller; 262 } 263 264 /** 265 * Lazy-load the CLI object. 266 * 267 * @return Writing_On_GitHub_CLI 268 */ 269 public function cli() { 270 if ( ! $this->cli ) { 271 $this->cli = new Writing_On_GitHub_CLI; 272 } 273 274 return $this->cli; 275 } 276 277 /** 278 * Lazy-load the Request object. 279 * 280 * @return Writing_On_GitHub_Request 281 */ 282 public function request() { 283 if ( ! $this->request ) { 284 $this->request = new Writing_On_GitHub_Request( $this ); 285 } 286 287 return $this->request; 288 } 289 290 /** 291 * Lazy-load the Response object. 292 * 293 * @return Writing_On_GitHub_Response 294 */ 295 public function response() { 296 if ( ! $this->response ) { 297 $this->response = new Writing_On_GitHub_Response( $this ); 298 } 299 300 return $this->response; 301 } 302 303 /** 304 * Lazy-load the Api object. 305 * 306 * @return Writing_On_GitHub_Api 307 */ 308 public function api() { 309 if ( ! $this->api ) { 310 $this->api = new Writing_On_GitHub_Api( $this ); 311 } 312 313 return $this->api; 314 } 315 316 /** 317 * Lazy-load the Import object. 318 * 319 * @return Writing_On_GitHub_Import 320 */ 321 public function import() { 322 if ( ! $this->import ) { 323 $this->import = new Writing_On_GitHub_Import( $this ); 324 } 325 326 return $this->import; 327 } 328 329 /** 330 * Lazy-load the Export object. 331 * 332 * @return Writing_On_GitHub_Export 333 */ 334 public function export() { 335 if ( ! $this->export ) { 336 $this->export = new Writing_On_GitHub_Export( $this ); 337 } 338 339 return $this->export; 340 } 341 342 /** 343 * Lazy-load the Semaphore object. 344 * 345 * @return Writing_On_GitHub_Semaphore 346 */ 347 public function semaphore() { 348 if ( ! $this->semaphore ) { 349 $this->semaphore = new Writing_On_GitHub_Semaphore; 350 } 351 352 return $this->semaphore; 353 } 354 355 /** 356 * Lazy-load the Database object. 357 * 358 * @return Writing_On_GitHub_Database 359 */ 360 public function database() { 361 if ( ! $this->database ) { 362 $this->database = new Writing_On_GitHub_Database( $this ); 363 } 364 365 return $this->database; 366 } 367 368 /** 369 * Print to WP_CLI if in CLI environment or 370 * write to debug.log if WP_DEBUG is enabled 371 * @source http://www.stumiller.me/sending-output-to-the-wordpress-debug-log/ 372 * 373 * @param mixed $msg 374 * @param string $write 375 */ 376 public static function write_log( $msg, $write = 'line' ) { 377 if ( defined( 'WP_CLI' ) && WP_CLI ) { 378 if ( is_array( $msg ) || is_object( $msg ) ) { 379 WP_CLI::print_value( $msg ); 380 } else { 381 WP_CLI::$write( $msg ); 382 } 383 } elseif ( true === WP_DEBUG ) { 384 if ( is_array( $msg ) || is_object( $msg ) ) { 385 error_log( print_r( $msg, true ) ); 386 } else { 387 error_log( $msg ); 388 } 389 } 390 } 391 392 /** 393 * Kicks of an import or export cronjob. 394 * 439 395 * @param bool $force 440 396 * @param string $type 441 397 */ 442 443 444 445 446 447 398 protected function start_cron( $type, $force = false ) { 399 update_option( '_wogh_' . $type . '_started', 'yes' ); 400 $user_id = get_current_user_id(); 401 wp_schedule_single_event( time(), 'wogh_' . $type . '', array( $user_id, $force ) ); 402 spawn_cron(); 403 } 448 404 }
Note: See TracChangeset
for help on using the changeset viewer.