The WP Remote WordPress Plugin - Version 2.7.0

Version Description

Download this release

Release Info

Developer danielbachhuber
Plugin Icon 128x128 The WP Remote WordPress Plugin
Version 2.7.0
Comparing to
See all releases

Code changes from version 2.6.7 to 2.7.0

Files changed (9) hide show
  1. .travis.yml +2 -2
  2. plugin.php +32 -3
  3. readme.txt +9 -2
  4. wprp.api.php +357 -18
  5. wprp.backups.php +222 -43
  6. wprp.hm.backup.php +540 -15
  7. wprp.integration.php +16 -0
  8. wprp.plugins.php +107 -23
  9. wprp.themes.php +27 -15
.travis.yml CHANGED
@@ -18,10 +18,10 @@ env:
18
  - WP_VERSION=3.2 WP_MULTISITE=1
19
 
20
  notifications:
21
- hipchat: dd6fd66a04b2e8e8c8b2b1fc47f081@WP Remote
22
 
23
  before_script:
24
  - export WP_TESTS_DIR=/tmp/wordpress-tests/
25
- - bash bin/install-wp-tests.sh wordpress_test root '' $WP_VERSION
26
 
27
  script: phpunit
18
  - WP_VERSION=3.2 WP_MULTISITE=1
19
 
20
  notifications:
21
+ secure: "gwybVEhn3tYVngWMegtYJ0dfSBjLa1+0LsD9LXypHtyMjkUBuoEu0NWkupkp4HA27Euq5Cryg01vhWhy7+8kUIFeSnkYDqcvIHduPvyYqSwaZFNEgINZ/2OiQOomg23C+/sYxqzmXeFiRChHvR26/9FfhLSDqvGPZ4/n/URRFgg="
22
 
23
  before_script:
24
  - export WP_TESTS_DIR=/tmp/wordpress-tests/
25
+ - bash bin/install-wp-tests.sh wordpress_test root '' $WP_VERSION
26
 
27
  script: phpunit
plugin.php CHANGED
@@ -3,7 +3,7 @@
3
  /*
4
  Plugin Name: WP Remote
5
  Description: Manage your WordPress site with <a href="https://wpremote.com/">WP Remote</a>. <strong>Deactivate to clear your API Key.</strong>
6
- Version: 2.6.7
7
  Author: Human Made Limited
8
  Author URI: http://hmn.md/
9
  */
