The WP Remote WordPress Plugin - Version 2.0

Version Description

Download this release

Release Info

Developer willmot
Plugin Icon 128x128 The WP Remote WordPress Plugin
Version 2.0
Comparing to
See all releases

Code changes from version 0.6.6 to 2.0

Files changed (12) hide show
  1. hm-backup/hm-backup.php +1233 -0
  2. plugin.php +81 -17
  3. readme.txt +28 -7
  4. screenshot-1.png +0 -0
  5. wpr.admin.php +2 -0
  6. wpr.api.php +225 -53
  7. wprp.admin.php +81 -0
  8. wprp.api.php +87 -0
  9. wprp.backups.php +57 -0
  10. wprp.plugin.php +126 -0
  11. wprp.plugins.php +116 -0
  12. wprp.themes.php +106 -0
hm-backup/hm-backup.php ADDED
@@ -0,0 +1,1233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Generic file and database backup class
5
+ *
6
+ * @version 1.3
7
+ */
8
+ class HM_Backup {
9
+
10
+ /**
11
+ * The path where the backup file should be saved
12
+ *
13
+ * @string
14
+ * @access public
15
+ */
16
+ public $path;
17
+
18
+ /**
19
+ * Whether the backup should be files only
20
+ *
21
+ * @bool
22
+ * @access public
23
+ */
24
+ public $files_only;
25
+
26
+ /**
27
+ * Whether the backup should be database only
28
+ *
29
+ * @bool
30
+ * @access public
31
+ */
32
+ public $database_only;
33
+
34
+ /**
35
+ * The filename of the backup file
36
+ *
37
+ * @string
38
+ * @access public
39
+ */
40
+ public $archive_filename;
41
+
42
+ /**
43
+ * The filename of the database dump
44
+ *
45
+ * @string
46
+ * @access public
47
+ */
48
+ public $database_dump_filename;
49
+
50
+ /**
51
+ * The path to the zip command
52
+ *
53
+ * @string
54
+ * @access public
55
+ */
56
+ public $zip_command_path;
57
+
58
+ /**
59
+ * The path to the mysqldump command
60
+ *
61
+ * @string
62
+ * @access public
63
+ */
64
+ public $mysqldump_command_path;
65
+
66
+ /**
67
+ * An array of exclude rules
68
+ *
69
+ * @array
70
+ * @access public
71
+ */
72
+ public $excludes;
73
+
74
+ /**
75
+ * The path that should be backed up
76
+ *
77
+ * @var string
78
+ * @access public
79
+ */
80
+ public $root;
81
+
82
+ /**
83
+ * Holds the current db connection
84
+ *
85
+ * @var resource
86
+ * @access private
87
+ */
88
+ private $db;
89
+
90
+ /**
91
+ * Store the current backup instance
92
+ *
93
+ * @var object
94
+ * @static
95
+ * @access public
96
+ */
97
+ private static $instance;
98
+
99
+ /**
100
+ * An array of all the files in root
101
+ * excluding excludes
102
+ *
103
+ * @var array
104
+ * @access private
105
+ */
106
+ private $files;
107
+
108
+ /**
109
+ * Contains an array of errors
110
+ *
111
+ * @var mixed
112
+ * @access private
113
+ */
114
+ private $errors;
115
+
116
+ /**
117
+ * Contains an array of warnings
118
+ *
119
+ * @var mixed
120
+ * @access private
121
+ */
122
+ private $warnings;
123
+
124
+ /**
125
+ * The archive method used
126
+ *
127
+ * @var string
128
+ * @access private
129
+ */
130
+ private $archive_method;
131
+
132
+ /**
133
+ * The mysqldump method used
134
+ *
135
+ * @var string
136
+ * @access private
137
+ */
138
+ private $mysqldump_method;
139
+
140
+ /**
141
+ * Sets up the default properties
142
+ *
143
+ * @access public
144
+ * @return null
145
+ */
146
+ public function __construct() {
147
+
148
+ // Raise the memory limit and max_execution_time time
149
+ @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
150
+ @set_time_limit( 0 );
151
+
152
+ $this->errors = array();
153
+
154
+ set_error_handler( array( &$this, 'error_handler' ) );
155
+
156
+ // Defaults
157
+ $this->root = $this->conform_dir( ABSPATH );
158
+
159
+ $this->path = $this->conform_dir( WP_CONTENT_DIR . '/backups' );
160
+
161
+ $this->database_dump_filename = 'database_' . DB_NAME . '.sql';
162
+
163
+ $this->archive_filename = strtolower( sanitize_file_name( get_bloginfo( 'name' ) . '.backup.' . date( 'Y-m-d-H-i-s', time() + ( current_time( 'timestamp' ) - time() ) ) . '.zip' ) );
164
+
165
+ $this->mysqldump_command_path = $this->guess_mysqldump_command_path();
166
+ $this->zip_command_path = $this->guess_zip_command_path();
167
+
168
+ $this->database_only = false;
169
+ $this->files_only = false;
170
+
171
+ }
172
+
173
+ /**
174
+ * Return the current instance
175
+ *
176
+ * @access public
177
+ * @static
178
+ * @return object
179
+ */
180
+ public static function get_instance() {
181
+
182
+ if ( empty( self::$instance ) )
183
+ self::$instance = new HM_Backup();
184
+
185
+ return self::$instance;
186
+
187
+ }
188
+
189
+ /**
190
+ * The full filepath to the archive file.
191
+ *
192
+ * @access public
193
+ * @return string
194
+ */
195
+ public function archive_filepath() {
196
+ return trailingslashit( $this->path() ) . $this->archive_filename();
197
+ }
198
+
199
+ /**
200
+ * The full filepath to the archive file.
201
+ *
202
+ * @access public
203
+ * @return string
204
+ */
205
+ public function archive_filename() {
206
+ return strtolower( sanitize_file_name( remove_accents( $this->archive_filename ) ) );
207
+ }
208
+
209
+ /**
210
+ * The full filepath to the database dump file.
211
+ *
212
+ * @access public
213
+ * @return string
214
+ */
215
+ public function database_dump_filepath() {
216
+ return trailingslashit( $this->path() ) . $this->database_dump_filename();
217
+ }
218
+
219
+ public function database_dump_filename() {
220
+ return strtolower( sanitize_file_name( remove_accents( $this->database_dump_filename ) ) );
221
+ }
222
+
223
+ public function root() {
224
+ return $this->conform_dir( $this->root );
225
+ }
226
+
227
+ public function path() {
228
+ return $this->conform_dir( $this->path );
229
+ }
230
+
231
+ public function archive_method() {
232
+ return $this->archive_method;
233
+ }
234
+
235
+ public function mysqldump_method() {
236
+ return $this->mysqldump_method;
237
+ }
238
+
239
+ /**
240
+ * Kick off a backup
241
+ *
242
+ * @access public
243
+ * @return bool
244
+ */
245
+ public function backup() {
246
+
247
+ do_action( 'hmbkp_backup_started', $this );
248
+
249
+ // Backup database
250
+ if ( ! $this->files_only )
251
+ $this->mysqldump();
252
+
253
+ // Zip everything up
254
+ $this->archive();
255
+
256
+ do_action( 'hmbkp_backup_complete', $this );
257
+
258
+ }
259
+
260
+ /**
261
+ * Create the mysql backup
262
+ *
263
+ * Uses mysqldump if available, falls back to PHP
264
+ * if not.
265
+ *
266
+ * @access public
267
+ * @return null
268
+ */
269
+ public function mysqldump() {
270
+
271
+ do_action( 'hmbkp_mysqldump_started' );
272
+
273
+ $this->mysqldump_method = 'mysqldump';
274
+
275
+ // Use mysqldump if we can
276
+ if ( $this->mysqldump_command_path ) {
277
+
278
+ // Path to the mysqldump executable
279
+ $cmd = escapeshellarg( $this->mysqldump_command_path );
280
+
281
+ // No Create DB command
282
+ $cmd .= ' --no-create-db';
283
+
284
+ // Make sure binary data is exported properly
285
+ $cmd .= ' --hex-blob';
286
+
287
+ // Username
288
+ $cmd .= ' -u ' . escapeshellarg( DB_USER );
289
+
290
+ // Don't pass the password if it's blank
291
+ if ( DB_PASSWORD )
292
+ $cmd .= ' -p' . escapeshellarg( DB_PASSWORD );
293
+
294
+ // Set the host
295
+ $cmd .= ' -h ' . escapeshellarg( DB_HOST );
296
+
297
+ // Save the file
298
+ $cmd .= ' -r ' . escapeshellarg( $this->database_dump_filepath() );
299
+
300
+ // The database we're dumping
301
+ $cmd .= ' ' . escapeshellarg( DB_NAME );
302
+
303
+ // Send stdout to null
304
+ $cmd .= ' 2> /dev/null';
305
+
306
+ shell_exec( $cmd );
307
+
308
+ }
309
+
310
+ // If not or if the shell mysqldump command failed, use the PHP fallback
311
+ if ( ! file_exists( $this->database_dump_filepath() ) )
312
+ $this->mysqldump_fallback();
313
+
314
+ do_action( 'hmbkp_mysqldump_finished' );
315
+
316
+ }
317
+
318
+ /**
319
+ * PHP mysqldump fallback functions, exports the database to a .sql file
320
+ *
321
+ * @access public
322
+ * @return null
323
+ */
324
+ public function mysqldump_fallback() {
325
+
326
+ $this->mysqldump_method = 'mysqldump_fallback';
327
+
328
+ $this->db = mysql_pconnect( DB_HOST, DB_USER, DB_PASSWORD );
329
+
330
+ mysql_select_db( DB_NAME, $this->db );
331
+ mysql_set_charset( DB_CHARSET, $this->db );
332
+
333
+ // Begin new backup of MySql
334
+ $tables = mysql_query( 'SHOW TABLES' );
335
+
336
+ $sql_file = "# WordPress : " . get_bloginfo( 'url' ) . " MySQL database backup\n";
337
+ $sql_file .= "#\n";
338
+ $sql_file .= "# Generated: " . date( 'l j. F Y H:i T' ) . "\n";
339
+ $sql_file .= "# Hostname: " . DB_HOST . "\n";
340
+ $sql_file .= "# Database: " . $this->sql_backquote( DB_NAME ) . "\n";
341
+ $sql_file .= "# --------------------------------------------------------\n";
342
+
343
+ for ( $i = 0; $i < mysql_num_rows( $tables ); $i++ ) {
344
+
345
+ $curr_table = mysql_tablename( $tables, $i );
346
+
347
+ // Create the SQL statements
348
+ $sql_file .= "# --------------------------------------------------------\n";
349
+ $sql_file .= "# Table: " . $this->sql_backquote( $curr_table ) . "\n";
350
+ $sql_file .= "# --------------------------------------------------------\n";
351
+
352
+ $this->make_sql( $sql_file, $curr_table );
353
+
354
+ }
355
+
356
+ }
357
+
358
+ /**
359
+ * Zip up all the files.
360
+ *
361
+ * Attempts to use the shell zip command, if
362
+ * thats not available then it fallsback to
363
+ * PHP ZipArchive and finally PclZip.
364
+ *
365
+ * @access public
366
+ * @return null
367
+ */
368
+ public function archive() {
369
+
370
+ do_action( 'hmbkp_archive_started' );
371
+
372
+ // Do we have the path to the zip command
373
+ if ( $this->zip_command_path )
374
+ $this->zip();
375
+
376
+ // If not or if the shell zip failed then use ZipArchive
377
+ if ( ( empty( $this->archive_verified ) || $this->errors() ) && class_exists( 'ZipArchive' ) && empty( $this->skip_zip_archive ) )
378
+ $this->zip_archive();
379
+
380
+ // If ZipArchive is unavailable or one of the above failed
381
+ if ( ( empty( $this->archive_verified ) || $this->errors() ) )
382
+ $this->pcl_zip();
383
+
384
+ // Delete the database dump file
385
+ if ( file_exists( $this->database_dump_filepath() ) )
386
+ unlink( $this->database_dump_filepath() );
387
+
388
+ do_action( 'hmbkp_archive_finished' );
389
+
390
+ }
391
+
392
+ /**
393
+ * Zip using the native zip command
394
+ *
395
+ * @access public
396
+ * @return null
397
+ */
398
+ public function zip() {
399
+
400
+ $this->archive_method = 'zip';
401
+
402
+ // Zip up $this->root with excludes
403
+ if ( ! $this->database_only && $this->exclude_string( 'zip' ) )
404
+ $this->error( 'zip', shell_exec( 'cd ' . escapeshellarg( $this->root() ) . ' && ' . escapeshellarg( $this->zip_command_path ) . ' -rq ' . escapeshellarg( $this->archive_filepath() ) . ' ./' . ' -x ' . $this->exclude_string( 'zip' ) . ' 2>&1' ) );
405
+
406
+ // Zip up $this->root without excludes
407
+ elseif ( ! $this->database_only )
408
+ $this->error( 'zip', shell_exec( 'cd ' . escapeshellarg( $this->root() ) . ' && ' . escapeshellarg( $this->zip_command_path ) . ' -rq ' . escapeshellarg( $this->archive_filepath() ) . ' ./' . ' 2>&1' ) );
409
+
410
+ // Add the database dump to the archive
411
+ if ( ! $this->files_only )
412
+ $this->error( 'zip', shell_exec( 'cd ' . escapeshellarg( $this->path() ) . ' && ' . escapeshellarg( $this->zip_command_path ) . ' -uq ' . escapeshellarg( $this->archive_filepath() ) . ' ' . escapeshellarg( $this->database_dump_filename() ) . ' 2>&1' ) );
413
+
414
+ $this->check_archive();
415
+
416
+ }
417
+
418
+ /**
419
+ * Fallback for creating zip archives if zip command is
420
+ * unnavailable.
421
+ *
422
+ * @access public
423
+ * @param string $path
424
+ */
425
+ public function zip_archive() {
426
+
427
+ $this->archive_method = 'ziparchive';
428
+
429
+ $zip = new ZipArchive();
430
+
431
+ if ( ! class_exists( 'ZipArchive' ) || ! $zip->open( $this->archive_filepath(), ZIPARCHIVE::CREATE ) )
432
+ return;
433
+
434
+ if ( ! $this->database_only ) {
435
+
436
+ $files_added = 0;
437
+
438
+ foreach ( $this->files() as $file ) {
439
+
440
+ if ( is_dir( trailingslashit( $this->root() ) . $file ) )
441
+ $zip->addEmptyDir( trailingslashit( $file ) );
442
+
443
+ elseif ( is_file( trailingslashit( $this->root() ) . $file ) )
444
+ $zip->addFile( trailingslashit( $this->root() ) . $file, $file );
445
+
446
+ if ( ++$files_added % 500 === 0 )
447
+ if ( ! $zip->close() || ! $zip->open( $this->archive_filepath(), ZIPARCHIVE::CREATE ) )
448
+ return;
449
+
450
+ }
451
+
452
+ }
453
+
454
+ // Add the database
455
+ if ( ! $this->files_only )
456
+ $zip->addFile( $this->database_dump_filepath(), $this->database_dump_filename() );
457
+
458
+ if ( $zip->status )
459
+ $this->error( 'ziparchive', $zip->status );
460
+
461
+ if ( $zip->statusSys )
462
+ $this->error( 'ziparchive', $zip->statusSys );
463
+
464
+ $zip->close();
465
+
466
+ $this->check_archive();
467
+
468
+ }
469
+
470
+ /**
471
+ * Fallback for creating zip archives if zip command and ZipArchive are
472
+ * unnavailable.
473
+ *
474
+ * Uses the PclZip library that ships with WordPress
475
+ *
476
+ * @access public
477
+ * @param string $path
478
+ */
479
+ public function pcl_zip() {
480
+
481
+ $this->archive_method = 'pclzip';
482
+
483
+ global $_hmbkp_exclude_string;
484
+
485
+ $_hmbkp_exclude_string = $this->exclude_string( 'regex' );
486
+
487
+ $this->load_pclzip();
488
+
489
+ $archive = new PclZip( $this->archive_filepath() );
490
+
491
+ // Zip up everything
492
+ if ( ! $this->database_only )
493
+ if ( ! $archive->add( $this->root(), PCLZIP_OPT_REMOVE_PATH, $this->root(), PCLZIP_CB_PRE_ADD, 'hmbkp_pclzip_callback' ) )
494
+ $this->error( 'pclzip', $archive->errorInfo( true ) );
495
+
496
+ // Add the database
497
+ if ( ! $this->files_only )
498
+ if ( ! $archive->add( $this->database_dump_filepath(), PCLZIP_OPT_REMOVE_PATH, $this->path() ) )
499
+ $this->error( 'pclzip', $archive->errorInfo( true ) );
500
+
501
+ unset( $GLOBALS['_hmbkp_exclude_string'] );
502
+
503
+ $this->check_archive();
504
+
505
+ }
506
+
507
+ /**
508
+ * Verify that the archive is valid and contains all the files it should contain.
509
+ *
510
+ * @access public
511
+ * @return bool
512
+ */
513
+ public function check_archive() {
514
+
515
+ // If we've already passed then no need to check again
516
+ if ( ! empty( $this->archive_verified ) )
517
+ return true;
518
+
519
+ if ( ! file_exists( $this->archive_filepath() ) )
520
+ $this->error( $this->archive_method(), __( 'The backup file was not created', 'hmbkp' ) );
521
+
522
+ // Verify using the zip command if possible
523
+ if ( ! $this->errors() && $this->zip_command_path ) {
524
+
525
+ $verify = shell_exec( escapeshellarg( $this->zip_command_path ) . ' -T ' . escapeshellarg( $this->archive_filepath() ) . ' 2> /dev/null' );
526
+
527
+ if ( strpos( $verify, 'OK' ) === false )
528
+ $this->error( $this->archive_method(), $verify );
529
+
530
+ }
531
+
532
+ if ( ! $this->errors() ) {
533
+
534
+ // If it's a file backup, get an array of all the files that should have been backed up
535
+ if ( ! $this->database_only )
536
+ $files = $this->files();
537
+
538
+ // Check that the database was backed up
539
+ if ( ! $this->files_only )
540
+ $files[] = $this->database_dump_filename();
541
+
542
+ $this->load_pclzip();
543
+
544
+ $archive = new PclZip( $this->archive_filepath() );
545
+ $filesystem = $archive->extract( PCLZIP_OPT_EXTRACT_AS_STRING );
546
+
547
+ foreach( $filesystem as $file )
548
+ $archive_files[] = untrailingslashit( $file['filename'] );
549
+
550
+ // Check that the array of files that should have been backed up matches the array of files in the zip
551
+ if ( $files !== $archive_files )
552
+ $this->error( $this->archive_method(), __( 'Backup file doesn\'t contain the the following files: ', 'hmbkp' ) . implode( ', ', array_diff( $files, $archive_files ) ) );
553
+
554
+ }
555
+
556
+ // If there are errors delete the backup file.
557
+ if ( $this->errors() && file_exists( $this->archive_filepath() ) )
558
+ unlink( $this->archive_filepath() );
559
+
560
+ if ( $this->errors() )
561
+ return false;
562
+
563
+ return $this->archive_verified = true;
564
+
565
+ }
566
+
567
+ /**
568
+ * Generate the array of files to be backed up by looping through
569
+ * root, ignored unreadable files and excludes
570
+ *
571
+ * @access public
572
+ * @return array
573
+ */
574
+ public function files() {
575
+
576
+ if ( ! empty( $this->files ) )
577
+ return $this->files;
578
+
579
+ $this->files = array();
580
+
581
+ if ( defined( 'RecursiveDirectoryIterator::FOLLOW_SYMLINKS' ) ) {
582
+
583
+ $filesystem = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $this->root(), RecursiveDirectoryIterator::FOLLOW_SYMLINKS ), RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD );
584
+
585
+ $excludes = $this->exclude_string( 'regex' );
586
+
587
+ foreach ( $filesystem as $file ) {
588
+
589
+ if ( ! $file->isReadable() ) {
590
+ $this->unreadable_files[] = $file->getPathName();
591
+ continue;
592
+ }
593
+
594
+ $pathname = str_ireplace( trailingslashit( $this->root() ), '', $this->conform_dir( $file->getPathname() ) );
595
+
596
+ // Excludes
597
+ if ( $excludes && preg_match( '(' . $excludes . ')', $pathname ) )
598
+ continue;
599
+
600
+ // Don't include database dump as it's added separately
601
+ if ( basename( $pathname ) == $this->database_dump_filename() )
602
+ continue;
603
+
604
+ $this->files[] = $pathname;
605
+
606
+ }
607
+
608
+ } else {
609
+
610
+ $this->files = $this->files_fallback( $this->root() );
611
+
612
+ }
613
+
614
+ if ( ! empty( $this->unreadable_files ) )
615
+ $this->warning( $this->archive_method(), __( 'The following files are unreadable and couldn\'t be backed up: ', 'hmbkp' ) . implode( ', ', $this->unreadable_files ) );
616
+
617
+ return $this->files;
618
+
619
+ }
620
+
621
+ /**
622
+ * Fallback function for generating a filesystem
623
+ * array
624
+ *
625
+ * Used if RecursiveDirectoryIterator::FOLLOW_SYMLINKS isn't available
626
+ *
627
+ * @access private
628
+ * @param stromg $dir
629
+ * @param array $files. (default: array())
630
+ * @return array
631
+ */
632
+ private function files_fallback( $dir, $files = array() ) {
633
+
634
+ $handle = opendir( $dir );
635
+
636
+ $excludes = $this->exclude_string( 'regex' );
637
+
638
+ while ( $file = readdir( $handle ) ) :
639
+
640
+ // Ignore current dir and containing dir and any unreadable files or directories
641
+ if ( $file == '.' || $file == '..' )
642
+ continue;
643
+
644
+ $filepath = $this->conform_dir( trailingslashit( $dir ) . $file );
645
+ $file = str_ireplace( trailingslashit( $this->root() ), '', $filepath );
646
+
647
+ if ( ! is_readable( $filepath ) ) {
648
+ $this->unreadable_files[] = $filepath;
649
+ continue;
650
+ }
651
+
652
+ // Skip the backups dir and any excluded paths
653
+ if ( ( $excludes && preg_match( '(' . $excludes . ')', $file ) ) )
654
+ continue;
655
+
656
+ $files[] = $file;
657
+
658
+ if ( is_dir( $filepath ) )
659
+ $files = $this->files_fallback( $filepath, $files );
660
+
661
+ endwhile;
662
+
663
+ return $files;
664
+
665
+ }
666
+
667
+ private function load_pclzip() {
668
+
669
+ // Load PclZip
670
+ if ( ! defined( 'PCLZIP_TEMPORARY_DIR' ) )
671
+ define( 'PCLZIP_TEMPORARY_DIR', $this->path() );
672
+
673
+ require_once( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
674
+
675
+ }
676
+
677
+ /**
678
+ * Attempt to work out the path to mysqldump
679
+ *
680
+ * @access public
681
+ * @return bool
682
+ */
683
+ public function guess_mysqldump_command_path() {
684
+
685
+ if ( ! $this->shell_exec_available() )
686
+ return '';
687
+
688
+ // List of possible mysqldump locations
689
+ $mysqldump_locations = array(
690
+ 'mysqldump',
691
+ '/usr/local/bin/mysqldump',
692
+ '/usr/local/mysql/bin/mysqldump',
693
+ '/usr/mysql/bin/mysqldump',
694
+ '/usr/bin/mysqldump',
695
+ '/opt/local/lib/mysql6/bin/mysqldump',
696
+ '/opt/local/lib/mysql5/bin/mysqldump',
697
+ '/opt/local/lib/mysql4/bin/mysqldump',
698
+ '\xampp\mysql\bin\mysqldump',
699
+ '\Program Files\xampp\mysql\bin\mysqldump',
700
+ '\Program Files\MySQL\MySQL Server 6.0\bin\mysqldump',
701
+ '\Program Files\MySQL\MySQL Server 5.5\bin\mysqldump',
702
+ '\Program Files\MySQL\MySQL Server 5.4\bin\mysqldump',
703
+ '\Program Files\MySQL\MySQL Server 5.1\bin\mysqldump',
704
+ '\Program Files\MySQL\MySQL Server 5.0\bin\mysqldump',
705
+ '\Program Files\MySQL\MySQL Server 4.1\bin\mysqldump'
706
+ );
707
+
708
+ // Find the one which works
709
+ foreach ( $mysqldump_locations as $location )
710
+ if ( ! shell_exec( 'hash ' . $location . ' 2>&1' ) )
711
+ return $location;
712
+
713
+ return '';
714
+
715
+ }
716
+
717
+ /**
718
+ * Attempt to work out the path to the zip command
719
+ *
720
+ * @access public
721
+ * @return bool
722
+ */
723
+ public function guess_zip_command_path() {
724
+
725
+ // Check shell_exec is available and hasn't been explicitly bypassed
726
+ if ( ! $this->shell_exec_available() )
727
+ return '';
728
+
729
+ // List of possible zip locations
730
+ $zip_locations = array(
731
+ 'zip',
732
+ '/usr/bin/zip'
733
+ );
734
+
735
+ // Find the one which works
736
+ foreach ( $zip_locations as $location )
737
+ if ( ! shell_exec( 'hash ' . $location . ' 2>&1' ) )
738
+ return $location;
739
+
740
+ return '';
741
+
742
+ }
743
+
744
+ /**
745
+ * Generate the exclude param string for the zip backup
746
+ *
747
+ * Takes the exclude rules and formats them for use with either
748
+ * the shell zip command or pclzip
749
+ *
750
+ * @access public
751
+ * @param string $context. (default: 'zip')
752
+ * @return string
753
+ */
754
+ public function exclude_string( $context = 'zip' ) {
755
+
756
+ // Return a comma separated list by default
757
+ $separator = ', ';
758
+ $wildcard = '';
759
+
760
+ // The zip command
761
+ if ( $context == 'zip' ) {
762
+ $wildcard = '*';
763
+ $separator = ' -x ';
764
+
765
+ // The PclZip fallback library
766
+ } elseif ( $context == 'regex' ) {
767
+ $wildcard = '([\s\S]*?)';
768
+ $separator = '|';
769
+
770
+ }
771
+
772
+ // Sanitize the excludes
773
+ $excludes = array_filter( array_unique( array_map( 'trim', (array) $this->excludes ) ) );
774
+
775
+ // If path() is inside root(), exclude it
776
+ if ( strpos( $this->path(), $this->root() ) !== false )
777
+ $excludes[] = trailingslashit( $this->path() );
778
+
779
+ foreach( $excludes as $key => &$rule ) {
780
+
781
+ $file = $absolute = $fragment = false;
782
+
783
+ // Files don't end with /
784
+ if ( ! in_array( substr( $rule, -1 ), array( '\\', '/' ) ) )
785
+ $file = true;
786
+
787
+ // If rule starts with a / then treat as absolute path
788
+ elseif ( in_array( substr( $rule, 0, 1 ), array( '\\', '/' ) ) )
789
+ $absolute = true;
790
+
791
+ // Otherwise treat as dir fragment
792
+ else
793
+ $fragment = true;
794
+
795
+ // Strip $this->root and conform
796
+ $rule = str_ireplace( $this->root(), '', untrailingslashit( $this->conform_dir( $rule ) ) );
797
+
798
+ // Strip the preceeding slash
799
+ if ( in_array( substr( $rule, 0, 1 ), array( '\\', '/' ) ) )
800
+ $rule = substr( $rule, 1 );
801
+
802
+ // Escape string for regex
803
+ if ( $context == 'regex' )
804
+ $rule = str_replace( '.', '\.', $rule );
805
+
806
+ // Convert any existing wildcards
807
+ if ( $wildcard != '*' && strpos( $rule, '*' ) !== false )
808
+ $rule = str_replace( '*', $wildcard, $rule );
809
+
810
+ // Wrap directory fragments and files in wildcards for zip
811
+ if ( $context == 'zip' && ( $fragment || $file ) )
812
+ $rule = $wildcard . $rule . $wildcard;
813
+
814
+ // Add a wildcard to the end of absolute url for zips
815
+ if ( $context == 'zip' && $absolute )
816
+ $rule .= $wildcard;
817
+
818
+ // Add and end carrot to files for pclzip but only if it doesn't end in a wildcard
819
+ if ( $file && $context == 'regex' )
820
+ $rule .= '$';
821
+
822
+ // Add a start carrot to absolute urls for pclzip
823
+ if ( $absolute && $context == 'regex' )
824
+ $rule = '^' . $rule;
825
+
826
+ }
827
+
828
+ // Escape shell args for zip command
829
+ if ( $context == 'zip' )
830
+ $excludes = array_map( 'escapeshellarg', array_unique( $excludes ) );
831
+
832
+ return implode( $separator, $excludes );
833
+
834
+ }
835
+
836
+ /**
837
+ * Check whether safe mode is active or not
838
+ *
839
+ * @access private
840
+ * @return bool
841
+ */
842
+ public function is_safe_mode_active() {
843
+
844
+ if ( $safe_mode = ini_get( 'safe_mode' ) && strtolower( $safe_mode ) != 'off' )
845
+ return true;
846
+
847
+ return false;
848
+
849
+ }
850
+
851
+ /**
852
+ * Check whether shell_exec has been disabled.
853
+ *
854
+ * @access private
855
+ * @return bool
856
+ */
857
+ private function shell_exec_available() {
858
+
859
+ // Are we in Safe Mode
860
+ if ( $this->is_safe_mode_active() )
861
+ return false;
862
+
863
+ // Is shell_exec disabled?
864
+ if ( in_array( 'shell_exec', array_map( 'trim', explode( ',', ini_get( 'disable_functions' ) ) ) ) )
865
+ return false;
866
+
867
+ return true;
868
+
869
+ }
870
+
871
+ /**
872
+ * Sanitize a directory path
873
+ *
874
+ * @access public
875
+ * @param string $dir
876
+ * @param bool $rel. (default: false)
877
+ * @return string $dir
878
+ */
879
+ public function conform_dir( $dir, $recursive = false ) {
880
+
881
+ // Replace single forward slash (looks like double slash because we have to escape it)
882
+ $dir = str_replace( '\\', '/', $dir );
883
+ $dir = str_replace( '//', '/', $dir );
884
+
885
+ // Remove the trailing slash
886
+ $dir = untrailingslashit( $dir );
887
+
888
+ // Carry on until completely normalized
889
+ if ( ! $recursive && $this->conform_dir( $dir, true ) != $dir )
890
+ return $this->conform_dir( $dir );
891
+
892
+ return $dir;
893
+ }
894
+
895
+ /**
896
+ * Add backquotes to tables and db-names inSQL queries. Taken from phpMyAdmin.
897
+ *
898
+ * @access private
899
+ * @param mixed $a_name
900
+ */
901
+ private function sql_backquote( $a_name ) {
902
+
903
+ if ( ! empty( $a_name ) && $a_name != '*' ) {
904
+
905
+ if ( is_array( $a_name ) ) {
906
+
907
+ $result = array();
908
+
909
+ reset( $a_name );
910
+
911
+ while ( list( $key, $val ) = each( $a_name ) )
912
+ $result[$key] = '`' . $val . '`';
913
+
914
+ return $result;
915
+
916
+ } else {
917
+
918
+ return '`' . $a_name . '`';
919
+
920
+ }
921
+
922
+ } else {
923
+
924
+ return $a_name;
925
+
926
+ }
927
+
928
+ }
929
+
930
+ /**
931
+ * Reads the Database table in $table and creates
932
+ * SQL Statements for recreating structure and data
933
+ * Taken partially from phpMyAdmin and partially from
934
+ * Alain Wolf, Zurich - Switzerland
935
+ * Website: http://restkultur.ch/personal/wolf/scripts/db_backup/
936
+ *
937
+ * @access private
938
+ * @param string $sql_file
939
+ * @param string $table
940
+ */
941
+ private function make_sql( $sql_file, $table ) {
942
+
943
+ // Add SQL statement to drop existing table
944
+ $sql_file .= "\n";
945
+ $sql_file .= "\n";
946
+ $sql_file .= "#\n";
947
+ $sql_file .= "# Delete any existing table " . $this->sql_backquote( $table ) . "\n";
948
+ $sql_file .= "#\n";
949
+ $sql_file .= "\n";
950
+ $sql_file .= "DROP TABLE IF EXISTS " . $this->sql_backquote( $table ) . ";\n";
951
+
952
+ /* Table Structure */
953
+
954
+ // Comment in SQL-file
955
+ $sql_file .= "\n";
956
+ $sql_file .= "\n";
957
+ $sql_file .= "#\n";
958
+ $sql_file .= "# Table structure of table " . $this->sql_backquote( $table ) . "\n";
959
+ $sql_file .= "#\n";
960
+ $sql_file .= "\n";
961
+
962
+ // Get table structure
963
+ $query = 'SHOW CREATE TABLE ' . $this->sql_backquote( $table );
964
+ $result = mysql_query( $query, $this->db );
965
+
966
+ if ( $result ) {
967
+
968
+ if ( mysql_num_rows( $result ) > 0 ) {
969
+ $sql_create_arr = mysql_fetch_array( $result );
970
+ $sql_file .= $sql_create_arr[1];
971
+ }
972
+
973
+ mysql_free_result( $result );
974
+ $sql_file .= ' ;';
975
+
976
+ }
977
+
978
+ /* Table Contents */
979
+
980
+ // Get table contents
981
+ $query = 'SELECT * FROM ' . $this->sql_backquote( $table );
982
+ $result = mysql_query( $query, $this->db );
983
+
984
+ if ( $result ) {
985
+ $fields_cnt = mysql_num_fields( $result );
986
+ $rows_cnt = mysql_num_rows( $result );
987
+ }
988
+
989
+ // Comment in SQL-file
990
+ $sql_file .= "\n";
991
+ $sql_file .= "\n";
992
+ $sql_file .= "#\n";
993
+ $sql_file .= "# Data contents of table " . $table . " (" . $rows_cnt . " records)\n";
994
+ $sql_file .= "#\n";
995
+
996
+ // Checks whether the field is an integer or not
997
+ for ( $j = 0; $j < $fields_cnt; $j++ ) {
998
+
999
+ $field_set[$j] = $this->sql_backquote( mysql_field_name( $result, $j ) );
1000
+ $type = mysql_field_type( $result, $j );
1001
+
1002
+ if ( $type == 'tinyint' || $type == 'smallint' || $type == 'mediumint' || $type == 'int' || $type == 'bigint' || $type == 'timestamp')
1003
+ $field_num[$j] = true;
1004
+
1005
+ else
1006
+ $field_num[$j] = false;
1007
+
1008
+ }
1009
+
1010
+ // Sets the scheme
1011
+ $entries = 'INSERT INTO ' . $this->sql_backquote( $table ) . ' VALUES (';
1012
+ $search = array( '\x00', '\x0a', '\x0d', '\x1a' ); //\x08\\x09, not required
1013
+ $replace = array( '\0', '\n', '\r', '\Z' );
1014
+ $current_row = 0;
1015
+ $batch_write = 0;
1016
+
1017
+ while ( $row = mysql_fetch_row( $result ) ) {
1018
+
1019
+ $current_row++;
1020
+
1021
+ // build the statement
1022
+ for ( $j = 0; $j < $fields_cnt; $j++ ) {
1023
+
1024
+ if ( ! isset($row[$j] ) ) {
1025
+ $values[] = 'NULL';
1026
+
1027
+ } elseif ( $row[$j] == '0' || $row[$j] != '' ) {
1028
+
1029
+ // a number
1030
+ if ( $field_num[$j] )
1031
+ $values[] = $row[$j];
1032
+
1033
+ else
1034
+ $values[] = "'" . str_replace( $search, $replace, $this->sql_addslashes( $row[$j] ) ) . "'";
1035
+
1036
+ } else {
1037
+ $values[] = "''";
1038
+
1039
+ }
1040
+
1041
+ }
1042
+
1043
+ $sql_file .= " \n" . $entries . implode( ', ', $values ) . ") ;";
1044
+
1045
+ // write the rows in batches of 100
1046
+ if ( $batch_write == 100 ) {
1047
+ $batch_write = 0;
1048
+ $this->write_sql( $sql_file );
1049
+ $sql_file = '';
1050
+ }
1051
+
1052
+ $batch_write++;
1053
+
1054
+ unset( $values );
1055
+
1056
+ }
1057
+
1058
+ mysql_free_result( $result );
1059
+
1060
+ // Create footer/closing comment in SQL-file
1061
+ $sql_file .= "\n";
1062
+ $sql_file .= "#\n";
1063
+ $sql_file .= "# End of data contents of table " . $table . "\n";
1064
+ $sql_file .= "# --------------------------------------------------------\n";
1065
+ $sql_file .= "\n";
1066
+
1067
+ $this->write_sql( $sql_file );
1068
+
1069
+ }
1070
+
1071
+ /**
1072
+ * Better addslashes for SQL queries.
1073
+ * Taken from phpMyAdmin.
1074
+ *
1075
+ * @access private
1076
+ * @param string $a_string. (default: '')
1077
+ * @param bool $is_like. (default: false)
1078
+ */
1079
+ private function sql_addslashes( $a_string = '', $is_like = false ) {
1080
+
1081
+ if ( $is_like )
1082
+ $a_string = str_replace( '\\', '\\\\\\\\', $a_string );
1083
+
1084
+ else
1085
+ $a_string = str_replace( '\\', '\\\\', $a_string );
1086
+
1087
+ $a_string = str_replace( '\'', '\\\'', $a_string );
1088
+
1089
+ return $a_string;
1090
+ }
1091
+
1092
+ /**
1093
+ * Write the SQL file
1094
+ *
1095
+ * @access private
1096
+ * @param string $sql
1097
+ */
1098
+ private function write_sql( $sql ) {
1099
+
1100
+ $sqlname = $this->database_dump_filepath();
1101
+
1102
+ // Actually write the sql file
1103
+ if ( is_writable( $sqlname ) || ! file_exists( $sqlname ) ) {
1104
+
1105
+ if ( ! $handle = fopen( $sqlname, 'a' ) )
1106
+ return;
1107
+
1108
+ if ( ! fwrite( $handle, $sql ) )
1109
+ return;
1110
+
1111
+ fclose( $handle );
1112
+
1113
+ return true;
1114
+
1115
+ }
1116
+
1117
+ }
1118
+
1119
+ /**
1120
+ * Get the errors
1121
+ *
1122
+ * @access public
1123
+ * @return null
1124
+ */
1125
+ public function errors() {
1126
+
1127
+ return $this->errors;
1128
+
1129
+ }
1130
+
1131
+
1132
+ /**
1133
+ * Add an error to the errors stack
1134
+ *
1135
+ * @access private
1136
+ * @param string $context
1137
+ * @param mixed $error
1138
+ * @return null
1139
+ */
1140
+ private function error( $context, $error ) {
1141
+
1142
+ if ( empty( $context ) || empty( $error ) )
1143
+ return;
1144
+
1145
+ if ( $context == 'zip' && strpos( $error, 'zip warning' ) !== false )
1146
+ return $this->warning( $context, $error );
1147
+
1148
+
1149
+ $this->errors[$context][$_key = md5( implode( ':' , (array) $error ) )] = $error;
1150
+
1151
+ }
1152
+
1153
+ /**
1154
+ * Get the warnings
1155
+ *
1156
+ * @access public
1157
+ * @return null
1158
+ */
1159
+ public function warnings() {
1160
+
1161
+ return $this->warnings;
1162
+
1163
+ }
1164
+
1165
+
1166
+ /**
1167
+ * Add an warning to the warnings stack
1168
+ *
1169
+ * @access private
1170
+ * @param string $context
1171
+ * @param mixed $warning
1172
+ * @return null
1173
+ */
1174
+ private function warning( $context, $warning ) {
1175
+
1176
+ if ( empty( $context ) || empty( $warning ) )
1177
+ return;
1178
+
1179
+ $this->warnings[$context][$_key = md5( implode( ':' , (array) $warning ) )] = $warning;
1180
+
1181
+ }
1182
+
1183
+
1184
+ /**
1185
+ * Custom error handler for catching errors
1186
+ *
1187
+ * @access private
1188
+ * @param string $type
1189
+ * @param string $message
1190
+ * @param string $file
1191
+ * @param string $line
1192
+ * @return null
1193
+ */
1194
+ public function error_handler( $type ) {
1195
+
1196
+ if ( in_array( $type, array( E_STRICT, E_DEPRECATED ) ) || error_reporting() === 0 )
1197
+ return false;
1198
+
1199
+ $args = func_get_args();
1200
+
1201
+ $this->warning( 'php', array_splice( $args, 0, 4 ) );
1202
+
1203
+ return false;
1204
+
1205
+ }
1206
+
1207
+ }
1208
+
1209
+ /**
1210
+ * Add file callback for PclZip, excludes files
1211
+ * and sets the database dump to be stored in the root
1212
+ * of the zip
1213
+ *
1214
+ * @access private
1215
+ * @param string $event
1216
+ * @param array &$file
1217
+ * @return bool
1218
+ */
1219
+ function hmbkp_pclzip_callback( $event, &$file ) {
1220
+
1221
+ global $_hmbkp_exclude_string;
1222
+
1223
+ // Don't try to add unreadable files.
1224
+ if ( ! is_readable( $file['filename'] ) || ! file_exists( $file['filename'] ) )
1225
+ return false;
1226
+
1227
+ // Match everything else past the exclude list
1228
+ elseif ( $_hmbkp_exclude_string && preg_match( '(' . $_hmbkp_exclude_string . ')', $file['stored_filename'] ) )
1229
+ return false;
1230
+
1231
+ return true;
1232
+
1233
+ }
plugin.php CHANGED
@@ -3,53 +3,117 @@
3
  /*
4
  Plugin Name: WP Remote
5
  Description: Extends the functionality of <a href="http://www.wpremote.com">WP Remote</a>.
6
- Version: 0.6.6
7
- Author: willmot
8
- Author URI: http://humanmade.co.uk/
9
  */
10
 
11
- require_once( 'wpr.admin.php' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  // Don't include when doing a core update
14
  if ( empty( $_GET['action'] ) || $_GET['action'] != 'do-core-upgrade' ) :
15
 
16
  require_once ( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
17
-
18
- class WPR_Upgrader_Skin extends Plugin_Installer_Skin {
19
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  var $feedback;
21
  var $error;
22
-
23
  function error( $error ) {
24
  $this->error = $error;
25
  }
 
26
  function feedback( $feedback ) {
27
  $this->feedback = $feedback;
28
  }
 
29
  function before() {
30
-
31
  }
 
32
  function after() {
33
-
34
  }
 
35
  function header() {
36
-
37
  }
 
38
  function footer() {
39
-
40
  }
 
41
  }
42
 
43
  endif;
44
 
 
 
 
 
 
 
45
  function wpr_catch_api_call() {
46
 
47
- if ( empty( $_GET['wpr_api_key'] ) || !urldecode( $_GET['wpr_api_key'] ) || !isset( $_GET['actions'] ) ) {
48
  return;
49
- }
50
-
51
- require_once( 'wpr.api.php' );
52
-
53
  exit;
54
 
55
  }
3
  /*
4
  Plugin Name: WP Remote
5
  Description: Extends the functionality of <a href="http://www.wpremote.com">WP Remote</a>.
6
+ Version: 2.0 Bleeding
7
+ Author: Human Made Limited
8
+ Author URI: http://hmn.md/
9
  */
10
 
11
+ /* Copyright 2011 Human Made Limited (email : hello@humanmade.co.uk)
12
+
13
+ This program is free software; you can redistribute it and/or modify
14
+ it under the terms of the GNU General Public License as published by
15
+ the Free Software Foundation; either version 2 of the License, or
16
+ (at your option) any later version.
17
+
18
+ This program is distributed in the hope that it will be useful,
19
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ GNU General Public License for more details.
22
+
23
+ You should have received a copy of the GNU General Public License
24
+ along with this program; if not, write to the Free Software
25
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26
+ */
27
+
28
+ define( 'WPR_PLUGIN_SLUG', 'wpremote' );
29
+ define( 'WPR_PLUGIN_PATH', WP_PLUGIN_DIR . '/' . WPR_PLUGIN_SLUG );
30
+ define( 'WPR_PLUGIN_URL', WP_PLUGIN_URL . '/' . WPR_PLUGIN_SLUG );
31
+
32
+ // Load the admin
33
+ require_once( WPR_PLUGIN_PATH . '/wpr.admin.php' );
34
 
35
  // Don't include when doing a core update
36
  if ( empty( $_GET['action'] ) || $_GET['action'] != 'do-core-upgrade' ) :
37
 
38
  require_once ( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
39
+
40
+ class WPR_Plugin_Upgrader_Skin extends Plugin_Installer_Skin {
41
+
42
+ var $feedback;
43
+ var $error;
44
+
45
+ function error( $error ) {
46
+ $this->error = $error;
47
+ }
48
+
49
+ function feedback( $feedback ) {
50
+ $this->feedback = $feedback;
51
+ }
52
+
53
+ function before() {
54
+
55
+ }
56
+
57
+ function after() {
58
+
59
+ }
60
+
61
+ function header() {
62
+
63
+ }
64
+
65
+ function footer() {
66
+
67
+ }
68
+
69
+ }
70
+
71
+ class WPR_Theme_Upgrader_Skin extends Theme_Installer_Skin {
72
+
73
  var $feedback;
74
  var $error;
75
+
76
  function error( $error ) {
77
  $this->error = $error;
78
  }
79
+
80
  function feedback( $feedback ) {
81
  $this->feedback = $feedback;
82
  }
83
+
84
  function before() {
85
+
86
  }
87
+
88
  function after() {
89
+
90
  }
91
+
92
  function header() {
93
+
94
  }
95
+
96
  function footer() {
97
+
98
  }
99
+
100
  }
101
 
102
  endif;
103
 
104
+ /**
105
+ * Catch the API calls and load the API
106
+ *
107
+ * @access public
108
+ * @return null
109
+ */
110
  function wpr_catch_api_call() {
111
 
112
+ if ( empty( $_GET['wpr_api_key'] ) || ! urldecode( $_GET['wpr_api_key'] ) || ! isset( $_GET['actions'] ) )
113
  return;
114
+
115
+ require_once( WPR_PLUGIN_PATH . '/wpr.api.php' );
116
+
 
117
  exit;
118
 
119
  }
readme.txt CHANGED
@@ -1,12 +1,33 @@
1
  === The WP Remote WordPress Plugin ===
2
- Contributors: willmot, joehoyle
3
- Tags: wpremote, remote, xmlrpc
4
- Requires at least: 2.7
5
- Tested up to: 3.0.5
6
- Stable tag: 0.6.6
7
 
8
- Adds extra functionality for use with WP Remote
9
 
10
  == Description ==
11
 
12
- The WP Remote WordPress Plugin is required for the use with [WP Remote](http://www.wpremote.com/)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  === The WP Remote WordPress Plugin ===
2
+ Contributors: humanmade, joehoyle, mattheu, tcrsavage, willmot
3
+ Tags: wpremote, remote administration, multiple wordpress
4
+ Requires at least: 3.1
5
+ Tested up to: 3.3.1
6
+ Stable tag: 2.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
 
10
  == Description ==
11
 
12
+ The WP Remote WordPress Plugin works with [WP Remote](https://wpremote.com/) to enable you to remotely manage all your WordPress sites.
13
+
14
+ = Features =
15
+
16
+ * Track all your WordPress sites from one place.
17
+ * Track the WordPress version each site is running and easily update.
18
+ * Track all your plugins and themes 1 click update them.
19
+ * Free to use and you can track an unlimited number of sites.
20
+
21
+ = Coming Soon =
22
+
23
+ * Backups
24
+
25
+ = Support =
26
+
27
+ You can email us at support@wpremote.com for support.
28
+
29
+ == Installation ==
30
+
31
+ 1. Install The WP Remote WordPress Plugin either via the WordPress.org plugin directory, or by uploading the files to your server.
32
+ 2. Activate the plugin.
33
+ 3. Sign up for an account at wpremote.com and add your site.
screenshot-1.png ADDED
Binary file
wpr.admin.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  function wpr_setup_admin() {
4
 
5
  //register the options page
1
  <?php
2
 
3
+ // @todo this should all be better
4
+
5
  function wpr_setup_admin() {
6
 
7
  //register the options page
wpr.api.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
2
 
3
- if( !isset( $_GET['wpr_api_key'] ) || urldecode( $_GET['wpr_api_key'] ) !== get_option( 'wpr_api_key' ) || !isset( $_GET['actions'] ) ) {
 
4
  echo json_encode( 'bad-api-key' );
5
  exit;
6
  }
@@ -8,126 +9,297 @@ if( !isset( $_GET['wpr_api_key'] ) || urldecode( $_GET['wpr_api_key'] ) !== get_
8
  $actions = explode( ',', $_GET['actions'] );
9
  $actions = array_flip( $actions );
10
 
 
11
  wp_set_current_user( 1 );
12
 
13
- foreach( $actions as $action => $value ) :
14
 
15
- switch( $action ) :
16
-
17
- //Plugin version
18
  case 'get_plugin_version' :
 
19
  $actions[$action] = '1.1';
20
- break;
21
-
 
22
  case 'get_wp_version' :
23
-
24
  global $wp_version;
 
25
  $actions[$action] = (string) $wp_version;
26
-
27
- break;
28
-
29
  case 'get_plugins' :
 
30
  $actions[$action] = _wpr_supports_plugin_upgrade() ? _wpr_get_plugins() : 'not-implemented';
31
- break;
32
-
 
33
  case 'upgrade_plugin' :
 
34
  $actions[$action] = _wpr_upgrade_plugin( (string) $_GET['plugin'] );
35
- break;
36
-
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  default :
 
38
  $actions[$action] = 'not-implemented';
39
-
40
- endswitch;
41
 
42
- endforeach;
 
 
43
 
44
  echo json_encode( $actions );
45
  exit;
46
- // functions
47
 
 
 
 
 
 
48
  function _wpr_get_plugins() {
49
-
50
- require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
51
-
 
52
  $plugins = get_plugins();
 
 
53
  $active = get_option( 'active_plugins', array() );
54
-
 
55
  wp_update_plugins();
56
-
57
- //different versions of wp store the updates in different places
 
58
  if( function_exists( 'get_site_transient' ) && $transient = get_site_transient( 'update_plugins' ) )
59
  $current = $transient;
 
60
  elseif( $transient = get_transient( 'update_plugins' ) )
61
  $current = $transient;
 
62
  else
63
- $cuurrent = get_option( 'update_plugins' );
64
-
65
  foreach ( (array) $plugins as $plugin_file => $plugin ) {
66
-
67
  $new_version = isset( $current->response[$plugin_file] ) ? $current->response[$plugin_file]->new_version : null;
68
-
69
- if ( is_plugin_active( $plugin_file ) ) {
70
  $plugins[$plugin_file]['active'] = true;
71
- } else {
 
72
  $plugins[$plugin_file]['active'] = false;
73
- }
74
-
75
  if ( $new_version ) {
76
  $plugins[$plugin_file]['latest_version'] = $new_version;
77
  $plugins[$plugin_file]['latest_package'] = $current->response[$plugin_file]->package;
78
  $plugins[$plugin_file]['slug'] = $current->response[$plugin_file]->slug;
 
79
  } else {
80
  $plugins[$plugin_file]['latest_version'] = $plugin['Version'];
 
81
  }
 
82
  }
83
-
 
84
  global $wp_version;
85
  $plugins_args = (object) compact( 'plugins' );
86
 
87
-
88
  return $plugins;
89
  }
90
 
 
 
 
 
 
 
 
91
  function _wpr_upgrade_plugin( $plugin ) {
92
 
93
  include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
94
-
95
- if( !class_exists( 'Plugin_Upgrader' ) )
96
  return array( 'status' => 'error', 'error' => 'WordPress version too old for plugin upgrades' );
97
-
98
- $skin = new WPR_Upgrader_Skin();
99
  $upgrader = new Plugin_Upgrader( $skin );
100
  $is_active = is_plugin_active( $plugin );
101
-
 
102
  ob_start();
103
  $result = $upgrader->upgrade( $plugin );
104
  $data = ob_get_contents();
105
  ob_clean();
106
-
107
- if( ( !$result && !is_null( $result ) ) || $data )
108
  return array( 'status' => 'error', 'error' => 'file_permissions_error' );
109
-
110
- elseif( is_wp_error( $result ) )
111
  return array( 'status' => 'error', 'error' => $result->get_error_code() );
112
-
113
- if( $skin->error )
114
  return array( 'status' => 'error', 'error' => $skin->error );
115
-
116
- //if the plugin was activited, we have to re-activate it
117
- if( $is_active ) {
 
118
  $current = get_option( 'active_plugins', array() );
119
  $current[] = plugin_basename( trim( $plugin ) );;
120
- sort($current);
121
- update_option('active_plugins', $current);
122
  }
123
 
124
  return array( 'status' => 'success' );
125
-
126
  }
127
 
 
 
 
 
 
 
 
128
  function _wpr_supports_plugin_upgrade() {
129
-
130
  include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
 
131
  return class_exists( 'Plugin_Upgrader' );
132
 
133
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
 
3
+ // Check the API Key
4
+ if( ! isset( $_GET['wpr_api_key'] ) || urldecode( $_GET['wpr_api_key'] ) !== get_option( 'wpr_api_key' ) || ! isset( $_GET['actions'] ) ) {
5
  echo json_encode( 'bad-api-key' );
6
  exit;
7
  }
9
  $actions = explode( ',', $_GET['actions'] );
10
  $actions = array_flip( $actions );
11
 
12
+ // log in as admin
13
  wp_set_current_user( 1 );
14
 
15
+ foreach( $actions as $action => $value ) {
16
 
17
+ switch( $action ) {
18
+
19
+ // TODO should be dynamic
20
  case 'get_plugin_version' :
21
+
22
  $actions[$action] = '1.1';
23
+
24
+ break;
25
+
26
  case 'get_wp_version' :
27
+
28
  global $wp_version;
29
+
30
  $actions[$action] = (string) $wp_version;
31
+
32
+ break;
33
+
34
  case 'get_plugins' :
35
+
36
  $actions[$action] = _wpr_supports_plugin_upgrade() ? _wpr_get_plugins() : 'not-implemented';
37
+
38
+ break;
39
+
40
  case 'upgrade_plugin' :
41
+
42
  $actions[$action] = _wpr_upgrade_plugin( (string) $_GET['plugin'] );
43
+
44
+ break;
45
+
46
+ case 'get_themes' :
47
+
48
+ $actions[$action] = _wpr_supports_theme_upgrade() ? _wpr_get_themes() : 'not-implemented';
49
+
50
+ break;
51
+
52
+ case 'upgrade_theme' :
53
+
54
+ $actions[$action] = _wpr_upgrade_theme( (string) $_GET['theme'] );
55
+
56
+ break;
57
+
58
  default :
59
+
60
  $actions[$action] = 'not-implemented';
 
 
61
 
62
+ }
63
+
64
+ }
65
 
66
  echo json_encode( $actions );
67
  exit;
 
68
 
69
+ /**
70
+ * Return an array of installed plugins
71
+ *
72
+ * @return array
73
+ */
74
  function _wpr_get_plugins() {
75
+
76
+ require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
77
+
78
+ // Get all plugins
79
  $plugins = get_plugins();
80
+
81
+ // Get the list of active plugins
82
  $active = get_option( 'active_plugins', array() );
83
+
84
+ // Force a plugin update check
85
  wp_update_plugins();
86
+
87
+ // Different versions of wp store the updates in different places
88
+ // TODO can we depreciate
89
  if( function_exists( 'get_site_transient' ) && $transient = get_site_transient( 'update_plugins' ) )
90
  $current = $transient;
91
+
92
  elseif( $transient = get_transient( 'update_plugins' ) )
93
  $current = $transient;
94
+
95
  else
96
+ $current = get_option( 'update_plugins' );
97
+
98
  foreach ( (array) $plugins as $plugin_file => $plugin ) {
99
+
100
  $new_version = isset( $current->response[$plugin_file] ) ? $current->response[$plugin_file]->new_version : null;
101
+
102
+ if ( is_plugin_active( $plugin_file ) )
103
  $plugins[$plugin_file]['active'] = true;
104
+
105
+ else
106
  $plugins[$plugin_file]['active'] = false;
107
+
 
108
  if ( $new_version ) {
109
  $plugins[$plugin_file]['latest_version'] = $new_version;
110
  $plugins[$plugin_file]['latest_package'] = $current->response[$plugin_file]->package;
111
  $plugins[$plugin_file]['slug'] = $current->response[$plugin_file]->slug;
112
+
113
  } else {
114
  $plugins[$plugin_file]['latest_version'] = $plugin['Version'];
115
+
116
  }
117
+
118
  }
119
+
120
+ // TODO is this necessary?
121
  global $wp_version;
122
  $plugins_args = (object) compact( 'plugins' );
123
 
 
124
  return $plugins;
125
  }
126
 
127
+ /**
128
+ * Update a plugin
129
+ *
130
+ * @access private
131
+ * @param mixed $plugin
132
+ * @return array
133
+ */
134
  function _wpr_upgrade_plugin( $plugin ) {
135
 
136
  include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
137
+
138
+ if ( ! _wpr_supports_plugin_upgrade() )
139
  return array( 'status' => 'error', 'error' => 'WordPress version too old for plugin upgrades' );
140
+
141
+ $skin = new WPR_Plugin_Upgrader_Skin();
142
  $upgrader = new Plugin_Upgrader( $skin );
143
  $is_active = is_plugin_active( $plugin );
144
+
145
+ // Do the upgrade
146
  ob_start();
147
  $result = $upgrader->upgrade( $plugin );
148
  $data = ob_get_contents();
149
  ob_clean();
150
+
151
+ if ( ( ! $result && ! is_null( $result ) ) || $data )
152
  return array( 'status' => 'error', 'error' => 'file_permissions_error' );
153
+
154
+ elseif ( is_wp_error( $result ) )
155
  return array( 'status' => 'error', 'error' => $result->get_error_code() );
156
+
157
+ if ( $skin->error )
158
  return array( 'status' => 'error', 'error' => $skin->error );
159
+
160
+ // If the plugin was activited, we have to re-activate it
161
+ // @todo Shouldn't this use activate_plugin?
162
+ if ( $is_active ) {
163
  $current = get_option( 'active_plugins', array() );
164
  $current[] = plugin_basename( trim( $plugin ) );;
165
+ sort( $current );
166
+ update_option('active_plugins', $current );
167
  }
168
 
169
  return array( 'status' => 'success' );
170
+
171
  }
172
 
173
+ /**
174
+ * Check if the site can support plugin upgrades
175
+ *
176
+ * @todo should probably check if we have direct filesystem access
177
+ * @todo can we remove support for versions which don't support Plugin_Upgrader
178
+ * @return bool
179
+ */
180
  function _wpr_supports_plugin_upgrade() {
181
+
182
  include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
183
+
184
  return class_exists( 'Plugin_Upgrader' );
185
 
186
  }
187
+
188
+ /**
189
+ * Return an array of installed themes
190
+ *
191
+ * @return array
192
+ */
193
+ function _wpr_get_themes() {
194
+
195
+ require_once( ABSPATH . '/wp-admin/includes/theme.php' );
196
+
197
+ // Get all themes
198
+ $themes = get_themes();
199
+
200
+ // Get the list of active themes
201
+ $active = get_option( 'active_themes', array() );
202
+
203
+ // Force a theme update check
204
+ wp_update_themes();
205
+
206
+ // Different versions of wp store the updates in different places
207
+ // TODO can we depreciate
208
+ if( function_exists( 'get_site_transient' ) && $transient = get_site_transient( 'update_themes' ) )
209
+ $current = $transient;
210
+
211
+ elseif( $transient = get_transient( 'update_themes' ) )
212
+ $current = $transient;
213
+
214
+ else
215
+ $current = get_option( 'update_themes' );
216
+
217
+ foreach ( (array) $themes as $theme_file => $theme ) {
218
+
219
+ $new_version = isset( $current->response[$theme_file] ) ? $current->response[$theme_file]->new_version : null;
220
+
221
+ if ( is_theme_active( $theme_file ) )
222
+ $themes[$theme_file]['active'] = true;
223
+
224
+ else
225
+ $themes[$theme_file]['active'] = false;
226
+
227
+ if ( $new_version ) {
228
+ $themes[$theme_file]['latest_version'] = $new_version;
229
+ $themes[$theme_file]['latest_package'] = $current->response[$theme_file]->package;
230
+ $themes[$theme_file]['slug'] = $current->response[$theme_file]->slug;
231
+
232
+ } else {
233
+ $themes[$theme_file]['latest_version'] = $theme['Version'];
234
+
235
+ }
236
+
237
+ }
238
+
239
+ // TODO is this necessary?
240
+ global $wp_version;
241
+ $themes_args = (object) compact( 'themes' );
242
+
243
+ return $themes;
244
+ }
245
+
246
+ /**
247
+ * Update a theme
248
+ *
249
+ * @access private
250
+ * @param mixed $theme
251
+ * @return array
252
+ */
253
+ function _wpr_upgrade_theme( $theme ) {
254
+
255
+ include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
256
+
257
+ if ( ! _wpr_supports_theme_upgrade() )
258
+ return array( 'status' => 'error', 'error' => 'WordPress version too old for theme upgrades' );
259
+
260
+ $skin = new WPR_Theme_Upgrader_Skin();
261
+ $upgrader = new Theme_Upgrader( $skin );
262
+ $is_active = is_theme_active( $theme );
263
+
264
+ // Do the upgrade
265
+ ob_start();
266
+ $result = $upgrader->upgrade( $theme );
267
+ $data = ob_get_contents();
268
+ ob_clean();
269
+
270
+ if ( ( ! $result && ! is_null( $result ) ) || $data )
271
+ return array( 'status' => 'error', 'error' => 'file_permissions_error' );
272
+
273
+ elseif ( is_wp_error( $result ) )
274
+ return array( 'status' => 'error', 'error' => $result->get_error_code() );
275
+
276
+ if ( $skin->error )
277
+ return array( 'status' => 'error', 'error' => $skin->error );
278
+
279
+ // If the theme was activited, we have to re-activate it
280
+ // @todo Shouldn't this use activate_theme?
281
+ if ( $is_active ) {
282
+ $current = get_option( 'active_plugins', array() );
283
+ $current[] = theme_basename( trim( $theme ) );;
284
+ sort( $current );
285
+ update_option('active_themes', $current );
286
+ }
287
+
288
+ return array( 'status' => 'success' );
289
+
290
+ }
291
+
292
+ /**
293
+ * Check if the site can support theme upgrades
294
+ *
295
+ * @todo should probably check if we have direct filesystem access
296
+ * @todo can we remove support for versions which don't support Theme_Upgrader
297
+ * @return bool
298
+ */
299
+ function _wpr_supports_plugin_upgrade() {
300
+
301
+ include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
302
+
303
+ return class_exists( 'Theme_Upgrader' );
304
+
305
+ }
wprp.admin.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Register the wpr_api_key settings
5
+ *
6
+ * @return null
7
+ */
8
+ function wprp_setup_admin() {
9
+ register_setting( 'wpr-settings', 'wpr_api_key' );
10
+ }
11
+
12
+ add_action( 'admin_menu', 'wprp_setup_admin' );
13
+
14
+ /**
15
+ * Add API Key form
16
+ *
17
+ * Only shown if no API Key
18
+ *
19
+ * @return null
20
+ */
21
+ function wprp_add_api_key_admin_notice() { ?>
22
+
23
+ <div id="wprp-message" class="updated">
24
+
25
+ <form method="post" action="options.php">
26
+
27
+ <p>
28
+
29
+ <strong>WP Remote is almost ready</strong>, <label style="vertical-align: baseline;" for="wpr_api_key">enter your API Key to continue</label>
30
+
31
+ <input style="margin: -4px 5px; vertical-align: text-bottom; line-height: 13px; font-size: 12px;" type="text" class="code regular-text" id="wpr_api_key" name="wpr_api_key" />
32
+
33
+ <input style="vertical-align: text-bottom; margin: -3px 0; line-height: 12px;" type="submit" value="Save API Key" class="button-primary" />
34
+
35
+ </p>
36
+
37
+ <style>#message { display : none; }</style>
38
+
39
+ <?php settings_fields( 'wpr-settings' );
40
+
41
+ // Output any sections defined for page sl-settings
42
+ do_settings_sections( 'wpr-settings' ); ?>
43
+
44
+ </form>
45
+
46
+ </div>
47
+
48
+
49
+ <?php }
50
+
51
+ if ( ! get_option( 'wpr_api_key' ) )
52
+ add_action( 'admin_notices', 'wprp_add_api_key_admin_notice' );
53
+
54
+ /**
55
+ * Success message for a newly added API Key
56
+ *
57
+ * @return null
58
+ */
59
+ function wprp_api_key_added_admin_notice() {
60
+
61
+ if ( get_current_screen()->base != 'plugins' || empty( $_GET['settings-updated'] ) || ! get_option( 'wpr_api_key' ) )
62
+ return; ?>
63
+
64
+ <div id="wprp-message" class="updated">
65
+ <p><strong>WP Remote API Key successfully added</strong>, close this page to go back to <a href="https://wpremote.com/">WP Remote</a>.</p>
66
+ </div>
67
+
68
+ <?php }
69
+ add_action( 'admin_notices', 'wprp_api_key_added_admin_notice' );
70
+
71
+ /**
72
+ * Delete the API key on activate and deactivate
73
+ *
74
+ * @return null
75
+ */
76
+ function wprp_deactivate() {
77
+ delete_option( 'wpr_api_key' );
78
+ }
79
+ // Plugin activation and deactivation
80
+ add_action( 'activate_' . WPRP_PLUGIN_SLUG . '/wprp.plugin.php', 'wprp_deactivate' );
81
+ add_action( 'deactivate_' . WPRP_PLUGIN_SLUG . '/wprp.plugin.php', 'wprp_deactivate' );
wprp.api.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Check the API Key
4
+ if ( ! isset( $_GET['wpr_api_key'] ) || urldecode( $_GET['wpr_api_key'] ) !== get_option( 'wpr_api_key' ) || ! isset( $_GET['actions'] ) ) {
5
+ echo json_encode( 'bad-api-key' );
6
+ exit;
7
+ }
8
+
9
+ $actions = explode( ',', $_GET['actions'] );
10
+ $actions = array_flip( $actions );
11
+
12
+ // Disable error_reporting so they don't break the json request
13
+ error_reporting( 0 );
14
+
15
+ // Log in as admin
16
+ wp_set_current_user( 1 );
17
+
18
+ foreach( $actions as $action => $value ) {
19
+
20
+ // TODO Instead should just fire actions which we hook into.
21
+ // TODO should namespace api methods?
22
+ switch( $action ) {
23
+
24
+ // TODO should be dynamic
25
+ case 'get_plugin_version' :
26
+
27
+ $actions[$action] = '1.1';
28
+
29
+ break;
30
+
31
+ case 'get_filesystem_method' :
32
+
33
+ $actions[$action] = get_filesystem_method();
34
+
35
+ break;
36
+
37
+ case 'get_wp_version' :
38
+
39
+ global $wp_version;
40
+
41
+ $actions[$action] = (string) $wp_version;
42
+
43
+ break;
44
+
45
+ case 'get_plugins' :
46
+
47
+ $actions[$action] = _wprp_supports_plugin_upgrade() ? _wprp_get_plugins() : 'not-implemented';
48
+
49
+ break;
50
+
51
+ case 'upgrade_plugin' :
52
+
53
+ $actions[$action] = _wprp_upgrade_plugin( (string) $_GET['plugin'] );
54
+
55
+ break;
56
+
57
+ case 'get_themes' :
58
+
59
+ $actions[$action] = _wprp_supports_theme_upgrade() ? _wprp_get_themes() : 'not-implemented';
60
+
61
+ break;
62
+
63
+ case 'upgrade_theme' :
64
+
65
+ $actions[$action] = _wprp_upgrade_theme( (string) $_GET['theme'] );
66
+
67
+ break;
68
+
69
+ case 'do_backup' :
70
+ case 'delete_backup' :
71
+ case 'supports_backups' :
72
+
73
+ $actions[$action] = _wprp_backups_api_call( $action );
74
+
75
+ break;
76
+
77
+ default :
78
+
79
+ $actions[$action] = 'not-implemented';
80
+
81
+ break;
82
+
83
+ }
84
+
85
+ }
86
+ echo json_encode( $actions );
87
+ exit;
wprp.backups.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Handle the backups API calls
5
+ *
6
+ * @param string $call
7
+ * @return mixed
8
+ */
9
+ function _wprp_backups_api_call( $action ) {
10
+
11
+ if ( ! class_exists( 'hm_backup' ) )
12
+ return new WP_Error( 'Backups module not present' );
13
+
14
+ switch( $action ) :
15
+
16
+ // TODO in the future we should do some check here to make sure they do support backups
17
+ case 'supports_backups' :
18
+ return true;
19
+
20
+ case 'do_backup' :
21
+
22
+ $backup = new HM_Backup();
23
+
24
+ $upload_dir = wp_upload_dir();
25
+
26
+ // Store the backup file in the uploads dir
27
+ $backup->path = $upload_dir['basedir'];
28
+
29
+ // Set a random backup filename
30
+ $backup->archive_filename = md5( time() ) . '.zip';
31
+
32
+ $backup->backup();
33
+
34
+ if ( $errors = $backup->errors() ) {
35
+ $wp_error = new WP_Error;
36
+
37
+ foreach ( $errors as $error )
38
+ $wp_error->add( reset( $error ), reset( $error ) );
39
+
40
+ return $wp_error;
41
+ }
42
+
43
+
44
+ return str_replace( ABSPATH, site_url( '/' ), $backup->archive_filepath() );
45
+
46
+ case 'delete_backup' :
47
+
48
+ $upload_dir = wp_upload_dir();
49
+
50
+ if ( ! empty( $_REQUEST['backup'] ) && file_exists( $upload_dir['basedir'] . '/' . $_REQUEST['backup'] ) && substr( $_REQUEST['backup'], -4 ) == '.zip' )
51
+ unlink( $upload_dir['basedir'] . '/' . $_REQUEST['backup'] );
52
+
53
+ break;
54
+
55
+ endswitch;
56
+
57
+ }
wprp.plugin.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ Plugin Name: WP Remote
5
+ Description: Manage your WordPress site with <a href="https://wpremote.com/">WP Remote</a>. Deactivate to clear your API Key.
6
+ Version: 2.0
7
+ Author: Human Made Limited
8
+ Author URI: http://hmn.md/
9
+ */
10
+
11
+ /* Copyright 2011 Human Made Limited (email : hello@humanmade.co.uk)
12
+
13
+ This program is free software; you can redistribute it and/or modify
14
+ it under the terms of the GNU General Public License as published by
15
+ the Free Software Foundation; either version 2 of the License, or
16
+ (at your option) any later version.
17
+
18
+ This program is distributed in the hope that it will be useful,
19
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ GNU General Public License for more details.
22
+
23
+ You should have received a copy of the GNU General Public License
24
+ along with this program; if not, write to the Free Software
25
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26
+ */
27
+
28
+ define( 'WPRP_PLUGIN_SLUG', basename( dirname( __FILE__ ) ) );
29
+ define( 'WPRP_PLUGIN_PATH', WP_PLUGIN_DIR . '/' . WPRP_PLUGIN_SLUG );
30
+ define( 'WPRP_PLUGIN_URL', WP_PLUGIN_URL . '/' . WPRP_PLUGIN_SLUG );
31
+
32
+ require_once( WPRP_PLUGIN_PATH .'/wprp.admin.php' );
33
+
34
+ // Backups require 3.1 TODO - do they?
35
+ if ( version_compare( get_bloginfo('version'), '3.1', '>=' ) && ! class_exists( 'HM_Backup' ) )
36
+ require( WPRP_PLUGIN_PATH . '/hm-backup/hm-backup.php' );
37
+
38
+ // Don't include when doing a core update
39
+ if ( empty( $_GET['action'] ) || $_GET['action'] != 'do-core-upgrade' ) :
40
+
41
+ require_once ( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
42
+
43
+ class WPRP_Plugin_Upgrader_Skin extends Plugin_Installer_Skin {
44
+
45
+ var $feedback;
46
+ var $error;
47
+
48
+ function error( $error ) {
49
+ $this->error = $error;
50
+ }
51
+
52
+ function feedback( $feedback ) {
53
+ $this->feedback = $feedback;
54
+ }
55
+
56
+ function before() {
57
+
58
+ }
59
+
60
+ function after() {
61
+
62
+ }
63
+
64
+ function header() {
65
+
66
+ }
67
+
68
+ function footer() {
69
+
70
+ }
71
+
72
+ }
73
+
74
+ class WPRP_Theme_Upgrader_Skin extends Theme_Installer_Skin {
75
+
76
+ var $feedback;
77
+ var $error;
78
+
79
+ function error( $error ) {
80
+ $this->error = $error;
81
+ }
82
+
83
+ function feedback( $feedback ) {
84
+ $this->feedback = $feedback;
85
+ }
86
+
87
+ function before() {
88
+
89
+ }
90
+
91
+ function after() {
92
+
93
+ }
94
+
95
+ function header() {
96
+
97
+ }
98
+
99
+ function footer() {
100
+
101
+ }
102
+
103
+ }
104
+
105
+ endif;
106
+
107
+ /**
108
+ * Catch the API calls and load the API
109
+ *
110
+ * @return null
111
+ */
112
+ function wprp_catch_api_call() {
113
+
114
+ if ( empty( $_GET['wpr_api_key'] ) || ! urldecode( $_GET['wpr_api_key'] ) || ! isset( $_GET['actions'] ) )
115
+ return;
116
+
117
+ require_once( WPRP_PLUGIN_PATH . '/wprp.backups.php' );
118
+ require_once( WPRP_PLUGIN_PATH . '/wprp.plugins.php' );
119
+ require_once( WPRP_PLUGIN_PATH . '/wprp.themes.php' );
120
+
121
+ require_once( WPRP_PLUGIN_PATH . '/wprp.api.php' );
122
+
123
+ exit;
124
+
125
+ }
126
+ add_action( 'init', 'wprp_catch_api_call', 1 );
wprp.plugins.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Return an array of installed plugins
5
+ *
6
+ * @return array
7
+ */
8
+ function _wprp_get_plugins() {
9
+
10
+ require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
11
+
12
+ // Get all plugins
13
+ $plugins = get_plugins();
14
+
15
+ // Get the list of active plugins
16
+ $active = get_option( 'active_plugins', array() );
17
+
18
+ // Force a plugin update check
19
+ wp_update_plugins();
20
+
21
+ // Different versions of wp store the updates in different places
22
+ // TODO can we depreciate
23
+ if( function_exists( 'get_site_transient' ) && $transient = get_site_transient( 'update_plugins' ) )
24
+ $current = $transient;
25
+
26
+ elseif( $transient = get_transient( 'update_plugins' ) )
27
+ $current = $transient;
28
+
29
+ else
30
+ $current = get_option( 'update_plugins' );
31
+
32
+ foreach ( (array) $plugins as $plugin_file => $plugin ) {
33
+
34
+ $new_version = isset( $current->response[$plugin_file] ) ? $current->response[$plugin_file]->new_version : null;
35
+
36
+ if ( is_plugin_active( $plugin_file ) )
37
+ $plugins[$plugin_file]['active'] = true;
38
+
39
+ else
40
+ $plugins[$plugin_file]['active'] = false;
41
+
42
+ if ( $new_version ) {
43
+ $plugins[$plugin_file]['latest_version'] = $new_version;
44
+ $plugins[$plugin_file]['latest_package'] = $current->response[$plugin_file]->package;
45
+ $plugins[$plugin_file]['slug'] = $current->response[$plugin_file]->slug;
46
+
47
+ } else {
48
+ $plugins[$plugin_file]['latest_version'] = $plugin['Version'];
49
+
50
+ }
51
+
52
+ }
53
+
54
+ return $plugins;
55
+ }
56
+
57
+ /**
58
+ * Update a plugin
59
+ *
60
+ * @access private
61
+ * @param mixed $plugin
62
+ * @return array
63
+ */
64
+ function _wprp_upgrade_plugin( $plugin ) {
65
+
66
+ include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
67
+
68
+ if ( ! _wprp_supports_plugin_upgrade() )
69
+ return array( 'status' => 'error', 'error' => 'WordPress version too old for plugin upgrades' );
70
+
71
+ $skin = new WPRP_Plugin_Upgrader_Skin();
72
+ $upgrader = new Plugin_Upgrader( $skin );
73
+ $is_active = is_plugin_active( $plugin );
74
+
75
+ // Do the upgrade
76
+ ob_start();
77
+ $result = $upgrader->upgrade( $plugin );
78
+ $data = ob_get_contents();
79
+ ob_clean();
80
+
81
+ if ( ( ! $result && ! is_null( $result ) ) || $data )
82
+ return array( 'status' => 'error', 'error' => 'file_permissions_error' );
83
+
84
+ elseif ( is_wp_error( $result ) )
85
+ return array( 'status' => 'error', 'error' => $result->get_error_code() );
86
+
87
+ if ( $skin->error )
88
+ return array( 'status' => 'error', 'error' => $skin->error );
89
+
90
+ // If the plugin was activited, we have to re-activate it
91
+ // @todo Shouldn't this use activate_plugin?
92
+ if ( $is_active ) {
93
+ $current = get_option( 'active_plugins', array() );
94
+ $current[] = plugin_basename( trim( $plugin ) );;
95
+ sort( $current );
96
+ update_option('active_plugins', $current );
97
+ }
98
+
99
+ return array( 'status' => 'success' );
100
+
101
+ }
102
+
103
+ /**
104
+ * Check if the site can support plugin upgrades
105
+ *
106
+ * @todo should probably check if we have direct filesystem access
107
+ * @todo can we remove support for versions which don't support Plugin_Upgrader
108
+ * @return bool
109
+ */
110
+ function _wprp_supports_plugin_upgrade() {
111
+
112
+ include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
113
+
114
+ return class_exists( 'Plugin_Upgrader' );
115
+
116
+ }
wprp.themes.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Return an array of installed themes
5
+ *
6
+ * @return array
7
+ */
8
+ function _wprp_get_themes() {
9
+
10
+ require_once( ABSPATH . '/wp-admin/includes/theme.php' );
11
+
12
+ // Get all themes
13
+ $themes = get_themes();
14
+
15
+ // Get the list of active themes
16
+ $active = get_option( 'current_theme' );
17
+
18
+ // Force a theme update check
19
+ wp_update_themes();
20
+
21
+ // Different versions of wp store the updates in different places
22
+ // TODO can we depreciate
23
+ if ( function_exists( 'get_site_transient' ) && $transient = get_site_transient( 'update_themes' ) )
24
+ $current = $transient;
25
+
26
+ elseif ( $transient = get_transient( 'update_themes' ) )
27
+ $current = $transient;
28
+
29
+ else
30
+ $current = get_option( 'update_themes' );
31
+
32
+ foreach ( (array) $themes as $theme ) {
33
+
34
+ $new_version = isset( $current->response[$theme['Template']] ) ? $current->response[$theme['Template']]['new_version'] : null;
35
+
36
+ if ( $active == $theme['Name'] )
37
+ $themes[$theme['Name']]['active'] = true;
38
+
39
+ else
40
+ $themes[$theme['Name']]['active'] = false;
41
+
42
+ if ( $new_version ) {
43
+
44
+ $themes[$theme['Name']]['latest_version'] = $new_version;
45
+ $themes[$theme['Name']]['latest_package'] = $current->response[$theme['Template']]['package'];
46
+
47
+ } else {
48
+
49
+ $themes[$theme['Name']]['latest_version'] = $theme['Version'];
50
+
51
+ }
52
+
53
+ }
54
+
55
+ return $themes;
56
+ }
57
+
58
+ /**
59
+ * Update a theme
60
+ *
61
+ * @param mixed $theme
62
+ * @return array
63
+ */
64
+ function _wprp_upgrade_theme( $theme ) {
65
+
66
+ include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
67
+
68
+ if ( ! _wprp_supports_theme_upgrade() )
69
+ return array( 'status' => 'error', 'error' => 'WordPress version too old for theme upgrades' );
70
+
71
+ $skin = new WPRP_Theme_Upgrader_Skin();
72
+ $upgrader = new Theme_Upgrader( $skin );
73
+
74
+ // Do the upgrade
75
+ ob_start();
76
+ $result = $upgrader->upgrade( $theme );
77
+ $data = ob_get_contents();
78
+ ob_clean();
79
+
80
+ if ( ( ! $result && ! is_null( $result ) ) || $data )
81
+ return array( 'status' => 'error', 'error' => 'file_permissions_error' );
82
+
83
+ elseif ( is_wp_error( $result ) )
84
+ return array( 'status' => 'error', 'error' => $result->get_error_code() );
85
+
86
+ if ( $skin->error )
87
+ return array( 'status' => 'error', 'error' => $skin->error );
88
+
89
+ return array( 'status' => 'success' );
90
+
91
+ }
92
+
93
+ /**
94
+ * Check if the site can support theme upgrades
95
+ *
96
+ * @todo should probably check if we have direct filesystem access
97
+ * @todo can we remove support for versions which don't support Theme_Upgrader
98
+ * @return bool
99
+ */
100
+ function _wprp_supports_theme_upgrade() {
101
+
102
+ include_once ( ABSPATH . 'wp-admin/includes/admin.php' );
103
+
104
+ return class_exists( 'Theme_Upgrader' );
105
+
106
+ }