@@ -88,6 +88,7 @@ function wprp_catch_api_call() {
88
  if ( empty( $_POST['wpr_verify_key'] ) )
89
  return;
90
 
 
91
  require_once( WPRP_PLUGIN_PATH . '/wprp.plugins.php' );
92
  require_once( WPRP_PLUGIN_PATH . '/wprp.themes.php' );
93
 
@@ -96,7 +97,32 @@ function wprp_catch_api_call() {
96
  exit;
97
 
98
  }
99
- add_action( 'init', 'wprp_catch_api_call', 1 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
  /**
102
  * Get the stored WPR API key
@@ -162,6 +188,9 @@ function wprp_update() {
162
 
163
  function _wprp_upgrade_core() {
164
 
 
 
 
165
  include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
166
  include_once ( ABSPATH . 'wp-admin/includes/upgrade.php' );
167
  include_once ( ABSPATH . 'wp-includes/update.php' );
@@ -170,7 +199,7 @@ function _wprp_upgrade_core() {
170
 
171
  // check for filesystem access
172
  if ( ! _wpr_check_filesystem_access() )
173
- return array( 'status' => 'error', 'error' => __( 'The filesystem is not writable with the supplied credentials', 'wpremote' ) );
174
 
175
  // force refresh
176
  wp_version_check();
3
  /*
4
  Plugin Name: WP Remote
5
  Description: Manage your WordPress site with <a href="https://wpremote.com/">WP Remote</a>. <strong>Deactivate to clear your API Key.</strong>
6
+ Version: 2.7.0
7
  Author: Human Made Limited
8
  Author URI: http://hmn.md/
9
  */
88
  if ( empty( $_POST['wpr_verify_key'] ) )
89
  return;
90
 
91
+ require_once( WPRP_PLUGIN_PATH . '/wprp.integration.php' );
92
  require_once( WPRP_PLUGIN_PATH . '/wprp.plugins.php' );
93
  require_once( WPRP_PLUGIN_PATH . '/wprp.themes.php' );
94
 
97
  exit;
98
 
99
  }
100
+ add_action( 'init', 'wprp_catch_api_call', 100 );
101
+
102
+
103
+ /**
104
+ * Check for a bat signal from the mothership
105
+ *
106
+ * @since 2.7.0
107
+ */
108
+ function wprp_check_bat_signal() {
109
+
110
+ $bat_signal_key = 'wprp_bat_signal';
111
+
112
+ if ( false === get_transient( $bat_signal_key ) ) {
113
+
114
+ $bat_signal_url = trailingslashit( WPR_URL ) . 'bat-signal/';
115
+ $response = wp_remote_get( $bat_signal_url );
116
+ $response_body = wp_remote_retrieve_body( $response );
117
+ if ( 'destroy the evidence!' == trim( $response_body ) )
118
+ delete_option( 'wpr_api_key' );
119
+
120
+ // One request per day
121
+ set_transient( $bat_signal_key, 'the coast is clear', 60 * 60 * 24 );
122
+ }
123
+
124
+ }
125
+ add_action( 'init', 'wprp_check_bat_signal' );
126
 
127
  /**
128
  * Get the stored WPR API key
188
 
189
  function _wprp_upgrade_core() {
190
 
191
+ if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
192
+ return new WP_Error( 'disallow-file-mods', __( "File modification is disabled with the DISALLOW_FILE_MODS constant.", 'wpremote' ) );
193
+
194
  include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
195
  include_once ( ABSPATH . 'wp-admin/includes/upgrade.php' );
196
  include_once ( ABSPATH . 'wp-includes/update.php' );
199
 
200
  // check for filesystem access
201
  if ( ! _wpr_check_filesystem_access() )
202
+ return new WP_Error( 'filesystem-not-writable', __( 'The filesystem is not writable with the supplied credentials', 'wpremote' ) );
203
 
204
  // force refresh
205
  wp_version_check();
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: humanmade, willmot, joehoyle, danielbachhuber, mattheu, pauldewouters, cuvelier, tcrsavage
3
  Tags: wpremote, remote administration, multiple wordpress
4
  Requires at least: 3.0
5
- Tested up to: 3.7
6
- Stable tag: 2.6.7
7
 
8
  WP Remote is a free web app that enables you to easily manage all of your WordPress powered sites from one place.
9
 
@@ -37,6 +37,13 @@ You can email us at support@wpremote.com for support.
37
 
38
  == Changelog ==
39
 
 
 
 
 
 
 
 
40
  #### 2.6.7 (27 October 2013)
41
 
42
  * API improvement: specify database- and file-only backups
2
  Contributors: humanmade, willmot, joehoyle, danielbachhuber, mattheu, pauldewouters, cuvelier, tcrsavage
3
  Tags: wpremote, remote administration, multiple wordpress
4
  Requires at least: 3.0
5
+ Tested up to: 3.7.1
6
+ Stable tag: 2.7.0
7
 
8
  WP Remote is a free web app that enables you to easily manage all of your WordPress powered sites from one place.
9
 
37
 
38
  == Changelog ==
39
 
40
+ #### 2.7.0 (19 November 2013)
41
+
42
+ * Improved durability of backups where the backup process can take more than 90 seconds.
43
+ * New API support for posts, comments, and fixed support for users (oops).
44
+ * Reporting and update integration with premium plugins that support ManageWP's API implementation.
45
+ * Plugin, theme, and core updates now respect the `DISALLOW_FILE_MODS` constant.
46
+
47
  #### 2.6.7 (27 October 2013)
48
 
49
  * API improvement: specify database- and file-only backups
wprp.api.php CHANGED
@@ -65,7 +65,7 @@ class WPR_API_Request {
65
  }
66
 
67
  static function get_arg( $arg ) {
68
- return ( isset( self::$args[$arg] ) ) ? self::$args[$arg] : '';
69
  }
70
  }
71
 
@@ -79,6 +79,10 @@ if ( class_exists( 'WPRP_Log' ) )
79
  if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG )
80
  error_reporting( 0 );
81
 
 
 
 
 
82
  // Log in as admin
83
  // TODO what about if admin use doesn't exists?
84
  wp_set_current_user( 1 );
@@ -129,6 +133,23 @@ foreach( WPR_API_Request::get_actions() as $action ) {
129
 
130
  break;
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  case 'upgrade_core' :
133
 
134
  $actions[$action] = _wprp_upgrade_core();
@@ -144,34 +165,37 @@ foreach( WPR_API_Request::get_actions() as $action ) {
144
  case 'update_plugin' :
145
  case 'upgrade_plugin' :
146
 
147
- $actions[$action] = _wprp_update_plugin( (string) sanitize_text_field( WPR_API_Request::get_arg( 'plugin' ) ) );
 
 
 
148
 
149
  break;
150
 
151
  case 'install_plugin' :
152
 
153
  $api_args = array(
154
- 'version' => sanitize_text_field( (string)WPR_API_Request::get_arg( 'version' ) ),
155
  );
156
- $actions[$action] = _wprp_install_plugin( (string) sanitize_text_field( WPR_API_Request::get_arg( 'plugin' ) ), $api_args );
157
 
158
  break;
159
 
160
  case 'activate_plugin' :
161
 
162
- $actions[$action] = _wprp_activate_plugin( (string) sanitize_text_field( WPR_API_Request::get_arg( 'plugin' ) ) );
163
 
164
  break;
165
 
166
  case 'deactivate_plugin' :
167
 
168
- $actions[$action] = _wprp_deactivate_plugin( (string) sanitize_text_field( WPR_API_Request::get_arg( 'plugin' ) ) );
169
 
170
  break;
171
 
172
  case 'uninstall_plugin' :
173
 
174
- $actions[$action] = _wprp_uninstall_plugin( (string) sanitize_text_field( WPR_API_Request::get_arg( 'plugin' ) ) );
175
 
176
  break;
177
 
@@ -184,28 +208,28 @@ foreach( WPR_API_Request::get_actions() as $action ) {
184
  case 'install_theme':
185
 
186
  $api_args = array(
187
- 'version' => sanitize_text_field( (string)WPR_API_Request::get_arg( 'version' ) ),
188
  );
189
- $actions[$action] = _wprp_install_theme( (string) sanitize_text_field( WPR_API_Request::get_arg( 'theme' ) ), $api_args );
190
 
191
  break;
192
 
193
  case 'activate_theme':
194
 
195
- $actions[$action] = _wprp_activate_theme( (string) sanitize_text_field( WPR_API_Request::get_arg( 'theme' ) ), $api_args );
196
 
197
  break;
198
 
199
  case 'update_theme' :
200
  case 'upgrade_theme' : // 'upgrade' is deprecated
201
 
202
- $actions[$action] = _wprp_update_theme( (string) sanitize_text_field( WPR_API_Request::get_arg( 'theme' ) ) );
203
 
204
  break;
205
 
206
  case 'delete_theme':
207
 
208
- $actions[$action] = _wprp_delete_theme( (string) sanitize_text_field( WPR_API_Request::get_arg( 'theme' ) ) );
209
 
210
  break;
211
 
@@ -214,6 +238,9 @@ foreach( WPR_API_Request::get_actions() as $action ) {
214
  if ( in_array( WPR_API_Request::get_arg( 'backup_type' ), array( 'complete', 'database', 'file' ) ) )
215
  WPRP_Backups::get_instance()->set_type( WPR_API_Request::get_arg( 'backup_type' ) );
216
 
 
 
 
217
  $actions[$action] = WPRP_Backups::get_instance()->do_backup();
218
 
219
  break;
@@ -230,6 +257,17 @@ foreach( WPR_API_Request::get_actions() as $action ) {
230
 
231
  break;
232
 
 
 
 
 
 
 
 
 
 
 
 
233
  case 'supports_backups' :
234
 
235
  $actions[$action] = true;
@@ -242,7 +280,8 @@ foreach( WPR_API_Request::get_actions() as $action ) {
242
  'site_url' => get_site_url(),
243
  'home_url' => get_home_url(),
244
  'admin_url' => get_admin_url(),
245
- 'backups' => function_exists( '_wprp_get_backups_info' ) ? _wprp_get_backups_info() : array()
 
246
  );
247
 
248
  break;
@@ -265,6 +304,256 @@ foreach( WPR_API_Request::get_actions() as $action ) {
265
 
266
  break;
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  case 'get_users':
269
 
270
  $arg_keys = array(
@@ -288,6 +577,58 @@ foreach( WPR_API_Request::get_actions() as $action ) {
288
 
289
  break;
290
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  case 'create_user':
292
 
293
  $args = array(
@@ -301,14 +642,12 @@ foreach( WPR_API_Request::get_actions() as $action ) {
301
  );
302
  foreach( $args as $key => $value ) {
303
  // Note: wp_insert_user() handles sanitization / validation
304
- if ( $new_value = WPR_API_Request::get_arg( $key ) )
305
  $args[$key] = $new_value;
306
  }
307
 
308
  if ( ! $args['user_pass'] ) {
309
- $args['user_pass'] = $generated_password = wp_generate_password();
310
- } else {
311
- $generated_password = false;
312
  }
313
 
314
  $user_id = wp_insert_user( $args );
@@ -316,7 +655,7 @@ foreach( WPR_API_Request::get_actions() as $action ) {
316
  if ( is_wp_error( $user_id ) ) {
317
  $actions[$action] = array( 'status' => 'error', 'error' => $user_id->get_error_message() );
318
  } else {
319
- $actions[$action] = new WP_Error( 'log-not-enabled', 'Logging is not enabled' );
320
  }
321
 
322
  break;
65
  }
66
 
67
  static function get_arg( $arg ) {
68
+ return ( isset( self::$args[$arg] ) ) ? self::$args[$arg] : null;
69
  }
70
  }
71
 
79
  if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG )
80
  error_reporting( 0 );
81
 
82
+ // Temp hack so our requests to verify file size are signed.
83
+ global $wprp_noauth_nonce;
84
+ $wprp_noauth_nonce = wp_create_nonce( 'wprp_calculate_backup_size' );
85
+
86
  // Log in as admin
87
  // TODO what about if admin use doesn't exists?
88
  wp_set_current_user( 1 );
133
 
134
  break;
135
 
136
+ case 'get_constants':
137
+
138
+ $constants = array();
139
+ if ( is_array( WPR_API_Request::get_arg( 'constants' ) ) ) {
140
+
141
+ foreach( WPR_API_Request::get_arg( 'constants' ) as $constant ) {
142
+ if ( defined( $constant ) )
143
+ $constants[$constant] = constant( $constant );
144
+ else
145
+ $constants[$constant] = null;
146
+ }
147
+
148
+ }
149
+ $actions[$action] = $constants;
150
+
151
+ break;
152
+
153
  case 'upgrade_core' :
154
 
155
  $actions[$action] = _wprp_upgrade_core();
165
  case 'update_plugin' :
166
  case 'upgrade_plugin' :
167
 
168
+ $api_args = array(
169
+ 'zip_url' => esc_url_raw( WPR_API_Request::get_arg( 'zip_url' ) ),
170
+ );
171
+ $actions[$action] = _wprp_update_plugin( sanitize_text_field( WPR_API_Request::get_arg( 'plugin' ) ), $api_args );
172
 
173
  break;
174
 
175
  case 'install_plugin' :
176
 
177
  $api_args = array(
178
+ 'version' => sanitize_text_field( WPR_API_Request::get_arg( 'version' ) ),
179
  );
180
+ $actions[$action] = _wprp_install_plugin( sanitize_text_field( WPR_API_Request::get_arg( 'plugin' ) ), $api_args );
181
 
182
  break;
183
 
184
  case 'activate_plugin' :
185
 
186
+ $actions[$action] = _wprp_activate_plugin( sanitize_text_field( WPR_API_Request::get_arg( 'plugin' ) ) );
187
 
188
  break;
189
 
190
  case 'deactivate_plugin' :
191
 
192
+ $actions[$action] = _wprp_deactivate_plugin( sanitize_text_field( WPR_API_Request::get_arg( 'plugin' ) ) );
193
 
194
  break;
195
 
196
  case 'uninstall_plugin' :
197
 
198
+ $actions[$action] = _wprp_uninstall_plugin( sanitize_text_field( WPR_API_Request::get_arg( 'plugin' ) ) );
199
 
200
  break;
201
 
208
  case 'install_theme':
209
 
210
  $api_args = array(
211
+ 'version' => sanitize_text_field( WPR_API_Request::get_arg( 'version' ) ),
212
  );
213
+ $actions[$action] = _wprp_install_theme( sanitize_text_field( WPR_API_Request::get_arg( 'theme' ) ), $api_args );
214
 
215
  break;
216
 
217
  case 'activate_theme':
218
 
219
+ $actions[$action] = _wprp_activate_theme( sanitize_text_field( WPR_API_Request::get_arg( 'theme' ) ) );
220
 
221
  break;
222
 
223
  case 'update_theme' :
224
  case 'upgrade_theme' : // 'upgrade' is deprecated
225
 
226
+ $actions[$action] = _wprp_update_theme( sanitize_text_field( WPR_API_Request::get_arg( 'theme' ) ) );
227
 
228
  break;
229
 
230
  case 'delete_theme':
231
 
232
+ $actions[$action] = _wprp_delete_theme( sanitize_text_field( WPR_API_Request::get_arg( 'theme' ) ) );
233
 
234
  break;
235
 
238
  if ( in_array( WPR_API_Request::get_arg( 'backup_type' ), array( 'complete', 'database', 'file' ) ) )
239
  WPRP_Backups::get_instance()->set_type( WPR_API_Request::get_arg( 'backup_type' ) );
240
 
241
+ if ( WPR_API_Request::get_arg( 'backup_approach' ) && 'file_manifest' == WPR_API_Request::get_arg( 'backup_approach' ) )
242
+ WPRP_Backups::get_instance()->set_is_using_file_manifest( true );
243
+
244
  $actions[$action] = WPRP_Backups::get_instance()->do_backup();
245
 
246
  break;
257
 
258
  break;
259
 
260
+ case 'backup_heartbeat' :
261
+
262
+ WPRP_Backups::get_instance()->set_is_using_file_manifest( true );
263
+
264
+ if ( in_array( WPR_API_Request::get_arg( 'backup_type' ), array( 'complete', 'database', 'file' ) ) )
265
+ WPRP_Backups::get_instance()->set_type( WPR_API_Request::get_arg( 'backup_type' ) );
266
+
267
+ $actions[$action] = WPRP_Backups::get_instance()->backup_heartbeat();
268
+
269
+ break;
270
+
271
  case 'supports_backups' :
272
 
273
  $actions[$action] = true;
280
  'site_url' => get_site_url(),
281
  'home_url' => get_home_url(),
282
  'admin_url' => get_admin_url(),
283
+ 'backups' => function_exists( '_wprp_get_backups_info' ) ? _wprp_get_backups_info() : array(),
284
+ 'web_host' => _wprp_integration_get_web_host(),
285
  );
286
 
287
  break;
304
 
305
  break;
306
 
307
+ case 'get_posts':
308
+
309
+ $arg_keys = array(
310
+ /** Author **/
311
+ 'author',
312
+ 'author_name',
313
+ 'author__in',
314
+ 'author__not_in',
315
+
316
+ /** Category **/
317
+ 'cat',
318
+ 'category_name',
319
+ 'category__and',
320
+ 'category__in',
321
+ 'category__not_in',
322
+
323
+ /** Tag **/
324
+ 'tag',
325
+ 'tag_id',
326
+ 'tag__and',
327
+ 'tag__in',
328
+ 'tag__not_in',
329
+ 'tag_slug__and',
330
+ 'tag_slug__in',
331
+
332
+ /** Search **/
333
+ 's',
334
+
335
+ /** Post Attributes **/
336
+ 'name',
337
+ 'pagename',
338
+ 'post_parent',
339
+ 'post_parent__in',
340
+ 'post_parent__not_in',
341
+ 'post__in',
342
+ 'post__not_in',
343
+ 'post_status',
344
+ 'post_type',
345
+
346
+ /** Order / Pagination / Etc. **/
347
+ 'order',
348
+ 'orderby',
349
+ 'nopaging',
350
+ 'posts_per_page',
351
+ 'offset',
352
+ 'paged',
353
+ 'page',
354
+ 'ignore_sticky_posts',
355
+ );
356
+ $args = array();
357
+ foreach( $arg_keys as $arg_key ) {
358
+ // Note: WP_Query() supports validation / sanitization
359
+ if ( null !== ( $value = WPR_API_Request::get_arg( $arg_key ) ) )
360
+ $args[$arg_key] = $value;
361
+ }
362
+
363
+ $query = new WP_Query;
364
+ $query->query( $args );
365
+ $actions[$action] = $query->posts;
366
+
367
+ break;
368
+
369
+ case 'get_post':
370
+ case 'delete_post':
371
+
372
+ $post_id = (int)WPR_API_Request::get_arg( 'post_id' );
373
+ $post = get_post( $post_id );
374
+
375
+ if ( ! $post ) {
376
+ $actions[$action] = new WP_Error( 'missing-post', __( "No post found.", 'wpremote' ) );
377
+ break;
378
+ }
379
+
380
+ if ( 'get_post' == $action ) {
381
+
382
+ $actions[$action] = $post;
383
+
384
+ } else if ( 'delete_post' == $action ) {
385
+
386
+ $actions[$action] = wp_delete_post( $post_id );
387
+
388
+ }
389
+
390
+ break;
391
+
392
+ case 'create_post':
393
+ case 'update_post':
394
+
395
+ $arg_keys = array(
396
+ 'menu_order',
397
+ 'comment_status',
398
+ 'ping_status',
399
+ 'post_author',
400
+ 'post_content',
401
+ 'post_date',
402
+ 'post_date_gmt',
403
+ 'post_excerpt',
404
+ 'post_name',
405
+ 'post_parent',
406
+ 'post_password',
407
+ 'post_status',
408
+ 'post_title',
409
+ 'post_type',
410
+ 'tags_input',
411
+ );
412
+ $args = array();
413
+ foreach( $arg_keys as $arg_key ) {
414
+ // Note: wp_update_post() supports validation / sanitization
415
+ if ( null !== ( $value = WPR_API_Request::get_arg( $arg_key ) ) )
416
+ $args[$arg_key] = $value;
417
+ }
418
+
419
+ if ( 'create_post' == $action ) {
420
+
421
+ if ( $post_id = wp_insert_post( $args ) )
422
+ $actions[$action] = get_post( $post_id );
423
+ else
424
+ $actions[$action] = new WP_Error( 'create-post', __( "Error creating post.", 'wpremote' ) );
425
+
426
+ } else if ( 'update_post' == $action ) {
427
+
428
+ $args['ID'] = (int)WPR_API_Request::get_arg( 'post_id' );
429
+
430
+ if ( ! get_post( $args['ID'] ) ) {
431
+ $actions[$action] = new WP_Error( 'missing-post', __( "No post found.", 'wpremote' ) );
432
+ break;
433
+ }
434
+
435
+ if ( wp_update_post( $args ) )
436
+ $actions[$action] = get_post( $args['ID'] );
437
+ else
438
+ $actions[$action] = new WP_Error( 'update-post', __( "Error updating post.", 'wpremote' ) );
439
+
440
+ }
441
+
442
+ break;
443
+
444
+ case 'get_metadata':
445
+
446
+ $actions[$action] = get_metadata( WPR_API_Request::get_arg( 'meta_type' ), WPR_API_Request::get_arg( 'object_id' ), WPR_API_Request::get_arg( 'meta_key' ), false );
447
+
448
+ break;
449
+
450
+ case 'add_metadata':
451
+
452
+ $actions[$action] = add_metadata( WPR_API_Request::get_arg( 'meta_type' ), WPR_API_Request::get_arg( 'object_id' ), WPR_API_Request::get_arg( 'meta_key' ), WPR_API_Request::get_arg( 'meta_value' ) );
453
+
454
+ break;
455
+
456
+ case 'update_metadata':
457
+
458
+ $actions[$action] = update_metadata( WPR_API_Request::get_arg( 'meta_type' ), WPR_API_Request::get_arg( 'object_id' ), WPR_API_Request::get_arg( 'meta_key' ), WPR_API_Request::get_arg( 'meta_value' ) );
459
+
460
+ break;
461
+
462
+ case 'delete_metadata':
463
+
464
+ $actions[$action] = delete_metadata( WPR_API_Request::get_arg( 'meta_type' ), WPR_API_Request::get_arg( 'object_id' ), WPR_API_Request::get_arg( 'meta_key' ) );
465
+
466
+ break;
467
+
468
+ case 'get_comments':
469
+
470
+ $arg_keys = array(
471
+ 'status',
472
+ 'orderby',
473
+ 'order',
474
+ 'post_id',
475
+ );
476
+ $args = array();
477
+ foreach( $arg_keys as $arg_key ) {
478
+ // Note: get_comments() supports validation / sanitization
479
+ if ( null !== ( $value = WPR_API_Request::get_arg( $arg_key ) ) )
480
+ $args[$arg_key] = $value;
481
+ }
482
+ $actions[$action] = get_comments( $args );
483
+
484
+ break;
485
+
486
+ case 'get_comment':
487
+ case 'delete_comment':
488
+
489
+ $comment_id = (int)WPR_API_Request::get_arg( 'comment_id' );
490
+ $comment = get_comment( $comment_id );
491
+
492
+ if ( ! $comment ) {
493
+ $actions[$action] = new WP_Error( 'missing-comment', __( "No comment found.", 'wpremote' ) );
494
+ break;
495
+ }
496
+
497
+ if ( 'get_comment' == $action ) {
498
+
499
+ $actions[$action] = $comment;
500
+
501
+ } else if ( 'delete_comment' == $action ) {
502
+
503
+ $actions[$action] = wp_delete_comment( $comment_id );
504
+
505
+ }
506
+
507
+ break;
508
+
509
+ case 'create_comment':
510
+ case 'update_comment':
511
+
512
+ $arg_keys = array(
513
+ 'comment_post_ID',
514
+ 'comment_author',
515
+ 'comment_author_email',
516
+ 'comment_author_url',
517
+ 'comment_date',
518
+ 'comment_date_gmt',
519
+ 'comment_content',
520
+ 'comment_approved',
521
+ 'comment_type',
522
+ 'comment_parent',
523
+ 'user_id'
524
+ );
525
+ $args = array();
526
+ foreach( $arg_keys as $arg_key ) {
527
+ // Note: wp_update_comment() supports validation / sanitization
528
+ if ( null !== ( $value = WPR_API_Request::get_arg( $arg_key ) ) )
529
+ $args[$arg_key] = $value;
530
+ }
531
+
532
+ if ( 'create_comment' == $action ) {
533
+
534
+ if ( $comment_id = wp_insert_comment( $args ) )
535
+ $actions[$action] = get_comment( $comment_id );
536
+ else
537
+ $actions[$action] = new WP_Error( 'create-comment', __( "Error creating comment.", 'wpremote' ) );
538
+
539
+ } else if ( 'update_comment' == $action ) {
540
+
541
+ $args['comment_ID'] = (int)WPR_API_Request::get_arg( 'comment_id' );
542
+
543
+ if ( ! get_comment( $args['comment_ID'] ) ) {
544
+ $actions[$action] = new WP_Error( 'missing-comment', __( "No comment found.", 'wpremote' ) );
545
+ break;
546
+ }
547
+
548
+ if ( wp_update_comment( $args ) )
549
+ $actions[$action] = get_comment( $args['comment_ID'] );
550
+ else
551
+ $actions[$action] = new WP_Error( 'update-comment', __( "Error updating comment.", 'wpremote' ) );
552
+
553
+ }
554
+
555
+ break;
556
+
557
  case 'get_users':
558
 
559
  $arg_keys = array(
577
 
578
  break;
579
 
580
+ case 'get_user':
581
+ case 'update_user':
582
+ case 'delete_user':
583
+
584
+ $user_id = (int)WPR_API_Request::get_arg( 'user_id' );
585
+ $user = get_user_by( 'id', $user_id );
586
+
587
+ if ( ! $user ) {
588
+ $actions[$action] = new WP_Error( 'missing-user', "No user found." );
589
+ break;
590
+ }
591
+
592
+ require_once ABSPATH . '/wp-admin/includes/user.php';
593
+
594
+ if ( 'get_user' == $action ) {
595
+
596
+ $actions[$action] = wprp_format_user_obj( $user );
597
+
598
+ } else if ( 'update_user' == $action ) {
599
+
600
+ $fields = array(
601
+ 'user_email',
602
+ 'display_name',
603
+ 'first_name',
604
+ 'last_name',
605
+ 'user_nicename',
606
+ 'user_pass',
607
+ 'user_url',
608
+ 'description'
609
+ );
610
+ $args = array();
611
+ foreach( $fields as $field ) {
612
+ // Note: wp_update_user() handles sanitization / validation
613
+ if ( null !== ( $value = WPR_API_Request::get_arg( $field ) ) )
614
+ $args[$field] = $value;
615
+ }
616
+ $args['ID'] = $user->ID;
617
+ $ret = wp_update_user( $args );
618
+ if ( is_wp_error( $ret ) )
619
+ $actions[$action] = $ret;
620
+ else
621
+ $actions[$action] = wprp_format_user_obj( get_user_by( 'id', $ret ) );
622
+
623
+ } else if ( 'delete_user' == $action ) {
624
+
625
+ $actions[$action] = wp_delete_user( $user->ID );
626
+
627
+ }
628
+
629
+
630
+ break;
631
+
632
  case 'create_user':
633
 
634
  $args = array(
642
  );
643
  foreach( $args as $key => $value ) {
644
  // Note: wp_insert_user() handles sanitization / validation
645
+ if ( null !== ( $new_value = WPR_API_Request::get_arg( $key ) ) )
646
  $args[$key] = $new_value;
647
  }
648
 
649
  if ( ! $args['user_pass'] ) {
650
+ $args['user_pass'] = wp_generate_password();
 
 
651
  }
652
 
653
  $user_id = wp_insert_user( $args );
655
  if ( is_wp_error( $user_id ) ) {
656
  $actions[$action] = array( 'status' => 'error', 'error' => $user_id->get_error_message() );
657
  } else {
658
+ $actions[$action] = wprp_format_user_obj( get_user_by( 'id', $user_id ) );
659
  }
660
 
661
  break;
wprp.backups.php CHANGED
@@ -23,41 +23,13 @@ class WPRP_Backups extends WPRP_HM_Backup {
23
  */
24
  public static function get_instance() {
25
 
26
- if ( empty( self::$instance ) )
27
  self::$instance = new WPRP_Backups();
28
-
29
- return self::$instance;
30
-
31
- }
32
-
33
- /**
34
- * Recursively delete a directory including
35
- * all the files and sub-directories.
36
- *
37
- * @param string $dir
38
- * @return bool
39
- */
40
- public static function rmdir_recursive( $dir ) {
41
-
42
- if ( is_file( $dir ) )
43
- @unlink( $dir );
44
-
45
- if ( ! is_dir( $dir ) )
46
- return false;
47
-
48
- $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ), RecursiveIteratorIterator::CHILD_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD );
49
-
50
- foreach ( $files as $file ) {
51
-
52
- if ( $file->isDir() )
53
- @rmdir( $file->getPathname() );
54
-
55
- else
56
- @unlink( $file->getPathname() );
57
 
58
  }
59
 
60
- @rmdir( $dir );
61
 
62
  }
63
 
@@ -74,9 +46,12 @@ class WPRP_Backups extends WPRP_HM_Backup {
74
 
75
  // Set the excludes
76
  if ( class_exists( 'WPR_API_Request' ) && WPR_API_Request::get_arg( 'backup_excludes' ) )
77
- $this->set_excludes( WPR_API_Request::get_arg( 'backup_excludes' ) );
78
  else if ( isset( $_GET['backup_excludes'] ) )
79
- $this->set_excludes( $_GET['backup_excludes'] );
 
 
 
80
 
81
  $this->filesize_transient = 'wprp_' . '_' . $this->get_type() . '_' . substr( md5( $this->exclude_string() ), 20 ) . '_filesize';
82
 
@@ -92,10 +67,19 @@ class WPRP_Backups extends WPRP_HM_Backup {
92
 
93
  $this->set_status( 'Starting backup...' );
94
 
 
 
95
  $this->backup();
96
 
97
- if ( ! file_exists( $this->get_archive_filepath() ) )
98
- return new WP_Error( 'backup-failed', implode( ', ', $this->get_errors() ) );
 
 
 
 
 
 
 
99
 
100
  return true;
101
 
@@ -110,11 +94,18 @@ class WPRP_Backups extends WPRP_HM_Backup {
110
 
111
  global $is_apache;
112
 
113
- if ( $status = $this->get_status() )
114
- return new WP_Error( 'error-status', $status );
115
 
116
- $backup = glob( $this->get_path() . '/*.zip' );
117
- $backup = reset( $backup );
 
 
 
 
 
 
 
118
 
119
  if ( file_exists( $backup ) ) {
120
 
@@ -131,7 +122,10 @@ class WPRP_Backups extends WPRP_HM_Backup {
131
 
132
  }
133
 
134
- return str_replace( parent::conform_dir( WP_CONTENT_DIR ), WP_CONTENT_URL, $backup );
 
 
 
135
 
136
  }
137
 
@@ -153,6 +147,17 @@ class WPRP_Backups extends WPRP_HM_Backup {
153
 
154
  }
155
 
 
 
 
 
 
 
 
 
 
 
 
156
  /**
157
  * Get the estimated size of the sites files and database
158
  *
@@ -179,7 +184,8 @@ class WPRP_Backups extends WPRP_HM_Backup {
179
 
180
  // we dont know the size yet, fire off a remote request to get it for later
181
  // it can take some time so we have a small timeout then return "Calculating"
182
- wp_remote_get( add_query_arg( array( 'action' => 'wprp_calculate_backup_size', 'backup_excludes' => $this->get_excludes() ), admin_url( 'admin-ajax.php' ) ), array( 'timeout' => 0.1, 'sslverify' => false ) );
 
183
 
184
  return __( 'Calculating', 'wpremote' );
185
 
@@ -192,8 +198,16 @@ class WPRP_Backups extends WPRP_HM_Backup {
192
  */
193
  protected function do_action( $action ) {
194
 
 
 
195
  switch ( $action ) :
196
 
 
 
 
 
 
 
197
  case 'hmbkp_mysqldump_started' :
198
 
199
  $this->set_status( sprintf( __( 'Dumping Database %s', 'wpremote' ), '(<code>' . $this->get_mysqldump_method() . '</code>)' ) );
@@ -208,7 +222,12 @@ class WPRP_Backups extends WPRP_HM_Backup {
208
 
209
  case 'hmbkp_archive_started' :
210
 
211
- $this->set_status( sprintf( __( 'Creating zip archive %s', 'wpremote' ), '(<code>' . $this->get_archive_method() . '</code>)' ) );
 
 
 
 
 
212
 
213
  break;
214
 
@@ -223,6 +242,8 @@ class WPRP_Backups extends WPRP_HM_Backup {
223
  if ( file_exists( $this->get_schedule_running_path() ) )
224
  unlink( $this->get_schedule_running_path() );
225
 
 
 
226
  break;
227
 
228
  case 'hmbkp_error' :
@@ -414,6 +435,161 @@ class WPRP_Backups extends WPRP_HM_Backup {
414
 
415
  }
416
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  /**
418
  * Calculate the size of the backup
419
  *
@@ -475,7 +651,7 @@ class WPRP_Backups extends WPRP_HM_Backup {
475
 
476
  }
477
 
478
- /**
479
  * Return an array of back meta information
480
  *
481
  * @return array
@@ -499,6 +675,9 @@ function _wprp_get_backups_info() {
499
  */
500
  function wprp_ajax_calculate_backup_size() {
501
 
 
 
 
502
  WPRP_Backups::get_instance()->get_filesize();
503
 
504
  exit;
23
  */
24
  public static function get_instance() {
25
 
26
+ if ( empty( self::$instance ) ) {
27
  self::$instance = new WPRP_Backups();
28
+ self::$instance->set_is_using_file_manifest( apply_filters( 'wprp_backups_use_file_manifest', false ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  }
31
 
32
+ return self::$instance;
33
 
34
  }
35
 
46
 
47
  // Set the excludes
48
  if ( class_exists( 'WPR_API_Request' ) && WPR_API_Request::get_arg( 'backup_excludes' ) )
49
+ $backup_excludes = WPR_API_Request::get_arg( 'backup_excludes' );
50
  else if ( isset( $_GET['backup_excludes'] ) )
51
+ $backup_excludes = $_GET['backup_excludes'];
52
+
53
+ if ( ! empty( $backup_excludes ) )
54
+ $this->set_excludes( apply_filters( 'wprp_backup_excludes', $backup_excludes ) );
55
 
56
  $this->filesize_transient = 'wprp_' . '_' . $this->get_type() . '_' . substr( md5( $this->exclude_string() ), 20 ) . '_filesize';
57
 
67
 
68
  $this->set_status( 'Starting backup...' );
69
 
70
+ $this->set_start_timestamp();
71
+
72
  $this->backup();
73
 
74
+ if ( ! file_exists( $this->get_archive_filepath() ) ) {
75
+
76
+ $errors = $this->get_errors();
77
+ if ( ! empty( $errors ) )
78
+ return new WP_Error( 'backup-failed', implode( ', ', $errors ) );
79
+ else
80
+ return new WP_Error( 'backup-failed', __( 'Backup file is missing.', 'wpremote' ) );
81
+
82
+ }
83
 
84
  return true;
85
 
94
 
95
  global $is_apache;
96
 
97
+ // Restore the start timestamp to global scope so HM Backup recognizes the proper archive file
98
+ $this->restore_start_timestamp();
99
 
100
+ if ( $status = $this->get_status() ) {
101
+
102
+ if ( $this->is_backup_still_running() )
103
+ return new WP_Error( 'error-status', $status );
104
+ else
105
+ return new WP_Error( 'backup-process-killed', __( 'Backup process failed or was killed.', 'wpremote' ) );
106
+ }
107
+
108
+ $backup = $this->get_archive_filepath();
109
 
110
  if ( file_exists( $backup ) ) {
111
 
122
 
123
  }
124
 
125
+ $response = new stdClass;
126
+ $response->url = str_replace( parent::conform_dir( WP_CONTENT_DIR ), WP_CONTENT_URL, $backup );
127
+ $response->seconds_elapsed = time() - $this->start_timestamp;
128
+ return $response;
129
 
130
  }
131
 
147
 
148
  }
149
 
150
+ /**
151
+ * Cleanup old ZipArchive partials that may have been left by old processes
152
+ */
153
+ public function cleanup_ziparchive_partials() {
154
+
155
+ foreach( glob( $this->get_path() . '/*.zip.*' ) as $ziparchive_partial ) {
156
+ unlink( $ziparchive_partial );
157
+ }
158
+
159
+ }
160
+
161
  /**
162
  * Get the estimated size of the sites files and database
163
  *
184
 
185
  // we dont know the size yet, fire off a remote request to get it for later
186
  // it can take some time so we have a small timeout then return "Calculating"
187
+ global $wprp_noauth_nonce;
188
+ wp_remote_get( add_query_arg( array( 'action' => 'wprp_calculate_backup_size', 'backup_excludes' => $this->get_excludes() ), add_query_arg( '_wpnonce', $wprp_noauth_nonce, admin_url( 'admin-ajax.php' ) ) ), array( 'timeout' => 0.1, 'sslverify' => false ) );
189
 
190
  return __( 'Calculating', 'wpremote' );
191
 
198
  */
199
  protected function do_action( $action ) {
200
 
201
+ $this->update_heartbeat_timestamp();
202
+
203
  switch ( $action ) :
204
 
205
+ case 'hmbkp_backup_started':
206
+
207
+ $this->save_backup_process_id();
208
+
209
+ break;
210
+
211
  case 'hmbkp_mysqldump_started' :
212
 
213
  $this->set_status( sprintf( __( 'Dumping Database %s', 'wpremote' ), '(<code>' . $this->get_mysqldump_method() . '</code>)' ) );
222
 
223
  case 'hmbkp_archive_started' :
224
 
225
+ if ( $this->is_using_file_manifest() )
226
+ $status = sprintf( __( '%d files remaining to archive %s', 'wpremote' ), $this->file_manifest_remaining, '(<code>' . $this->get_archive_method() . '</code>)' );
227
+ else
228
+ $status = sprintf( __( 'Creating zip archive %s', 'wpremote' ), '(<code>' . $this->get_archive_method() . '</code>)' );
229
+
230
+ $this->set_status( $status );
231
 
232
  break;
233
 
242
  if ( file_exists( $this->get_schedule_running_path() ) )
243
  unlink( $this->get_schedule_running_path() );
244
 
245
+ $this->clear_backup_process_id();
246
+
247
  break;
248
 
249
  case 'hmbkp_error' :
435
 
436
  }
437
 
438
+ /**
439
+ * Set the start timestamp for the backup
440
+ */
441
+ private function set_start_timestamp() {
442
+ $this->start_timestamp = current_time( 'timestamp' );
443
+ file_put_contents( $this->get_path() . '/.start-timestamp', $this->start_timestamp );
444
+ }
445
+
446
+ /**
447
+ * Restore the start timestamp for the backup
448
+ */
449
+ private function restore_start_timestamp() {
450
+ if ( $start_timestamp = file_get_contents( $this->get_path() . '/.start-timestamp' ) )
451
+ $this->start_timestamp = (int) $start_timestamp;
452
+ }
453
+
454
+ /**
455
+ * Update the heartbeat timestamp to the current time.
456
+ */
457
+ private function update_heartbeat_timestamp() {
458
+ file_put_contents( $this->get_path() . '/.heartbeat-timestamp', time() );
459
+ }
460
+
461
+ /**
462
+ * Get the heartbeat timestamp.
463
+ */
464
+ private function get_heartbeat_timestamp() {
465
+
466
+ $heartbeat = $this->get_path() . '/.heartbeat-timestamp';
467
+
468
+ if ( file_exists( $heartbeat ) )
469
+ return (int) file_get_contents( $heartbeat );
470
+
471
+ return false;
472
+ }
473
+
474
+ /**
475
+ * Get the file path to the backup process ID log
476
+ *
477
+ * @access private
478
+ */
479
+ private function get_backup_process_id_path() {
480
+ return $this->get_path() . '/.backup-process-id';
481
+ }
482
+
483
+ /**
484
+ * Get the current backup process ID
485
+ *
486
+ * @access private
487
+ */
488
+ private function get_backup_process_id() {
489
+ $file = $this->get_backup_process_id_path();
490
+ if ( file_exists( $file ) )
491
+ return (int) trim( file_get_contents( $file ) );
492
+ else
493
+ return false;
494
+ }
495
+
496
+ /**
497
+ * Save this current backup process ID in case
498
+ * we need to check later whether it was killed in action
499
+ *
500
+ * @access private
501
+ */
502
+ private function save_backup_process_id() {
503
+
504
+ if ( ! $handle = fopen( $this->get_backup_process_id_path(), 'w' ) )
505
+ return;
506
+
507
+ fwrite( $handle, getmypid() );
508
+
509
+ fclose( $handle );
510
+
511
+ }
512
+
513
+ /**
514
+ * Clear the backup process ID
515
+ *
516
+ * @access private
517
+ */
518
+ private function clear_backup_process_id() {
519
+
520
+ if ( file_exists( $this->get_backup_process_id_path() ) )
521
+ unlink( $this->get_backup_process_id_path() );
522
+ }
523
+
524
+ /**
525
+ * Whether or not a backup appears to be in progress
526
+ *
527
+ * @access private
528
+ */
529
+ private function is_backup_still_running() {
530
+
531
+ // Check whether there's supposed to be a backup in progress
532
+ if ( false == ( $process_id = $this->get_backup_process_id() ) )
533
+ return false;
534
+
535
+ $time_to_wait = 120;
536
+
537
+ // If the heartbeat has been modified in the last 90 seconds, we might not be dead
538
+ if ( ( time() - $this->get_heartbeat_timestamp() ) < $time_to_wait )
539
+ return true;
540
+
541
+ // Check if the database archive was modified recently
542
+ $database = $this->get_database_dump_filepath();
543
+ if ( file_exists( $database ) && ( ( time() - filemtime( $database ) ) < $time_to_wait ) )
544
+ return true;
545
+
546
+ // Check if there's a ZipArchive file being modified.
547
+ $ziparchive_files = glob( $this->get_path() . '/*.zip.*' );
548
+ $ziparchive_mtimes = array();
549
+ foreach( $ziparchive_files as $ziparchive_file ) {
550
+ $ziparchive_mtimes[] = filemtime( $ziparchive_file );
551
+ }
552
+ if ( ! empty( $ziparchive_mtimes ) ) {
553
+ $latest_ziparchive_mtime = max( $ziparchive_mtimes );
554
+ if ( ( time() - $latest_ziparchive_mtime ) < $time_to_wait )
555
+ return true;
556
+ }
557
+
558
+ return false;
559
+ }
560
+
561
+ /**
562
+ * Check if there's a backup in progress, whether it's running,
563
+ * and restart it if it's not running
564
+ *
565
+ * @todo support checking whether the database should exist
566
+ */
567
+ public function backup_heartbeat() {
568
+
569
+ // Restore the start timestamp to global scope so HM Backup recognizes the proper archive file
570
+ $this->restore_start_timestamp();
571
+
572
+ // No process means no backup in progress
573
+ if ( ! $this->get_backup_process_id() )
574
+ return false;
575
+
576
+ // No file manifest directory means this wasn't a file manifest approach
577
+ if ( ! is_dir( $this->get_file_manifest_dirpath() ) )
578
+ return false;
579
+
580
+ // Check whether there's supposed to be a backup in progress
581
+ if ( $this->get_backup_process_id() && $this->is_backup_still_running() )
582
+ return false;
583
+
584
+ // Uh oh, needs to be restarted
585
+ $this->cleanup_ziparchive_partials();
586
+
587
+ $this->save_backup_process_id();
588
+
589
+ $this->restart_archive();
590
+
591
+ }
592
+
593
  /**
594
  * Calculate the size of the backup
595
  *
651
 
652
  }
653
 
654
+ /*
655
  * Return an array of back meta information
656
  *
657
  * @return array
675
  */
676
  function wprp_ajax_calculate_backup_size() {
677
 
678
+ if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'wprp_calculate_backup_size' ) )
679
+ exit;
680
+
681
  WPRP_Backups::get_instance()->get_filesize();
682
 
683
  exit;
wprp.hm.backup.php CHANGED
@@ -23,6 +23,14 @@ class WPRP_HM_Backup {
23
  */
24
  private $type = '';
25
 
 
 
 
 
 
 
 
 
26
  /**
27
  * The filename of the backup file
28
  *
@@ -138,6 +146,54 @@ class WPRP_HM_Backup {
138
  */
139
  private $mysqldump_method = '';
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  /**
142
  * Check whether safe mode is active or not
143
  *
@@ -271,8 +327,13 @@ class WPRP_HM_Backup {
271
  */
272
  public function get_archive_filename() {
273
 
274
- if ( empty( $this->archive_filename ) )
275
- $this->set_archive_filename( implode( '-', array( sanitize_title( str_ireplace( array( 'http://', 'https://', 'www' ), '', home_url() ) ), 'backup', date( 'Y-m-d-H-i-s', current_time( 'timestamp' ) ) ) ) . '.zip' );
 
 
 
 
 
276
 
277
  return $this->archive_filename;
278
 
@@ -425,6 +486,161 @@ class WPRP_HM_Backup {
425
  return $this->mysqldump_method;
426
  }
427
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
  /**
429
  * Get the backup type
430
  *
@@ -595,6 +811,60 @@ class WPRP_HM_Backup {
595
 
596
  }
597
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
598
  protected function do_action( $action ) {
599
 
600
  do_action( $action, $this );
@@ -767,6 +1037,147 @@ class WPRP_HM_Backup {
767
  */
768
  public function archive() {
769
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
770
  // Do we have the path to the zip command
771
  if ( $this->get_zip_command_path() )
772
  $this->zip();
@@ -787,6 +1198,26 @@ class WPRP_HM_Backup {
787
 
788
  }
789
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
790
  /**
791
  * Zip using the native zip command
792
  *
@@ -799,13 +1230,15 @@ class WPRP_HM_Backup {
799
  $this->do_action( 'hmbkp_archive_started' );
800
 
801
  // Zip up $this->root with excludes
802
- if ( $this->get_type() !== 'database' && $this->exclude_string( 'zip' ) )
803
  $stderr = shell_exec( 'cd ' . escapeshellarg( $this->get_root() ) . ' && ' . escapeshellcmd( $this->get_zip_command_path() ) . ' -rq ' . escapeshellarg( $this->get_archive_filepath() ) . ' ./' . ' -x ' . $this->exclude_string( 'zip' ) . ' 2>&1' );
804
 
805
  // Zip up $this->root without excludes
806
- elseif ( $this->get_type() !== 'database' )
807
  $stderr = shell_exec( 'cd ' . escapeshellarg( $this->get_root() ) . ' && ' . escapeshellcmd( $this->get_zip_command_path() ) . ' -rq ' . escapeshellarg( $this->get_archive_filepath() ) . ' ./' . ' 2>&1' );
808
 
 
 
809
  // Add the database dump to the archive
810
  if ( $this->get_type() !== 'file' && file_exists( $this->get_database_dump_filepath() ) )
811
  $stderr = shell_exec( 'cd ' . escapeshellarg( $this->get_path() ) . ' && ' . escapeshellcmd( $this->get_zip_command_path() ) . ' -uq ' . escapeshellarg( $this->get_archive_filepath() ) . ' ' . escapeshellarg( $this->get_database_dump_filename() ) . ' 2>&1' );
@@ -814,7 +1247,25 @@ class WPRP_HM_Backup {
814
  $this->warning( $this->get_archive_method(), $stderr );
815
 
816
  $this->verify_archive();
 
 
 
 
 
 
 
 
817
 
 
 
 
 
 
 
 
 
 
 
818
  }
819
 
820
  /**
@@ -828,10 +1279,8 @@ class WPRP_HM_Backup {
828
 
829
  $this->do_action( 'hmbkp_archive_started' );
830
 
831
- $zip = new ZipArchive();
832
-
833
- if ( ! class_exists( 'ZipArchive' ) || ! $zip->open( $this->get_archive_filepath(), ZIPARCHIVE::CREATE ) )
834
- return;
835
 
836
  $excludes = $this->exclude_string( 'regex' );
837
 
@@ -883,6 +1332,31 @@ class WPRP_HM_Backup {
883
 
884
  }
885
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
886
  /**
887
  * Fallback for creating zip archives if zip command and ZipArchive are
888
  * unavailable.
@@ -900,9 +1374,7 @@ class WPRP_HM_Backup {
900
 
901
  $_wprp_hmbkp_exclude_string = $this->exclude_string( 'regex' );
902
 
903
- $this->load_pclzip();
904
-
905
- $archive = new PclZip( $this->get_archive_filepath() );
906
 
907
  // Zip up everything
908
  if ( $this->get_type() !== 'database' )
@@ -920,6 +1392,28 @@ class WPRP_HM_Backup {
920
 
921
  }
922
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
923
  public function verify_mysqldump() {
924
 
925
  $this->do_action( 'hmbkp_mysqldump_verify_started' );
@@ -1593,10 +2087,10 @@ class WPRP_HM_Backup {
1593
  if ( ! $handle = @fopen( $sqlname, 'a' ) )
1594
  return;
1595
 
1596
- if ( ! fwrite( $handle, $sql ) )
1597
  return;
1598
 
1599
- fclose( $handle );
1600
 
1601
  return true;
1602
 
@@ -1630,10 +2124,10 @@ class WPRP_HM_Backup {
1630
  if ( empty( $context ) || empty( $error ) )
1631
  return;
1632
 
1633
- $this->do_action( 'hmbkp_error' );
1634
-
1635
  $this->errors[$context][$_key = md5( implode( ':' , (array) $error ) )] = $error;
1636
 
 
 
1637
  }
1638
 
1639
  /**
@@ -1715,6 +2209,37 @@ class WPRP_HM_Backup {
1715
 
1716
  }
1717
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1718
  }
1719
 
1720
  /**
23
  */
24
  private $type = '';
25
 
26
+ /**
27
+ * The start timestamp of the backup
28
+ *
29
+ * @int
30
+ * @access protected
31
+ */
32
+ protected $start_timestamp;
33
+
34
  /**
35
  * The filename of the backup file
36
  *
146
  */
147
  private $mysqldump_method = '';
148
 
149
+ /**
150
+ * Whether or not to use a file manifest (more write-intensive)
151
+ *
152
+ * @var bool
153
+ * @access private
154
+ */
155
+ private $using_file_manifest = false;
156
+
157
+ /**
158
+ * The current file manifest file.
159
+ *
160
+ * @access private
161
+ */
162
+ private $current_file_manifest = false;
163
+
164
+
165
+ /**
166
+ * Files of the file manifest that have already been archived
167
+ *
168
+ * @access private
169
+ */
170
+ private $file_manifest_already_archived = array();
171
+
172
+ /**
173
+ * When using the file manifest, the number of files that should be
174
+ * archived per batch.
175
+ *
176
+ * @access private
177
+ */
178
+ private $file_manifest_per_batch = 200;
179
+
180
+ /**
181
+ * Files remaining to be achived in the file manifest.
182
+ *
183
+ * @access protected
184
+ */
185
+ protected $file_manifest_remaining = 0;
186
+
187
+ /**
188
+ * A ZipArchive instance for this instance
189
+ */
190
+ private $ziparchive = false;
191
+
192
+ /**
193
+ * A PclZip instance for this instance
194
+ */
195
+ private $pclzip = false;
196
+
197
  /**
198
  * Check whether safe mode is active or not
199
  *
327
  */
328
  public function get_archive_filename() {
329
 
330
+ if ( empty( $this->archive_filename ) ) {
331
+
332
+ if ( empty( $this->start_timestamp ) )
333
+ $this->start_timestamp = current_time( 'timestamp' );
334
+
335
+ $this->set_archive_filename( implode( '-', array( sanitize_title( str_ireplace( array( 'http://', 'https://', 'www' ), '', home_url() ) ), 'backup', date( 'Y-m-d-H-i-s', $this->start_timestamp ) ) ) . '.zip' );
336
+ }
337
 
338
  return $this->archive_filename;
339
 
486
  return $this->mysqldump_method;
487
  }
488
 
489
+ /**
490
+ * Whether or not to use the file manifest
491
+ *
492
+ * @access public
493
+ */
494
+ public function is_using_file_manifest() {
495
+ return (bool)$this->using_file_manifest;
496
+ }
497
+
498
+ /**
499
+ * Set whether or not to use the file manifest
500
+ *
501
+ * @access public
502
+ */
503
+ public function set_is_using_file_manifest( $val ) {
504
+ $this->using_file_manifest = (bool)$val;
505
+ }
506
+
507
+ /**
508
+ * Create a series of file manifests for the backup
509
+ *
510
+ * @access private
511
+ */
512
+ private function create_file_manifests() {
513
+
514
+ if ( is_dir( $this->get_file_manifest_dirpath() ) )
515
+ $this->rmdir_recursive( $this->get_file_manifest_dirpath() );
516
+
517
+ mkdir( $this->get_file_manifest_dirpath(), 0755 );
518
+
519
+ // Protect against directory browsing by including a index.html file
520
+ $index = $this->get_file_manifest_dirpath() . '/index.html';
521
+ if ( ! file_exists( $index ) && is_writable( $this->get_file_manifest_dirpath() ) )
522
+ file_put_contents( $index, '' );
523
+
524
+ $excludes = $this->exclude_string( 'regex' );
525
+
526
+ $file_manifest = array();
527
+ $this->file_manifest_remaining = 0;
528
+ $file_manifest_file_count = 0;
529
+ $current_batch = 0;
530
+ foreach( $this->get_files() as $file ) {
531
+
532
+ // Skip dot files, they should only exist on versions of PHP between 5.2.11 -> 5.3
533
+ if ( method_exists( $file, 'isDot' ) && $file->isDot() )
534
+ continue;
535
+
536
+ // Skip unreadable files
537
+ if ( ! @realpath( $file->getPathname() ) || ! $file->isReadable() )
538
+ continue;
539
+
540
+ // Excludes
541
+ if ( $excludes && preg_match( '(' . $excludes . ')', str_ireplace( trailingslashit( $this->get_root() ), '', self::conform_dir( $file->getPathname() ) ) ) )
542
+ continue;
543
+
544
+ if ( $file->isDir() )
545
+ $line = trailingslashit( str_ireplace( trailingslashit( $this->get_root() ), '', self::conform_dir( $file->getPathname() ) ) );
546
+
547
+ elseif ( $file->isFile() )
548
+ $line = str_ireplace( trailingslashit( $this->get_root() ), '', self::conform_dir( $file->getPathname() ) );
549
+
550
+ // File manifest is full
551
+ if ( ! empty( $current_file ) && $current_batch >= $this->file_manifest_per_batch ) {
552
+
553
+ @fclose( $current_file );
554
+ $current_file = false;
555
+
556
+ }
557
+
558
+ // Create a new file manifest
559
+ if ( empty( $current_file ) ) {
560
+
561
+ $file_manifest_file_count++;
562
+ $file_manifest_filename = str_pad( $file_manifest_file_count, 10, "0", STR_PAD_LEFT );
563
+ if ( ! $current_file = @fopen( $this->get_file_manifest_dirpath() . '/' . $file_manifest_filename . '.txt', 'w' ) )
564
+ return false;
565
+
566
+ $current_batch = 0;
567
+ }
568
+
569
+ // Write the line to the file manifest if it isn't empty for some reason
570
+ if ( ! empty( $line ) ) {
571
+ @fwrite( $current_file, $line . PHP_EOL );
572
+ unset( $line );
573
+ $this->file_manifest_remaining++;
574
+ $current_batch++;
575
+ }
576
+
577
+ }
578
+
579
+ @file_put_contents( $this->get_path() . '/.file-manifest-remaining', $this->file_manifest_remaining );
580
+
581
+ return true;
582
+ }
583
+
584
+ /**
585
+ * Delete the current file manifest
586
+ *
587
+ * @access private
588
+ */
589
+ private function delete_current_file_manifest() {
590
+
591
+ if ( ! file_exists( $this->current_file_manifest ) )
592
+ return false;
593
+
594
+ // Remove the file manifest because it's already been archived
595
+ unlink( $this->current_file_manifest );
596
+
597
+ // Update the count of remaining files.
598
+ $this->file_manifest_remaining = $this->file_manifest_remaining - count( $this->file_manifest_already_archived );
599
+ if ( $this->file_manifest_remaining < 0 )
600
+ $this->file_manifest_remaining = 0;
601
+ file_put_contents( $this->get_path() . '/.file-manifest-remaining', $this->file_manifest_remaining );
602
+
603
+ $this->file_manifest_already_archived = array();
604
+
605
+ }
606
+
607
+
608
+ /**
609
+ * Get the path to the file manifest directory
610
+ *
611
+ * @access private
612
+ */
613
+ protected function get_file_manifest_dirpath() {
614
+ return $this->get_path() . '/.file-manifests';
615
+ }
616
+
617
+ /**
618
+ * Get batch of files to archive from the file manifest
619
+ * Ignore any files that already have been archived
620
+ *
621
+ * @access private
622
+ */
623
+ private function get_next_files_from_file_manifest() {
624
+
625
+ if ( ! is_dir( $this->get_file_manifest_dirpath() ) )
626
+ return array();
627
+
628
+ $files = glob( $this->get_file_manifest_dirpath() . '/*.txt' );
629
+ if ( empty( $files ) )
630
+ return array();
631
+
632
+ $this->current_file_manifest = array_shift( $files );
633
+
634
+ $files = file_get_contents( $this->current_file_manifest );
635
+ $files = array_map( 'trim', explode( PHP_EOL, $files ) );
636
+ if ( empty( $files ) )
637
+ return array();
638
+
639
+ $this->file_manifest_remaining = (int)file_get_contents( $this->get_path() . '/.file-manifest-remaining' );
640
+
641
+ return $files;
642
+ }
643
+
644
  /**
645
  * Get the backup type
646
  *
811
 
812
  }
813
 
814
+ /**
815
+ * Set up the ZipArchive instance if ZipArchive is available
816
+ */
817
+ protected function &setup_ziparchive() {
818
+
819
+ // Instance is already open
820
+ if ( ! empty( $this->ziparchive ) ) {
821
+ $this->ziparchive->open( $this->get_archive_filepath(), ZIPARCHIVE::CREATE );
822
+ return $this->ziparchive;
823
+ }
824
+
825
+ $ziparchive = new ZipArchive;
826
+
827
+ // Try opening ZipArchive
828
+ if ( ! file_exists( $this->get_archive_filepath() ) )
829
+ $ret = $ziparchive->open( $this->get_archive_filepath(), ZIPARCHIVE::CREATE );
830
+ else
831
+ $ret = $ziparchive->open( $this->get_archive_filepath() );
832
+
833
+ // File couldn't be opened
834
+ if ( ! $ret )
835
+ return false;
836
+
837
+ // Try closing ZipArchive
838
+ $ret = $ziparchive->close();
839
+
840
+ // File couldn't be closed
841
+ if ( ! $ret )
842
+ return false;
843
+
844
+ // Open it once more
845
+ if ( ! file_exists( $this->get_archive_filepath() ) )
846
+ $ziparchive->open( $this->get_archive_filepath(), ZIPARCHIVE::CREATE );
847
+ else
848
+ $ziparchive->open( $this->get_archive_filepath() );
849
+
850
+ $this->ziparchive = $ziparchive;
851
+ return $this->ziparchive;
852
+ }
853
+
854
+ /**
855
+ * Set up the PclZip instance
856
+ *
857
+ * @access protected
858
+ */
859
+ protected function &setup_pclzip() {
860
+
861
+ if ( empty( $this->pclzip ) ) {
862
+ $this->load_pclzip();
863
+ $this->pclzip = new PclZip( $this->get_archive_filepath() );
864
+ }
865
+ return $this->pclzip;
866
+ }
867
+
868
  protected function do_action( $action ) {
869
 
870
  do_action( $action, $this );
1037
  */
1038
  public function archive() {
1039
 
1040
+ // If using a manifest, perform the backup in chunks
1041
+ if ( 'database' !== $this->get_type()
1042
+ && $this->is_using_file_manifest()
1043
+ && $this->create_file_manifests() ) {
1044
+
1045
+ $this->archive_via_file_manifest();
1046
+
1047
+ } else {
1048
+
1049
+ $this->archive_via_single_request();
1050
+
1051
+ }
1052
+
1053
+ }
1054
+
1055
+ /**
1056
+ * Archive with a file manifest
1057
+ *
1058
+ * @access private
1059
+ */
1060
+ private function archive_via_file_manifest() {
1061
+
1062
+ $errors = array();
1063
+
1064
+ // Back up files from the file manifest in chunks
1065
+ $next_files = $this->get_next_files_from_file_manifest();
1066
+ do {
1067
+
1068
+ $this->do_action( 'hmbkp_archive_started' );
1069
+
1070
+ // ZipArchive is the fastest for chunked backups
1071
+ if ( class_exists( 'ZipArchive' ) && empty( $this->skip_zip_archive ) ) {
1072
+ $this->archive_method = 'zip_archive_files';
1073
+
1074
+ $ret = $this->zip_archive_files( $next_files );
1075
+ if ( ! $ret ) {
1076
+ $this->skip_zip_archive = true;
1077
+ continue;
1078
+ }
1079
+ }
1080
+
1081
+ // Fall back to `zip` if ZipArchive doesn't exist
1082
+ else if ( $this->get_zip_command_path() ) {
1083
+ $this->archive_method = 'zip_files';
1084
+ $error = $this->zip_files( $next_files );
1085
+ }
1086
+
1087
+ // Last opportunity
1088
+ else {
1089
+ $this->archive_method = 'pcl_zip_files';
1090
+ $error = $this->pcl_zip_files( $next_files );
1091
+ }
1092
+
1093
+ if ( ! empty( $error ) ) {
1094
+ $errors[] = $error;
1095
+ unset( $error );
1096
+ }
1097
+
1098
+ // Update the file manifest with these files that were archived
1099
+ $this->file_manifest_already_archived = array_merge( $this->file_manifest_already_archived, $next_files );
1100
+ $this->delete_current_file_manifest();
1101
+
1102
+ // Get the next set of files to archive
1103
+ $next_files = $this->get_next_files_from_file_manifest();
1104
+
1105
+ } while( ! empty( $next_files ) );
1106
+
1107
+ // If the database should be included in the backup, it's included last
1108
+ if ( 'file' !== $this->get_type() && file_exists( $this->get_database_dump_filepath() ) ) {
1109
+
1110
+ switch ( $this->archive_method ) {
1111
+
1112
+ case 'zip_archive_files':
1113
+
1114
+ $zip = &$this->setup_ziparchive();
1115
+
1116
+ $zip->addFile( $this->get_database_dump_filepath(), $this->get_database_dump_filename() );
1117
+
1118
+ $zip->close();
1119
+
1120
+ break;
1121
+
1122
+ case 'zip_files':
1123
+
1124
+ $error = shell_exec( 'cd ' . escapeshellarg( $this->get_path() ) . ' && ' . escapeshellcmd( $this->get_zip_command_path() ) . ' -uq ' . escapeshellarg( $this->get_archive_filepath() ) . ' ' . escapeshellarg( $this->get_database_dump_filename() ) . ' 2>&1' );
1125
+
1126
+ break;
1127
+
1128
+ case 'pcl_zip_files':
1129
+
1130
+ $pclzip = &$this->setup_pclzip();
1131
+
1132
+ if ( ! $pclzip->add( $this->get_database_dump_filepath(), PCLZIP_OPT_REMOVE_PATH, $this->get_path() ) )
1133
+ $this->warning( $this->get_archive_method(), $pclzip->errorInfo( true ) );
1134
+
1135
+ break;
1136
+ }
1137
+
1138
+ if ( ! empty( $error ) ) {
1139
+ $errors[] = $error;
1140
+ unset( $error );
1141
+ }
1142
+ }
1143
+
1144
+ // If the methods produced any errors, log them
1145
+ if ( ! empty( $errors ) )
1146
+ $this->warning( $this->get_archive_method(), implode( ', ', $errors ) );
1147
+
1148
+ // ZipArchive has some special reporting requirements
1149
+ if ( ! empty( $this->ziparchive ) ) {
1150
+
1151
+ if ( $this->ziparchive->status )
1152
+ $this->warning( $this->get_archive_method(), $this->ziparchive->status );
1153
+
1154
+ if ( $this->ziparchive->statusSys )
1155
+ $this->warning( $this->get_archive_method(), $this->ziparchive->statusSys );
1156
+
1157
+ }
1158
+
1159
+ // Verify and remove if errors
1160
+ $this->verify_archive();
1161
+
1162
+ // Remove the file manifest
1163
+ if ( is_dir( $this->get_file_manifest_dirpath() ) )
1164
+ $this->rmdir_recursive( $this->get_file_manifest_dirpath() );
1165
+
1166
+ // Delete the database dump file
1167
+ if ( file_exists( $this->get_database_dump_filepath() ) )
1168
+ unlink( $this->get_database_dump_filepath() );
1169
+
1170
+ $this->do_action( 'hmbkp_archive_finished' );
1171
+
1172
+ }
1173
+
1174
+ /**
1175
+ * Archive using our traditional method of one request
1176
+ *
1177
+ * @access private
1178
+ */
1179
+ private function archive_via_single_request() {
1180
+
1181
  // Do we have the path to the zip command
1182
  if ( $this->get_zip_command_path() )
1183
  $this->zip();
1198
 
1199
  }
1200
 
1201
+ /**
1202
+ * Restart a failed archive process
1203
+ *
1204
+ * @access public
1205
+ */
1206
+ public function restart_archive() {
1207
+
1208
+ if ( $this->is_using_file_manifest() ) {
1209
+
1210
+ $this->archive_via_file_manifest();
1211
+
1212
+ } else {
1213
+
1214
+ $this->archive_via_single_request();
1215
+
1216
+ }
1217
+
1218
+ $this->do_action( 'hmbkp_backup_complete' );
1219
+ }
1220
+
1221
  /**
1222
  * Zip using the native zip command
1223
  *
1230
  $this->do_action( 'hmbkp_archive_started' );
1231
 
1232
  // Zip up $this->root with excludes
1233
+ if ( $this->get_type() !== 'database' && $this->exclude_string( 'zip' ) ) {
1234
  $stderr = shell_exec( 'cd ' . escapeshellarg( $this->get_root() ) . ' && ' . escapeshellcmd( $this->get_zip_command_path() ) . ' -rq ' . escapeshellarg( $this->get_archive_filepath() ) . ' ./' . ' -x ' . $this->exclude_string( 'zip' ) . ' 2>&1' );
1235
 
1236
  // Zip up $this->root without excludes
1237
+ } elseif ( $this->get_type() !== 'database' ) {
1238
  $stderr = shell_exec( 'cd ' . escapeshellarg( $this->get_root() ) . ' && ' . escapeshellcmd( $this->get_zip_command_path() ) . ' -rq ' . escapeshellarg( $this->get_archive_filepath() ) . ' ./' . ' 2>&1' );
1239
 
1240
+ }
1241
+
1242
  // Add the database dump to the archive
1243
  if ( $this->get_type() !== 'file' && file_exists( $this->get_database_dump_filepath() ) )
1244
  $stderr = shell_exec( 'cd ' . escapeshellarg( $this->get_path() ) . ' && ' . escapeshellcmd( $this->get_zip_command_path() ) . ' -uq ' . escapeshellarg( $this->get_archive_filepath() ) . ' ' . escapeshellarg( $this->get_database_dump_filename() ) . ' 2>&1' );
1247
  $this->warning( $this->get_archive_method(), $stderr );
1248
 
1249
  $this->verify_archive();
1250
+ }
1251
+
1252
+ /**
1253
+ * Zip one or more files
1254
+ *
1255
+ * @access private
1256
+ */
1257
+ private function zip_files( $files ) {
1258
 
1259
+ // Not necessary to include directories when using `zip`
1260
+ foreach( $files as $key => $file ) {
1261
+
1262
+ if ( ! is_dir( $file ) )
1263
+ continue;
1264
+
1265
+ unset( $files[$key] );
1266
+ }
1267
+
1268
+ return shell_exec( 'cd ' . escapeshellarg( $this->get_root() ) . ' && ' . escapeshellcmd( $this->get_zip_command_path() ) . ' ' . escapeshellarg( $this->get_archive_filepath() ) . ' ' . implode( ' ', $files ) . ' -q 2>&1' );
1269
  }
1270
 
1271
  /**
1279
 
1280
  $this->do_action( 'hmbkp_archive_started' );
1281
 
1282
+ if ( false === ( $zip = &$this->setup_ziparchive() ) )
1283
+ return;
 
 
1284
 
1285
  $excludes = $this->exclude_string( 'regex' );
1286
 
1332
 
1333
  }
1334
 
1335
+ /**
1336
+ * Zip Archive one or more files
1337
+ *
1338
+ * @access private
1339
+ */
1340
+ private function zip_archive_files( $files ) {
1341
+
1342
+ if ( false === ( $zip = &$this->setup_ziparchive() ) )
1343
+ return false;
1344
+
1345
+ foreach( $files as $file ) {
1346
+
1347
+ $full_path = trailingslashit( $this->get_root() ) . $file;
1348
+ if ( is_dir( $full_path ) )
1349
+ $zip->addEmptyDir( $file );
1350
+ else
1351
+ $zip->addFile( $full_path, $file );
1352
+
1353
+ }
1354
+
1355
+ // Avoid limitations in ZipArchive by making sure we save each batch to disk
1356
+ $zip->close();
1357
+ return true;
1358
+ }
1359
+
1360
  /**
1361
  * Fallback for creating zip archives if zip command and ZipArchive are
1362
  * unavailable.
1374
 
1375
  $_wprp_hmbkp_exclude_string = $this->exclude_string( 'regex' );
1376
 
1377
+ $archive = &$this->setup_pclzip();
 
 
1378
 
1379
  // Zip up everything
1380
  if ( $this->get_type() !== 'database' )
1392
 
1393
  }
1394
 
1395
+ /**
1396
+ * Use PclZip to archive batches of files
1397
+ */
1398
+ private function pcl_zip_files( $files ) {
1399
+
1400
+ $this->errors_to_warnings( $this->get_archive_method() );
1401
+
1402
+ $pclzip = &$this->setup_pclzip();
1403
+
1404
+ foreach( $files as $file ) {
1405
+
1406
+ $full_path = trailingslashit( $this->get_root() ) . $file;
1407
+ if ( is_dir( $full_path ) )
1408
+ continue;
1409
+
1410
+ if ( ! $pclzip->add( $full_path, PCLZIP_OPT_REMOVE_PATH, $this->get_root() ) )
1411
+ $this->warning( $this->get_archive_method(), $pclzip->errorInfo( true ) );
1412
+
1413
+ }
1414
+
1415
+ }
1416
+
1417
  public function verify_mysqldump() {
1418
 
1419
  $this->do_action( 'hmbkp_mysqldump_verify_started' );
2087
  if ( ! $handle = @fopen( $sqlname, 'a' ) )
2088
  return;
2089
 
2090
+ if ( ! @fwrite( $handle, $sql ) )
2091
  return;
2092
 
2093
+ @fclose( $handle );
2094
 
2095
  return true;
2096
 
2124
  if ( empty( $context ) || empty( $error ) )
2125
  return;
2126
 
 
 
2127
  $this->errors[$context][$_key = md5( implode( ':' , (array) $error ) )] = $error;
2128
 
2129
+ $this->do_action( 'hmbkp_error' );
2130
+
2131
  }
2132
 
2133
  /**
2209
 
2210
  }
2211
 
2212
+ /**
2213
+ * Recursively delete a directory including
2214
+ * all the files and sub-directories.
2215
+ *
2216
+ * @param string $dir
2217
+ * @return bool
2218
+ */
2219
+ public static function rmdir_recursive( $dir ) {
2220
+
2221
+ if ( is_file( $dir ) )
2222
+ @unlink( $dir );
2223
+
2224
+ if ( ! is_dir( $dir ) )
2225
+ return false;
2226
+
2227
+ $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir ), RecursiveIteratorIterator::CHILD_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD );
2228
+
2229
+ foreach ( $files as $file ) {
2230
+
2231
+ if ( $file->isDir() )
2232
+ @rmdir( $file->getPathname() );
2233
+
2234
+ else
2235
+ @unlink( $file->getPathname() );
2236
+
2237
+ }
2238
+
2239
+ @rmdir( $dir );
2240
+
2241
+ }
2242
+
2243
  }
2244
 
2245
  /**
wprp.integration.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Integration compatibility with hosts, plugins, and themes
4
+ */
5
+
6
+ /**
7
+ * Get the likely web host for this site.
8
+ */
9
+ function _wprp_integration_get_web_host() {
10
+
11
+ // WP Engine
12
+ if ( defined( 'IS_WPE' ) && IS_WPE )
13
+ return 'wpengine';
14
+
15
+ return 'unknown';
16
+ }
wprp.plugins.php CHANGED
@@ -36,22 +36,36 @@ function _wprp_get_plugins() {
36
  else
37
  $current = get_option( 'update_plugins' );
38
 
39
- foreach ( (array) $plugins as $plugin_file => $plugin ) {
 
40
 
41
- $new_version = isset( $current->response[$plugin_file] ) ? $current->response[$plugin_file]->new_version : null;
42
 
43
  if ( is_plugin_active( $plugin_file ) )
44
  $plugins[$plugin_file]['active'] = true;
45
-
46
  else
47
  $plugins[$plugin_file]['active'] = false;
48
 
49
- if ( $new_version ) {
50
- $plugins[$plugin_file]['latest_version'] = $new_version;
51
- $plugins[$plugin_file]['latest_package'] = $current->response[$plugin_file]->package;
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  $plugins[$plugin_file]['slug'] = $current->response[$plugin_file]->slug;
53
 
54
  } else {
 
55
  $plugins[$plugin_file]['latest_version'] = $plugin['Version'];
56
 
57
  }
@@ -68,7 +82,11 @@ function _wprp_get_plugins() {
68
  * @param mixed $plugin
69
  * @return array
70
  */
71
- function _wprp_update_plugin( $plugin ) {
 
 
 
 
72
 
73
  include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
74
  require_once ( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
@@ -76,46 +94,109 @@ function _wprp_update_plugin( $plugin ) {
76
 
77
  // check for filesystem access
78
  if ( ! _wpr_check_filesystem_access() )
79
- return array( 'status' => 'error', 'error' => 'The filesystem is not writable with the supplied credentials' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
  $skin = new WPRP_Plugin_Upgrader_Skin();
82
  $upgrader = new Plugin_Upgrader( $skin );
83
- $is_active = is_plugin_active( $plugin );
84
 
85
- // Force a plugin update check
86
- wp_update_plugins();
 
 
 
 
 
 
 
 
87
 
88
  // Do the upgrade
89
  ob_start();
90
- $result = $upgrader->upgrade( $plugin );
91
  $data = ob_get_contents();
92
  ob_clean();
93
 
 
 
 
94
  if ( ! empty( $skin->error ) )
95
 
96
- return array( 'status' => 'error', 'error' => $upgrader->strings[$skin->error] );
97
 
98
  else if ( is_wp_error( $result ) )
99
 
100
- return array( 'status' => 'error', 'error' => $result->get_error_code() );
101
 
102
  else if ( ( ! $result && ! is_null( $result ) ) || $data )
103
 
104
- return array( 'status' => 'error', 'error' => 'Unknown error updating plugin.' );
105
 
106
  // If the plugin was activited, we have to re-activate it
107
  // but if activate_plugin() fatals, then we'll just have to return 500
108
  if ( $is_active )
109
- activate_plugin( $plugin, '', false, true );
110
 
111
  return array( 'status' => 'success' );
112
  }
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  /**
115
  * Install a plugin on this site
116
  */
117
  function _wprp_install_plugin( $plugin, $args = array() ) {
118
 
 
 
 
119
  include_once ABSPATH . 'wp-admin/includes/admin.php';
120
  include_once ABSPATH . 'wp-admin/includes/upgrade.php';
121
  include_once ABSPATH . 'wp-includes/update.php';
@@ -129,7 +210,7 @@ function _wprp_install_plugin( $plugin, $args = array() ) {
129
  $api = plugins_api( 'plugin_information', $api_args );
130
 
131
  if ( is_wp_error( $api ) )
132
- return array( 'status' => 'error', 'error' => $api->get_error_code() );
133
 
134
  $skin = new WPRP_Plugin_Upgrader_Skin();
135
  $upgrader = new Plugin_Upgrader( $skin );
@@ -141,9 +222,9 @@ function _wprp_install_plugin( $plugin, $args = array() ) {
141
 
142
  $result = $upgrader->install( $api->download_link );
143
  if ( is_wp_error( $result ) )
144
- return array( 'status' => 'error', 'error' => $result->get_error_code() );
145
  else if ( ! $result )
146
- return array( 'status' => 'error', 'error' => 'Unknown error installing plugin.' );
147
 
148
  return array( 'status' => 'success' );
149
  }
@@ -155,7 +236,7 @@ function _wprp_activate_plugin( $plugin ) {
155
  $result = activate_plugin( $plugin );
156
 
157
  if ( is_wp_error( $result ) )
158
- return array( 'status' => 'error', 'error' => $result->get_error_code() );
159
 
160
  return array( 'status' => 'success' );
161
  }
@@ -179,16 +260,19 @@ function _wprp_deactivate_plugin( $plugin ) {
179
  function _wprp_uninstall_plugin( $plugin ) {
180
  global $wp_filesystem;
181
 
 
 
 
182
  include_once ABSPATH . 'wp-admin/includes/admin.php';
183
  include_once ABSPATH . 'wp-admin/includes/upgrade.php';
184
  include_once ABSPATH . 'wp-includes/update.php';
185
 
186
  if ( ! _wpr_check_filesystem_access() || ! WP_Filesystem() )
187
- return array( 'status' => 'error', 'error' => 'The filesystem is not writable with the supplied credentials' );
188
 
189
  $plugins_dir = $wp_filesystem->wp_plugins_dir();
190
  if ( empty( $plugins_dir ) )
191
- return array( 'status' => 'error', 'error' => 'Unable to locate WordPress Plugin directory.' );
192
 
193
  $plugins_dir = trailingslashit( $plugins_dir );
194
 
@@ -209,7 +293,7 @@ function _wprp_uninstall_plugin( $plugin ) {
209
  }
210
  return array( 'status' => 'success' );
211
  } else {
212
- return array( 'status' => 'error', 'error' => 'Plugin uninstalled, but not deleted.' );
213
  }
214
 
215
  }
36
  else
37
  $current = get_option( 'update_plugins' );
38
 
39
+ // Premium plugins that have adopted the ManageWP API report new plugins by this filter
40
+ $manage_wp_updates = apply_filters( 'mwp_premium_update_notification', array() );
41
 
42
+ foreach ( (array) $plugins as $plugin_file => $plugin ) {
43
 
44
  if ( is_plugin_active( $plugin_file ) )
45
  $plugins[$plugin_file]['active'] = true;
 
46
  else
47
  $plugins[$plugin_file]['active'] = false;
48
 
49
+ $manage_wp_plugin_update = false;
50
+ foreach( $manage_wp_updates as $manage_wp_update ) {
51
+
52
+ if ( ! empty( $manage_wp_update['Name'] ) && $plugin['Name'] == $manage_wp_update['Name'] )
53
+ $manage_wp_plugin_update = $manage_wp_update;
54
+
55
+ }
56
+
57
+ if ( $manage_wp_plugin_update ) {
58
+
59
+ $plugins[$plugin_file]['latest_version'] = $manage_wp_plugin_update['new_version'];
60
+
61
+ } else if ( isset( $current->response[$plugin_file] ) ) {
62
+
63
+ $plugins[$plugin_file]['latest_version'] = $current->response[$plugin_file]->new_version;
64
+ $plugins[$plugin_file]['latest_package'] = $current->response[$plugin_file]->package;
65
  $plugins[$plugin_file]['slug'] = $current->response[$plugin_file]->slug;
66
 
67
  } else {
68
+
69
  $plugins[$plugin_file]['latest_version'] = $plugin['Version'];
70
 
71
  }
82
  * @param mixed $plugin
83
  * @return array
84
  */
85
+ function _wprp_update_plugin( $plugin_file, $args ) {
86
+ global $wprp_zip_update;
87
+
88
+ if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
89
+ return new WP_Error( 'disallow-file-mods', __( "File modification is disabled with the DISALLOW_FILE_MODS constant.", 'wpremote' ) );
90
 
91
  include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
92
  require_once ( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
94
 
95
  // check for filesystem access
96
  if ( ! _wpr_check_filesystem_access() )
97
+ return new WP_Error( 'filesystem-not-writable', __( 'The filesystem is not writable with the supplied credentials', 'wpremote' ) );
98
+
99
+ $is_active = is_plugin_active( $plugin_file );
100
+ foreach( get_plugins() as $path => $maybe_plugin ) {
101
+
102
+ if ( $path == $plugin_file ) {
103
+ $plugin = $maybe_plugin;
104
+ break;
105
+ }
106
+
107
+ }
108
+
109
+ // Permit specifying a zip URL to update the plugin with
110
+ if ( ! empty( $args['zip_url'] ) ) {
111
+
112
+ $zip_url = $args['zip_url'];
113
+
114
+ } else {
115
+
116
+ // Check to see if this is a premium plugin that supports the ManageWP implementation
117
+ $manage_wp_updates = apply_filters( 'mwp_premium_perform_update', array() );
118
+ $manage_wp_plugin_update = false;
119
+ foreach( $manage_wp_updates as $manage_wp_update ) {
120
+
121
+ if ( ! empty( $manage_wp_update['Name'] )
122
+ && $plugin['Name'] == $manage_wp_update['Name']
123
+ && ! empty( $manage_wp_update['url'] ) ) {
124
+ $zip_url = $manage_wp_update['url'];
125
+ break;
126
+ }
127
+
128
+ }
129
+
130
+ }
131
 
132
  $skin = new WPRP_Plugin_Upgrader_Skin();
133
  $upgrader = new Plugin_Upgrader( $skin );
 
134
 
135
+ // Fake out the plugin upgrader with our package url
136
+ if ( ! empty( $zip_url ) ) {
137
+ $wprp_zip_update = array(
138
+ 'plugin_file' => $plugin_file,
139
+ 'package' => $zip_url,
140
+ );
141
+ add_filter( 'pre_site_transient_update_plugins', '_wprp_forcably_filter_update_plugins' );
142
+ } else {
143
+ wp_update_plugins();
144
+ }
145
 
146
  // Do the upgrade
147
  ob_start();
148
+ $result = $upgrader->upgrade( $plugin_file );
149
  $data = ob_get_contents();
150
  ob_clean();
151
 
152
+ if ( $manage_wp_plugin_update )
153
+ remove_filter( 'pre_site_transient_update_plugins', '_wprp_forcably_filter_update_plugins' );
154
+
155
  if ( ! empty( $skin->error ) )
156
 
157
+ return new WP_Error( 'plugin-upgrader-skin', $upgrader->strings[$skin->error] );
158
 
159
  else if ( is_wp_error( $result ) )
160
 
161
+ return $result;
162
 
163
  else if ( ( ! $result && ! is_null( $result ) ) || $data )
164
 
165
+ return new WP_Error( 'plugin-update', __( 'Unknown error updating plugin.', 'wpremote' ) );
166
 
167
  // If the plugin was activited, we have to re-activate it
168
  // but if activate_plugin() fatals, then we'll just have to return 500
169
  if ( $is_active )
170
+ activate_plugin( $plugin_file, '', false, true );
171
 
172
  return array( 'status' => 'success' );
173
  }
174
 
175
+ /**
176
+ * Filter `update_plugins` to produce a response it will understand
177
+ * so we can have the Upgrader skin handle the update
178
+ */
179
+ function _wprp_forcably_filter_update_plugins() {
180
+ global $wprp_zip_update;
181
+
182
+ $current = new stdClass;
183
+ $current->response = array();
184
+
185
+ $plugin_file = $wprp_zip_update['plugin_file'];
186
+ $current->response[$plugin_file] = new stdClass;
187
+ $current->response[$plugin_file]->package = $wprp_zip_update['package'];
188
+
189
+ return $current;
190
+ }
191
+
192
  /**
193
  * Install a plugin on this site
194
  */
195
  function _wprp_install_plugin( $plugin, $args = array() ) {
196
 
197
+ if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
198
+ return new WP_Error( 'disallow-file-mods', __( "File modification is disabled with the DISALLOW_FILE_MODS constant.", 'wpremote' ) );
199
+
200
  include_once ABSPATH . 'wp-admin/includes/admin.php';
201
  include_once ABSPATH . 'wp-admin/includes/upgrade.php';
202
  include_once ABSPATH . 'wp-includes/update.php';
210
  $api = plugins_api( 'plugin_information', $api_args );
211
 
212
  if ( is_wp_error( $api ) )
213
+ return $api;
214
 
215
  $skin = new WPRP_Plugin_Upgrader_Skin();
216
  $upgrader = new Plugin_Upgrader( $skin );
222
 
223
  $result = $upgrader->install( $api->download_link );
224
  if ( is_wp_error( $result ) )
225
+ return $result;
226
  else if ( ! $result )
227
+ return new WP_Error( 'plugin-install', __( 'Unknown error installing plugin.', 'wpremote' ) );
228
 
229
  return array( 'status' => 'success' );
230
  }
236
  $result = activate_plugin( $plugin );
237
 
238
  if ( is_wp_error( $result ) )
239
+ return $result;
240
 
241
  return array( 'status' => 'success' );
242
  }
260
  function _wprp_uninstall_plugin( $plugin ) {
261
  global $wp_filesystem;
262
 
263
+ if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
264
+ return new WP_Error( 'disallow-file-mods', __( "File modification is disabled with the DISALLOW_FILE_MODS constant.", 'wpremote' ) );
265
+
266
  include_once ABSPATH . 'wp-admin/includes/admin.php';
267
  include_once ABSPATH . 'wp-admin/includes/upgrade.php';
268
  include_once ABSPATH . 'wp-includes/update.php';
269
 
270
  if ( ! _wpr_check_filesystem_access() || ! WP_Filesystem() )
271
+ return new WP_Error( 'filesystem-not-writable', __( 'The filesystem is not writable with the supplied credentials', 'wpremote' ) );
272
 
273
  $plugins_dir = $wp_filesystem->wp_plugins_dir();
274
  if ( empty( $plugins_dir ) )
275
+ return new WP_Error( 'missing-plugin-dir', __( 'Unable to locate WordPress Plugin directory.' , 'wpremote' ) );
276
 
277
  $plugins_dir = trailingslashit( $plugins_dir );
278
 
293
  }
294
  return array( 'status' => 'success' );
295
  } else {
296
+ return new WP_Error( 'plugin-uninstall', __( 'Plugin uninstalled, but not deleted.', 'wpremote' ) );
297
  }
298
 
299
  }
wprp.themes.php CHANGED
@@ -96,8 +96,11 @@ function _wprp_get_themes() {
96
  */
97
  function _wprp_install_theme( $theme, $args = array() ) {
98
 
 
 
 
99
  if ( wp_get_theme( $theme )->exists() )
100
- return array( 'status' => 'error', 'error' => 'Theme is already installed.' );
101
 
102
  include_once ABSPATH . 'wp-admin/includes/admin.php';
103
  include_once ABSPATH . 'wp-admin/includes/upgrade.php';
@@ -112,7 +115,7 @@ function _wprp_install_theme( $theme, $args = array() ) {
112
  $api = themes_api( 'theme_information', $api_args );
113
 
114
  if ( is_wp_error( $api ) )
115
- return array( 'status' => 'error', 'error' => $api->get_error_code() );
116
 
117
  $skin = new WPRP_Theme_Upgrader_Skin();
118
  $upgrader = new Theme_Upgrader( $skin );
@@ -126,7 +129,7 @@ function _wprp_install_theme( $theme, $args = array() ) {
126
  if ( is_wp_error( $result ) )
127
  return $result;
128
  else if ( ! $result )
129
- return array( 'status' => 'error', 'error' => 'Unknown error installing theme.' );
130
 
131
  return array( 'status' => 'success' );
132
  }
@@ -140,7 +143,7 @@ function _wprp_install_theme( $theme, $args = array() ) {
140
  function _wprp_activate_theme( $theme ) {
141
 
142
  if ( ! wp_get_theme( $theme )->exists() )
143
- return array( 'status' => 'error', 'error' => 'Theme is not installed.' );
144
 
145
  switch_theme( $theme );
146
  return array( 'status' => 'success' );
@@ -154,13 +157,16 @@ function _wprp_activate_theme( $theme ) {
154
  */
155
  function _wprp_update_theme( $theme ) {
156
 
 
 
 
157
  include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
158
  require_once ( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
159
  require_once WPRP_PLUGIN_PATH . 'inc/class-wprp-theme-upgrader-skin.php';
160
 
161
  // check for filesystem access
162
  if ( ! _wpr_check_filesystem_access() )
163
- return array( 'status' => 'error', 'error' => 'The filesystem is not writable with the supplied credentials' );
164
 
165
  $skin = new WPRP_Theme_Upgrader_Skin();
166
  $upgrader = new Theme_Upgrader( $skin );
@@ -171,14 +177,17 @@ function _wprp_update_theme( $theme ) {
171
  $data = ob_get_contents();
172
  ob_clean();
173
 
174
- if ( ( ! $result && ! is_null( $result ) ) || $data )
175
- return array( 'status' => 'error', 'error' => 'file_permissions_error' );
176
 
177
- elseif ( is_wp_error( $result ) )
178
- return array( 'status' => 'error', 'error' => $result->get_error_code() );
179
 
180
- if ( $skin->error )
181
- return array( 'status' => 'error', 'error' => $skin->error );
 
 
 
 
 
182
 
183
  return array( 'status' => 'success' );
184
 
@@ -193,26 +202,29 @@ function _wprp_update_theme( $theme ) {
193
  function _wprp_delete_theme( $theme ) {
194
  global $wp_filesystem;
195
 
 
 
 
196
  if ( ! wp_get_theme( $theme )->exists() )
197
- return array( 'status' => 'error', 'error' => 'Theme is not installed.' );
198
 
199
  include_once ABSPATH . 'wp-admin/includes/admin.php';
200
  include_once ABSPATH . 'wp-admin/includes/upgrade.php';
201
  include_once ABSPATH . 'wp-includes/update.php';
202
 
203
  if ( ! _wpr_check_filesystem_access() || ! WP_Filesystem() )
204
- return array( 'status' => 'error', 'error' => 'The filesystem is not writable with the supplied credentials' );
205
 
206
  $themes_dir = $wp_filesystem->wp_themes_dir();
207
  if ( empty( $themes_dir ) )
208
- return array( 'status' => 'error', 'error' => 'Unable to locate WordPress theme directory.' );
209
 
210
  $themes_dir = trailingslashit( $themes_dir );
211
  $theme_dir = trailingslashit( $themes_dir . $theme );
212
  $deleted = $wp_filesystem->delete( $theme_dir, true );
213
 
214
  if ( ! $deleted )
215
- return array( 'status' => 'error', 'error' => sprintf( 'Could not fully delete the theme: %s.', $theme ) );
216
 
217
  // Force refresh of theme update information
218
  delete_site_transient('update_themes');
96
  */
97
  function _wprp_install_theme( $theme, $args = array() ) {
98
 
99
+ if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
100
+ return new WP_Error( 'disallow-file-mods', __( "File modification is disabled with the DISALLOW_FILE_MODS constant.", 'wpremote' ) );
101
+
102
  if ( wp_get_theme( $theme )->exists() )
103
+ return new WP_Error( 'theme-installed', __( 'Theme is already installed.' ) );
104
 
105
  include_once ABSPATH . 'wp-admin/includes/admin.php';
106
  include_once ABSPATH . 'wp-admin/includes/upgrade.php';
115
  $api = themes_api( 'theme_information', $api_args );
116
 
117
  if ( is_wp_error( $api ) )
118
+ return $api;
119
 
120
  $skin = new WPRP_Theme_Upgrader_Skin();
121
  $upgrader = new Theme_Upgrader( $skin );
129
  if ( is_wp_error( $result ) )
130
  return $result;
131
  else if ( ! $result )
132
+ return new WP_Error( 'unknown-install-error', __( 'Unknown error installing theme.', 'wpremote' ) );
133
 
134
  return array( 'status' => 'success' );
135
  }
143
  function _wprp_activate_theme( $theme ) {
144
 
145
  if ( ! wp_get_theme( $theme )->exists() )
146
+ return new WP_Error( 'theme-not-installed', __( 'Theme is not installed.', 'wpremote' ) );
147
 
148
  switch_theme( $theme );
149
  return array( 'status' => 'success' );
157
  */
158
  function _wprp_update_theme( $theme ) {
159
 
160
+ if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
161
+ return new WP_Error( 'disallow-file-mods', __( "File modification is disabled with the DISALLOW_FILE_MODS constant.", 'wpremote' ) );
162
+
163
  include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
164
  require_once ( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
165
  require_once WPRP_PLUGIN_PATH . 'inc/class-wprp-theme-upgrader-skin.php';
166
 
167
  // check for filesystem access
168
  if ( ! _wpr_check_filesystem_access() )
169
+ return new WP_Error( 'filesystem-not-writable', __( 'The filesystem is not writable with the supplied credentials', 'wpremote' ) );
170
 
171
  $skin = new WPRP_Theme_Upgrader_Skin();
172
  $upgrader = new Theme_Upgrader( $skin );
177
  $data = ob_get_contents();
178
  ob_clean();
179
 
180
+ if ( ! empty( $skin->error ) )
 
181
 
182
+ return new WP_Error( 'theme-upgrader-skin', $upgrader->strings[$skin->error] );
 
183
 
184
+ else if ( is_wp_error( $result ) )
185
+
186
+ return $result;
187
+
188
+ else if ( ( ! $result && ! is_null( $result ) ) || $data )
189
+
190
+ return new WP_Error( 'theme-update', __( 'Unknown error updating theme.', 'wpremote' ) );
191
 
192
  return array( 'status' => 'success' );
193
 
202
  function _wprp_delete_theme( $theme ) {
203
  global $wp_filesystem;
204
 
205
+ if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
206
+ return new WP_Error( 'disallow-file-mods', __( "File modification is disabled with the DISALLOW_FILE_MODS constant.", 'wpremote' ) );
207
+
208
  if ( ! wp_get_theme( $theme )->exists() )
209
+ return new WP_Error( 'theme-missing', __( 'Theme is not installed.', 'wpremote' ) );
210
 
211
  include_once ABSPATH . 'wp-admin/includes/admin.php';
212
  include_once ABSPATH . 'wp-admin/includes/upgrade.php';
213
  include_once ABSPATH . 'wp-includes/update.php';
214
 
215
  if ( ! _wpr_check_filesystem_access() || ! WP_Filesystem() )
216
+ return new WP_Error( 'filesystem-not-writable', __( 'The filesystem is not writable with the supplied credentials', 'wpremote' ) );
217
 
218
  $themes_dir = $wp_filesystem->wp_themes_dir();
219
  if ( empty( $themes_dir ) )
220
+ return new WP_Error( 'theme-dir-missing', __( 'Unable to locate WordPress theme directory', 'wpremote' ) );
221
 
222
  $themes_dir = trailingslashit( $themes_dir );
223
  $theme_dir = trailingslashit( $themes_dir . $theme );
224
  $deleted = $wp_filesystem->delete( $theme_dir, true );
225
 
226
  if ( ! $deleted )
227
+ return new WP_Error( 'theme-delete', sprintf( __( 'Could not fully delete the theme: %s.', 'wpremote' ), $theme ) );
228
 
229
  // Force refresh of theme update information
230
  delete_site_transient('update_themes');