BackWPup – WordPress Backup Plugin - Version 1.4.1

Version Description

  • Dropbox changes
  • fixed problem on send log with mail
  • Security fix (thanks Massa Danilo)
Download this release

Release Info

Developer danielhuesken
Plugin Icon 128x128 BackWPup – WordPress Backup Plugin
Version 1.4.1
Comparing to
See all releases

Code changes from version 1.3.6 to 1.4.1

app/backwpup_dojob.php CHANGED
@@ -3,98 +3,6 @@
3
  if ( !defined('ABSPATH') )
4
  die('-1');
5
 
6
-
7
- //function for PHP error handling
8
- function backwpup_joberrorhandler($errno, $errstr, $errfile, $errline) {
9
- global $backwpup_logfile;
10
-
11
- //genrate timestamp
12
- if (!function_exists('memory_get_usage')) { // test if memory functions compiled in
13
- $timestamp="<span style=\"background-color:c3c3c3;\" title=\"[Line: ".$errline."|File: ".basename($errfile)."\">".date_i18n('Y-m-d H:i.s').":</span> ";
14
- } else {
15
- $timestamp="<span style=\"background-color:c3c3c3;\" title=\"[Line: ".$errline."|File: ".basename($errfile)."|Mem: ".backwpup_formatBytes(@memory_get_usage(true))."|Mem Max: ".backwpup_formatBytes(@memory_get_peak_usage(true))."|Mem Limit: ".ini_get('memory_limit')."]\">".date_i18n('Y-m-d H:i.s').":</span> ";
16
- }
17
-
18
- switch ($errno) {
19
- case E_NOTICE:
20
- case E_USER_NOTICE:
21
- $massage=$timestamp."<span>".$errstr."</span>";
22
- break;
23
- case E_WARNING:
24
- case E_USER_WARNING:
25
- $logheader=backwpup_read_logheader($backwpup_logfile); //read waring count from log header
26
- $warnings=$logheader['warnings']+1;
27
- $massage=$timestamp."<span style=\"background-color:yellow;\">".__('[WARNING]','backwpup')." ".$errstr."</span>";
28
- break;
29
- case E_ERROR:
30
- case E_USER_ERROR:
31
- $logheader=backwpup_read_logheader($backwpup_logfile); //read error count from log header
32
- $errors=$logheader['errors']+1;
33
- $massage=$timestamp."<span style=\"background-color:red;\">".__('[ERROR]','backwpup')." ".$errstr."</span>";
34
- break;
35
- case E_DEPRECATED:
36
- case E_USER_DEPRECATED:
37
- $massage=$timestamp."<span>".__('[DEPRECATED]','backwpup')." ".$errstr."</span>";
38
- break;
39
- case E_STRICT:
40
- $massage=$timestamp."<span>".__('[STRICT NOTICE]','backwpup')." ".$errstr."</span>";
41
- break;
42
- case E_RECOVERABLE_ERROR:
43
- $massage=$timestamp."<span>".__('[RECOVERABLE ERROR]','backwpup')." ".$errstr."</span>";
44
- break;
45
- default:
46
- $massage=$timestamp."<span>[".$errno."] ".$errstr."</span>";
47
- break;
48
- }
49
-
50
- if (!empty($massage)) {
51
- //wirte log file
52
- $fd=@fopen($backwpup_logfile,'a+');
53
- @fputs($fd,$massage."<br />\n");
54
- @fclose($fd);
55
-
56
- //output on run now
57
- if (!defined('DOING_CRON')) {
58
- echo $massage."<script type=\"text/javascript\">window.scrollBy(0, 15);</script><br />\n";
59
- @flush();
60
- @ob_flush();
61
- }
62
-
63
- //write new log header
64
- if (isset($errors) or isset($warnings)) {
65
- $fd=@fopen($backwpup_logfile,'r+');
66
- while (!feof($fd)) {
67
- $line=@fgets($fd);
68
- if (stripos($line,"<meta name=\"backwpup_errors\"") !== false and isset($errors)) {
69
- @fseek($fd,$filepos);
70
- @fputs($fd,str_pad("<meta name=\"backwpup_errors\" content=\"".$errors."\" />",100)."\n");
71
- break;
72
- }
73
- if (stripos($line,"<meta name=\"backwpup_warnings\"") !== false and isset($warnings)) {
74
- @fseek($fd,$filepos);
75
- @fputs($fd,str_pad("<meta name=\"backwpup_warnings\" content=\"".$warnings."\" />",100)."\n");
76
- break;
77
- }
78
- $filepos=ftell($fd);
79
- }
80
- @fclose($fd);
81
- }
82
-
83
- if ($errno==E_ERROR or $errno==E_CORE_ERROR or $errno==E_COMPILE_ERROR) {//Die on fatal php errors.
84
- $fd=@fopen($backwpup_logfile,'a+');
85
- fputs($fd,"</body>\n</html>\n");
86
- fclose($fd);
87
- die();
88
- }
89
- //300 is most webserver time limit. 0= max time! Give script 5 min. more to work.
90
- @set_time_limit(300);
91
- //true for no more php error hadling.
92
- return true;
93
- } else {
94
- return false;
95
- }
96
- }
97
-
98
  /**
99
  * BackWPup PHP class for WordPress
100
  *
@@ -109,14 +17,14 @@ class backwpup_dojob {
109
  private $backupfile='';
110
  private $backupfileformat='.zip';
111
  private $backupdir='';
112
- private $logdir='';
113
- private $logfile='';
114
  private $tempdir='';
115
  private $cfg=array();
116
  private $job=array();
117
 
118
  public function __construct($jobid) {
119
- global $wpdb,$backwpup_logfile;
120
  @ini_get('safe_mode','Off'); //disable safe mode
121
  @ini_set('ignore_user_abort','Off'); //Set PHP ini setting
122
  ignore_user_abort(true); //user can't abort script (close windows or so.)
@@ -135,8 +43,7 @@ class backwpup_dojob {
135
  return false;
136
  //set Log file name
137
  $this->logfile='backwpup_log_'.date_i18n('Y-m-d_H-i-s').'.html';
138
- //set global for error handling
139
- $backwpup_logfile=$this->logdir.$this->logfile;
140
  $fd=fopen($this->logdir.$this->logfile,'w+');
141
  //Create log file header
142
  @fputs($fd,"<html>\n<head>\n");
@@ -155,9 +62,9 @@ class backwpup_dojob {
155
  @fclose($fd);
156
  //set function for PHP user defineid error handling
157
  if (defined(WP_DEBUG) and WP_DEBUG)
158
- set_error_handler('backwpup_joberrorhandler',E_ALL | E_STRICT);
159
  else
160
- set_error_handler('backwpup_joberrorhandler',E_ALL & ~E_NOTICE);
161
  //find out if job already running and abort if
162
  if ($jobs[$this->jobid]['starttime']>0 and !empty($jobs[$this->jobid]['logfile'])) {
163
  if ($jobs[$this->jobid]['starttime']+600<current_time('timestamp')) { //Abort old jo if work longer as 10 min. because websever has 300 sec timeout
@@ -242,6 +149,7 @@ class backwpup_dojob {
242
  if (is_file($this->backupdir.$this->backupfile)) { // Put backup file to destination
243
  $this->destination_mail();
244
  $this->destination_ftp();
 
245
  $this->destination_s3();
246
  $this->destination_rsc();
247
  $this->destination_dir();
@@ -261,6 +169,99 @@ class backwpup_dojob {
261
  $this->job_end(); //call regualar job end
262
  }
263
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  private function _check_folders($folder) {
265
  if (!is_dir($folder)) { //create dir if not exists
266
  if (!mkdir($folder,0777,true)) {
@@ -886,20 +887,27 @@ class backwpup_dojob {
886
  if (false !== strpos($this->job['ftphost'],':')) //look for port
887
  list($ftphost,$ftpport)=explode(':',$this->job['ftphost'],2);
888
 
889
- if (function_exists('ftp_ssl_connect')) { //make SSL FTP connection
890
- $ftp_conn_id = ftp_ssl_connect($ftphost,$ftpport,10);
891
- if ($ftp_conn_id)
892
- trigger_error(__('Connected by SSL to FTP server:','backwpup').' '.$this->job['ftphost'],E_USER_NOTICE);
893
- }
894
- if (!$ftp_conn_id) { //make normal FTP conection if SSL not work
 
 
 
 
 
 
 
 
895
  $ftp_conn_id = ftp_connect($ftphost,$ftpport,10);
896
  if ($ftp_conn_id)
897
- trigger_error(__('Connected insecure to FTP server:','backwpup').' '.$this->job['ftphost'],E_USER_NOTICE);
898
- }
899
-
900
- if (!$ftp_conn_id) {
901
- trigger_error(__('Can not connect to FTP server:','backwpup').' '.$this->job['ftphost'],E_USER_ERROR);
902
- return false;
903
  }
904
 
905
  //FTP Login
@@ -995,12 +1003,12 @@ class backwpup_dojob {
995
 
996
  trigger_error(__('Prepare Sending backup file with mail...','backwpup'),E_USER_NOTICE);
997
 
998
- //Crate PHP Mailer
999
- include_once(ABSPATH.WPINC.'/class-phpmailer.php');
1000
- include_once(ABSPATH.WPINC.'/class-smtp.php');
1001
  $phpmailer = new PHPMailer();
1002
  //Setting den methode
1003
  if ($this->cfg['mailmethod']=="SMTP") {
 
1004
  $smtpport=25;
1005
  $smtphost=$this->cfg['mailhost'];
1006
  if (false !== strpos($this->cfg['mailhost'],':')) //look for port
@@ -1220,6 +1228,25 @@ class backwpup_dojob {
1220
  }
1221
  }
1222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1223
  private function job_end() {
1224
 
1225
  if ($filesize=filesize($this->backupdir.$this->backupfile))
@@ -1309,13 +1336,46 @@ class backwpup_dojob {
1309
  if (!$this->job['mailerroronly'] and !empty($this->job['mailaddresslog']))
1310
  $sendmail=true;
1311
  if ($sendmail) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1312
  $mailbody=__("Jobname:","backwpup")." ".$logdata['name']."\n";
1313
  $mailbody.=__("Jobtype:","backwpup")." ".$logdata['type']."\n";
1314
  if (!empty($logdata['errors']))
1315
  $mailbody.=__("Errors:","backwpup")." ".$logdata['errors']."\n";
1316
  if (!empty($logdata['warnings']))
1317
  $mailbody.=__("Warnings:","backwpup")." ".$logdata['warnings']."\n";
1318
- wp_mail($this->job['mailaddresslog'],__('BackWPup Log from','backwpup').' '.date_i18n('Y-m-d H:i').': '.$this->job['name'] ,$mailbody,'',array($this->logdir.$this->logfile));
 
 
 
 
 
 
 
 
1319
  }
1320
  }
1321
  }
3
  if ( !defined('ABSPATH') )
4
  die('-1');
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  /**
7
  * BackWPup PHP class for WordPress
8
  *
17
  private $backupfile='';
18
  private $backupfileformat='.zip';
19
  private $backupdir='';
20
+ public $logdir='';
21
+ public $logfile='';
22
  private $tempdir='';
23
  private $cfg=array();
24
  private $job=array();
25
 
26
  public function __construct($jobid) {
27
+ global $wpdb;
28
  @ini_get('safe_mode','Off'); //disable safe mode
29
  @ini_set('ignore_user_abort','Off'); //Set PHP ini setting
30
  ignore_user_abort(true); //user can't abort script (close windows or so.)
43
  return false;
44
  //set Log file name
45
  $this->logfile='backwpup_log_'.date_i18n('Y-m-d_H-i-s').'.html';
46
+ //create log file
 
47
  $fd=fopen($this->logdir.$this->logfile,'w+');
48
  //Create log file header
49
  @fputs($fd,"<html>\n<head>\n");
62
  @fclose($fd);
63
  //set function for PHP user defineid error handling
64
  if (defined(WP_DEBUG) and WP_DEBUG)
65
+ set_error_handler(array($this,'joberrorhandler'),E_ALL | E_STRICT);
66
  else
67
+ set_error_handler(array($this,'joberrorhandler'),E_ALL & ~E_NOTICE);
68
  //find out if job already running and abort if
69
  if ($jobs[$this->jobid]['starttime']>0 and !empty($jobs[$this->jobid]['logfile'])) {
70
  if ($jobs[$this->jobid]['starttime']+600<current_time('timestamp')) { //Abort old jo if work longer as 10 min. because websever has 300 sec timeout
149
  if (is_file($this->backupdir.$this->backupfile)) { // Put backup file to destination
150
  $this->destination_mail();
151
  $this->destination_ftp();
152
+ $this->destination_dropbox();
153
  $this->destination_s3();
154
  $this->destination_rsc();
155
  $this->destination_dir();
169
  $this->job_end(); //call regualar job end
170
  }
171
 
172
+ //function for PHP error handling
173
+ public function joberrorhandler() {
174
+ $args = func_get_args(); // 0:errno, 1:errstr, 2:errfile, 3:errline
175
+
176
+ //genrate timestamp
177
+ if (!function_exists('memory_get_usage')) { // test if memory functions compiled in
178
+ $timestamp="<span style=\"background-color:c3c3c3;\" title=\"[Line: ".$args[3]."|File: ".basename($args[2])."\">".date_i18n('Y-m-d H:i.s').":</span> ";
179
+ } else {
180
+ $timestamp="<span style=\"background-color:c3c3c3;\" title=\"[Line: ".$args[3]."|File: ".basename($args[2])."|Mem: ".backwpup_formatBytes(@memory_get_usage(true))."|Mem Max: ".backwpup_formatBytes(@memory_get_peak_usage(true))."|Mem Limit: ".ini_get('memory_limit')."]\">".date_i18n('Y-m-d H:i.s').":</span> ";
181
+ }
182
+
183
+ switch ($args[0]) {
184
+ case E_NOTICE:
185
+ case E_USER_NOTICE:
186
+ $massage=$timestamp."<span>".$args[1]."</span>";
187
+ break;
188
+ case E_WARNING:
189
+ case E_USER_WARNING:
190
+ $logheader=backwpup_read_logheader($this->logdir.$this->logfile); //read waring count from log header
191
+ $warnings=$logheader['warnings']+1;
192
+ $massage=$timestamp."<span style=\"background-color:yellow;\">".__('[WARNING]','backwpup')." ".$args[1]."</span>";
193
+ break;
194
+ case E_ERROR:
195
+ case E_USER_ERROR:
196
+ $logheader=backwpup_read_logheader($this->logdir.$this->logfile); //read error count from log header
197
+ $errors=$logheader['errors']+1;
198
+ $massage=$timestamp."<span style=\"background-color:red;\">".__('[ERROR]','backwpup')." ".$args[1]."</span>";
199
+ break;
200
+ case E_DEPRECATED:
201
+ case E_USER_DEPRECATED:
202
+ $massage=$timestamp."<span>".__('[DEPRECATED]','backwpup')." ".$args[1]."</span>";
203
+ break;
204
+ case E_STRICT:
205
+ $massage=$timestamp."<span>".__('[STRICT NOTICE]','backwpup')." ".$args[1]."</span>";
206
+ break;
207
+ case E_RECOVERABLE_ERROR:
208
+ $massage=$timestamp."<span>".__('[RECOVERABLE ERROR]','backwpup')." ".$args[1]."</span>";
209
+ break;
210
+ default:
211
+ $massage=$timestamp."<span>[".$args[0]."] ".$args[1]."</span>";
212
+ break;
213
+ }
214
+
215
+ if (!empty($massage)) {
216
+ //wirte log file
217
+ $fd=fopen($this->logdir.$this->logfile,'a+');
218
+ @fputs($fd,$massage."<br />\n");
219
+ @fclose($fd);
220
+
221
+ //output on run now
222
+ if (!defined('DOING_CRON')) {
223
+ echo $massage."<script type=\"text/javascript\">window.scrollBy(0, 15);</script><br />\n";
224
+ @flush();
225
+ @ob_flush();
226
+ }
227
+
228
+ //write new log header
229
+ if (isset($errors) or isset($warnings)) {
230
+ if ($fd=fopen($this->logdir.$this->logfile,'r+')) {
231
+ while (!feof($fd)) {
232
+ $line=@fgets($fd);
233
+ if (stripos($line,"<meta name=\"backwpup_errors\"") !== false and isset($errors)) {
234
+ @fseek($fd,$filepos);
235
+ @fputs($fd,str_pad("<meta name=\"backwpup_errors\" content=\"".$errors."\" />",100)."\n");
236
+ break;
237
+ }
238
+ if (stripos($line,"<meta name=\"backwpup_warnings\"") !== false and isset($warnings)) {
239
+ @fseek($fd,$filepos);
240
+ @fputs($fd,str_pad("<meta name=\"backwpup_warnings\" content=\"".$warnings."\" />",100)."\n");
241
+ break;
242
+ }
243
+ $filepos=ftell($fd);
244
+ }
245
+ @fclose($fd);
246
+ }
247
+ }
248
+
249
+ if ($args[0]==E_ERROR or $args[0]==E_CORE_ERROR or $args[0]==E_COMPILE_ERROR) {//Die on fatal php errors.
250
+ if ($fd=fopen($this->logdir.$this->logfile,'a+')) {
251
+ fputs($fd,"</body>\n</html>\n");
252
+ fclose($fd);
253
+ }
254
+ die();
255
+ }
256
+ //300 is most webserver time limit. 0= max time! Give script 5 min. more to work.
257
+ @set_time_limit(300);
258
+ //true for no more php error hadling.
259
+ return true;
260
+ } else {
261
+ return false;
262
+ }
263
+ }
264
+
265
  private function _check_folders($folder) {
266
  if (!is_dir($folder)) { //create dir if not exists
267
  if (!mkdir($folder,0777,true)) {
887
  if (false !== strpos($this->job['ftphost'],':')) //look for port
888
  list($ftphost,$ftpport)=explode(':',$this->job['ftphost'],2);
889
 
890
+ if ($this->job['ftpssl']) { //make SSL FTP connection
891
+ if (function_exists('ftp_ssl_connect')) {
892
+ $ftp_conn_id = ftp_ssl_connect($ftphost,$ftpport,10);
893
+ if ($ftp_conn_id)
894
+ trigger_error(__('Connected by SSL-FTP to Server:','backwpup').' '.$this->job['ftphost'],E_USER_NOTICE);
895
+ else {
896
+ trigger_error(__('Can not connect by SSL-FTP to Server:','backwpup').' '.$this->job['ftphost'],E_USER_ERROR);
897
+ return false;
898
+ }
899
+ } else {
900
+ trigger_error(__('PHP Function to connect with SSL-FTP to Server not exists!','backwpup'),E_USER_ERROR);
901
+ return false;
902
+ }
903
+ } else { //make normal FTP conection if SSL not work
904
  $ftp_conn_id = ftp_connect($ftphost,$ftpport,10);
905
  if ($ftp_conn_id)
906
+ trigger_error(__('Connected to FTP Server:','backwpup').' '.$this->job['ftphost'],E_USER_NOTICE);
907
+ else {
908
+ trigger_error(__('Can not connect to FTP Server:','backwpup').' '.$this->job['ftphost'],E_USER_ERROR);
909
+ return false;
910
+ }
 
911
  }
912
 
913
  //FTP Login
1003
 
1004
  trigger_error(__('Prepare Sending backup file with mail...','backwpup'),E_USER_NOTICE);
1005
 
1006
+ //Create PHP Mailer
1007
+ require_once(ABSPATH.WPINC.'/class-phpmailer.php');
 
1008
  $phpmailer = new PHPMailer();
1009
  //Setting den methode
1010
  if ($this->cfg['mailmethod']=="SMTP") {
1011
+ require_once(ABSPATH.WPINC.'/class-smtp.php');
1012
  $smtpport=25;
1013
  $smtphost=$this->cfg['mailhost'];
1014
  if (false !== strpos($this->cfg['mailhost'],':')) //look for port
1228
  }
1229
  }
1230
 
1231
+ private function destination_dropbox(){
1232
+ if (empty($this->job['dropemail']) or empty($this->job['dropepass']))
1233
+ return;
1234
+
1235
+ if (!(extension_loaded('curl') or @dl(PHP_SHLIB_SUFFIX == 'so' ? 'curl.so' : 'php_curl.dll'))) {
1236
+ trigger_error(__('Can not load curl extension is needed for Dropbox!','backwpup'),E_USER_ERROR);
1237
+ return;
1238
+ }
1239
+
1240
+ if (!class_exists('DropboxUploader'))
1241
+ try {
1242
+ require_once (dirname(__FILE__).'/libs/DropboxUploader.php');
1243
+ $uploader = new DropboxUploader($this->job['dropemail'], base64_decode($this->job['dropepass']));
1244
+ $uploader->upload($this->backupdir.$this->backupfile,$this->job['dropedir']);
1245
+ } catch (Exception $e) {
1246
+ trigger_error(__('DropBox:','backwpup').' '.__($e->getMessage(),'backwpup'),E_USER_ERROR);
1247
+ }
1248
+ }
1249
+
1250
  private function job_end() {
1251
 
1252
  if ($filesize=filesize($this->backupdir.$this->backupfile))
1336
  if (!$this->job['mailerroronly'] and !empty($this->job['mailaddresslog']))
1337
  $sendmail=true;
1338
  if ($sendmail) {
1339
+ //Create PHP Mailer
1340
+ require_once(ABSPATH.WPINC.'/class-phpmailer.php');
1341
+ $phpmailer = new PHPMailer();
1342
+ //Setting den methode
1343
+ if ($this->cfg['mailmethod']=="SMTP") {
1344
+ require_once(ABSPATH.WPINC.'/class-smtp.php');
1345
+ $smtpport=25;
1346
+ $smtphost=$this->cfg['mailhost'];
1347
+ if (false !== strpos($this->cfg['mailhost'],':')) //look for port
1348
+ list($smtphost,$smtpport)=explode(':',$this->cfg['mailhost'],2);
1349
+ $phpmailer->Host=$smtphost;
1350
+ $phpmailer->Port=$smtpport;
1351
+ $phpmailer->SMTPSecure=$this->cfg['mailsecure'];
1352
+ $phpmailer->Username=$this->cfg['mailuser'];
1353
+ $phpmailer->Password=base64_decode($this->cfg['mailpass']);
1354
+ if (!empty($this->cfg['mailuser']) and !empty($this->cfg['mailpass']))
1355
+ $phpmailer->SMTPAuth=true;
1356
+ $phpmailer->IsSMTP();
1357
+ } elseif ($this->cfg['mailmethod']=="Sendmail") {
1358
+ $phpmailer->Sendmail=$this->cfg['mailsendmail'];
1359
+ $phpmailer->IsSendmail();
1360
+ } else {
1361
+ $phpmailer->IsMail();
1362
+ }
1363
+
1364
  $mailbody=__("Jobname:","backwpup")." ".$logdata['name']."\n";
1365
  $mailbody.=__("Jobtype:","backwpup")." ".$logdata['type']."\n";
1366
  if (!empty($logdata['errors']))
1367
  $mailbody.=__("Errors:","backwpup")." ".$logdata['errors']."\n";
1368
  if (!empty($logdata['warnings']))
1369
  $mailbody.=__("Warnings:","backwpup")." ".$logdata['warnings']."\n";
1370
+
1371
+ $phpmailer->From = $this->cfg['mailsndemail'];
1372
+ $phpmailer->FromName = $this->cfg['mailsndname'];
1373
+ $phpmailer->AddAddress($this->job['mailaddresslog']);
1374
+ $phpmailer->Subject = __('BackWPup Logfile','backwpup').' '.$this->logfile.': '.$this->job['name'];
1375
+ $phpmailer->IsHTML(false);
1376
+ $phpmailer->Body = $mailbody;
1377
+ $phpmailer->AddAttachment($this->logdir.$this->logfile);
1378
+ $phpmailer->Send();
1379
  }
1380
  }
1381
  }
app/compatibility/class-wp-list-table.php ADDED
@@ -0,0 +1,993 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Base class for displaying a list of items in an ajaxified HTML table.
4
+ *
5
+ * @package WordPress
6
+ * @subpackage List_Table
7
+ * @since 3.1.0
8
+ */
9
+
10
+ /**
11
+ * Base class for displaying a list of items in an ajaxified HTML table.
12
+ *
13
+ * @package WordPress
14
+ * @subpackage List_Table
15
+ * @since 3.1.0
16
+ * @access private
17
+ */
18
+ class WP_List_Table {
19
+
20
+ /**
21
+ * The current list of items
22
+ *
23
+ * @since 3.1.0
24
+ * @var array
25
+ * @access protected
26
+ */
27
+ var $items;
28
+
29
+ /**
30
+ * Various information about the current table
31
+ *
32
+ * @since 3.1.0
33
+ * @var array
34
+ * @access private
35
+ */
36
+ var $_args;
37
+
38
+ /**
39
+ * Various information needed for displaying the pagination
40
+ *
41
+ * @since 3.1.0
42
+ * @var array
43
+ * @access private
44
+ */
45
+ var $_pagination_args = array();
46
+
47
+ /**
48
+ * The current screen
49
+ *
50
+ * @since 3.1.0
51
+ * @var object
52
+ * @access protected
53
+ */
54
+ var $screen;
55
+
56
+ /**
57
+ * Cached bulk actions
58
+ *
59
+ * @since 3.1.0
60
+ * @var array
61
+ * @access private
62
+ */
63
+ var $_actions;
64
+
65
+ /**
66
+ * Cached pagination output
67
+ *
68
+ * @since 3.1.0
69
+ * @var string
70
+ * @access private
71
+ */
72
+ var $_pagination;
73
+
74
+ /**
75
+ * Constructor. The child class should call this constructor from it's own constructor
76
+ *
77
+ * @param array $args An associative array with information about the current table
78
+ * @access protected
79
+ */
80
+ function WP_List_Table( $args = array() ) {
81
+ $args = wp_parse_args( $args, array(
82
+ 'plural' => '',
83
+ 'singular' => '',
84
+ 'ajax' => false
85
+ ) );
86
+
87
+ $screen = get_current_screen();
88
+
89
+ add_filter( "manage_{$screen->id}_columns", array( &$this, 'get_columns' ), 0 );
90
+
91
+ if ( !$args['plural'] )
92
+ $args['plural'] = $screen->base;
93
+
94
+ $this->_args = $args;
95
+
96
+ if ( $args['ajax'] ) {
97
+ // wp_enqueue_script( 'list-table' );
98
+ add_action( 'admin_footer', array( &$this, '_js_vars' ) );
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Checks the current user's permissions
104
+ * @uses wp_die()
105
+ *
106
+ * @since 3.1.0
107
+ * @access public
108
+ * @abstract
109
+ */
110
+ function ajax_user_can() {
111
+ die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
112
+ }
113
+
114
+ /**
115
+ * Prepares the list of items for displaying.
116
+ * @uses WP_List_Table::set_pagination_args()
117
+ *
118
+ * @since 3.1.0
119
+ * @access public
120
+ * @abstract
121
+ */
122
+ function prepare_items() {
123
+ die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
124
+ }
125
+
126
+ /**
127
+ * An internal method that sets all the necessary pagination arguments
128
+ *
129
+ * @param array $args An associative array with information about the pagination
130
+ * @access protected
131
+ */
132
+ function set_pagination_args( $args ) {
133
+ $args = wp_parse_args( $args, array(
134
+ 'total_items' => 0,
135
+ 'total_pages' => 0,
136
+ 'per_page' => 0,
137
+ ) );
138
+
139
+ if ( !$args['total_pages'] && $args['per_page'] > 0 )
140
+ $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
141
+
142
+ $this->_pagination_args = $args;
143
+ }
144
+
145
+ /**
146
+ * Access the pagination args
147
+ *
148
+ * @since 3.1.0
149
+ * @access public
150
+ *
151
+ * @param string $key
152
+ * @return array
153
+ */
154
+ function get_pagination_arg( $key ) {
155
+ if ( 'page' == $key )
156
+ return $this->get_pagenum();
157
+
158
+ if ( isset( $this->_pagination_args[$key] ) )
159
+ return $this->_pagination_args[$key];
160
+ }
161
+
162
+ /**
163
+ * Whether the table has items to display or not
164
+ *
165
+ * @since 3.1.0
166
+ * @access public
167
+ *
168
+ * @return bool
169
+ */
170
+ function has_items() {
171
+ return !empty( $this->items );
172
+ }
173
+
174
+ /**
175
+ * Message to be displayed when there are no items
176
+ *
177
+ * @since 3.1.0
178
+ * @access public
179
+ */
180
+ function no_items() {
181
+ _e( 'No items found.' );
182
+ }
183
+
184
+ /**
185
+ * Display the search box.
186
+ *
187
+ * @since 3.1.0
188
+ * @access public
189
+ *
190
+ * @param string $text The search button text
191
+ * @param string $input_id The search input id
192
+ */
193
+ function search_box( $text, $input_id ) {
194
+ if ( empty( $_REQUEST['s'] ) && !$this->has_items() )
195
+ return;
196
+
197
+ $input_id = $input_id . '-search-input';
198
+
199
+ if ( ! empty( $_REQUEST['orderby'] ) )
200
+ echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
201
+ if ( ! empty( $_REQUEST['order'] ) )
202
+ echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
203
+ ?>
204
+ <p class="search-box">
205
+ <label class="screen-reader-text" for="<?php echo $input_id ?>"><?php echo $text; ?>:</label>
206
+ <input type="text" id="<?php echo $input_id ?>" name="s" value="<?php _admin_search_query(); ?>" />
207
+ <?php submit_button( $text, 'button', false, false, array('id' => 'search-submit') ); ?>
208
+ </p>
209
+ <?php
210
+ }
211
+
212
+ /**
213
+ * Get an associative array ( id => link ) with the list
214
+ * of views available on this table.
215
+ *
216
+ * @since 3.1.0
217
+ * @access protected
218
+ *
219
+ * @return array
220
+ */
221
+ function get_views() {
222
+ return array();
223
+ }
224
+
225
+ /**
226
+ * Display the bulk actions dropdown.
227
+ *
228
+ * @since 3.1.0
229
+ * @access public
230
+ */
231
+ function views() {
232
+ $screen = get_current_screen();
233
+
234
+ $views = $this->get_views();
235
+ $views = apply_filters( 'views_' . $screen->id, $views );
236
+
237
+ if ( empty( $views ) )
238
+ return;
239
+
240
+ echo "<ul class='subsubsub'>\n";
241
+ foreach ( $views as $class => $view ) {
242
+ $views[ $class ] = "\t<li class='$class'>$view";
243
+ }
244
+ echo implode( " |</li>\n", $views ) . "</li>\n";
245
+ echo "</ul>";
246
+ }
247
+
248
+ /**
249
+ * Get an associative array ( option_name => option_title ) with the list
250
+ * of bulk actions available on this table.
251
+ *
252
+ * @since 3.1.0
253
+ * @access protected
254
+ *
255
+ * @return array
256
+ */
257
+ function get_bulk_actions() {
258
+ return array();
259
+ }
260
+
261
+ /**
262
+ * Display the bulk actions dropdown.
263
+ *
264
+ * @since 3.1.0
265
+ * @access public
266
+ */
267
+ function bulk_actions() {
268
+ $screen = get_current_screen();
269
+
270
+ if ( is_null( $this->_actions ) ) {
271
+ $no_new_actions = $this->_actions = $this->get_bulk_actions();
272
+ // This filter can currently only be used to remove actions.
273
+ $this->_actions = apply_filters( 'bulk_actions-' . $screen->id, $this->_actions );
274
+ $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
275
+ $two = '';
276
+ } else {
277
+ $two = '2';
278
+ }
279
+
280
+ if ( empty( $this->_actions ) )
281
+ return;
282
+
283
+ echo "<select name='action$two'>\n";
284
+ echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions' ) . "</option>\n";
285
+ foreach ( $this->_actions as $name => $title )
286
+ echo "\t<option value='$name'>$title</option>\n";
287
+ echo "</select>\n";
288
+
289
+ submit_button( __( 'Apply' ), 'button-secondary action', false, false, array( 'id' => "doaction$two" ) );
290
+ echo "\n";
291
+ }
292
+
293
+ /**
294
+ * Get the current action selected from the bulk actions dropdown.
295
+ *
296
+ * @since 3.1.0
297
+ * @access public
298
+ *
299
+ * @return string|bool The action name or False if no action was selected
300
+ */
301
+ function current_action() {
302
+ if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] )
303
+ return $_REQUEST['action'];
304
+
305
+ if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] )
306
+ return $_REQUEST['action2'];
307
+
308
+ return false;
309
+ }
310
+
311
+ /**
312
+ * Generate row actions div
313
+ *
314
+ * @since 3.1.0
315
+ * @access protected
316
+ *
317
+ * @param array $actions The list of actions
318
+ * @param bool $always_visible Wether the actions should be always visible
319
+ * @return string
320
+ */
321
+ function row_actions( $actions, $always_visible = false ) {
322
+ $action_count = count( $actions );
323
+ $i = 0;
324
+
325
+ if ( !$action_count )
326
+ return '';
327
+
328
+ $out = '<div class="' . ( $always_visible ? 'row-actions-visible' : 'row-actions' ) . '">';
329
+ foreach ( $actions as $action => $link ) {
330
+ ++$i;
331
+ ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
332
+ $out .= "<span class='$action'>$link$sep</span>";
333
+ }
334
+ $out .= '</div>';
335
+
336
+ return $out;
337
+ }
338
+
339
+ /**
340
+ * Display a monthly dropdown for filtering items
341
+ *
342
+ * @since 3.1.0
343
+ * @access protected
344
+ */
345
+ function months_dropdown( $post_type ) {
346
+ global $wpdb, $wp_locale;
347
+
348
+ $months = $wpdb->get_results( $wpdb->prepare( "
349
+ SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
350
+ FROM $wpdb->posts
351
+ WHERE post_type = %s
352
+ ORDER BY post_date DESC
353
+ ", $post_type ) );
354
+
355
+ $month_count = count( $months );
356
+
357
+ if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) )
358
+ return;
359
+
360
+ $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
361
+ ?>
362
+ <select name='m'>
363
+ <option<?php selected( $m, 0 ); ?> value='0'><?php _e( 'Show all dates' ); ?></option>
364
+ <?php
365
+ foreach ( $months as $arc_row ) {
366
+ if ( 0 == $arc_row->year )
367
+ continue;
368
+
369
+ $month = zeroise( $arc_row->month, 2 );
370
+ $year = $arc_row->year;
371
+
372
+ printf( "<option %s value='%s'>%s</option>\n",
373
+ selected( $m, $year . $month, false ),
374
+ esc_attr( $arc_row->year . $month ),
375
+ $wp_locale->get_month( $month ) . " $year"
376
+ );
377
+ }
378
+ ?>
379
+ </select>
380
+ <?php
381
+ }
382
+
383
+ /**
384
+ * Display a view switcher
385
+ *
386
+ * @since 3.1.0
387
+ * @access protected
388
+ */
389
+ function view_switcher( $current_mode ) {
390
+ $modes = array(
391
+ 'list' => __( 'List View' ),
392
+ 'excerpt' => __( 'Excerpt View' )
393
+ );
394
+
395
+ ?>
396
+ <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
397
+ <div class="view-switch">
398
+ <?php
399
+ foreach ( $modes as $mode => $title ) {
400
+ $class = ( $current_mode == $mode ) ? 'class="current"' : '';
401
+ echo "<a href='" . esc_url( add_query_arg( 'mode', $mode, $_SERVER['REQUEST_URI'] ) ) . "' $class><img id='view-switch-$mode' src='" . esc_url( includes_url( 'images/blank.gif' ) ) . "' width='20' height='20' title='$title' alt='$title' /></a>\n";
402
+ }
403
+ ?>
404
+ </div>
405
+ <?php
406
+ }
407
+
408
+ /**
409
+ * Display a comment count bubble
410
+ *
411
+ * @since 3.1.0
412
+ * @access protected
413
+ *
414
+ * @param int $post_id
415
+ * @param int $pending_comments
416
+ */
417
+ function comments_bubble( $post_id, $pending_comments ) {
418
+ $pending_phrase = sprintf( __( '%s pending' ), number_format( $pending_comments ) );
419
+
420
+ if ( $pending_comments )
421
+ echo '<strong>';
422
+
423
+ $link = "<a href='" . add_query_arg( 'p', $post_id, admin_url('edit-comments.php') ) . "' title='$pending_phrase' class='post-com-count'><span class='comment-count'>%s</span></a>";
424
+
425
+ comments_number(
426
+ sprintf( $link, /* translators: comment count link */ _x( '0', 'comment count' ) ),
427
+ sprintf( $link, /* translators: comment count link */ _x( '1', 'comment count' ) ),
428
+ sprintf( $link, /* translators: comment count link: % will be substituted by comment count */ _x( '%', 'comment count' ) )
429
+ );
430
+
431
+ if ( $pending_comments )
432
+ echo '</strong>';
433
+ }
434
+
435
+ /**
436
+ * Get the current page number
437
+ *
438
+ * @since 3.1.0
439
+ * @access protected
440
+ *
441
+ * @return int
442
+ */
443
+ function get_pagenum() {
444
+ $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
445
+
446
+ if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
447
+ $pagenum = $this->_pagination_args['total_pages'];
448
+
449
+ return max( 1, $pagenum );
450
+ }
451
+
452
+ /**
453
+ * Get number of items to display on a single page
454
+ *
455
+ * @since 3.1.0
456
+ * @access protected
457
+ *
458
+ * @return int
459
+ */
460
+ function get_items_per_page( $option, $default = 20 ) {
461
+ $per_page = (int) get_user_option( $option );
462
+ if ( empty( $per_page ) || $per_page < 1 )
463
+ $per_page = $default;
464
+
465
+ return (int) apply_filters( $option, $per_page );
466
+ }
467
+
468
+ /**
469
+ * Display the pagination.
470
+ *
471
+ * @since 3.1.0
472
+ * @access protected
473
+ */
474
+ function pagination( $which ) {
475
+ if ( empty( $this->_pagination_args ) )
476
+ return;
477
+
478
+ extract( $this->_pagination_args );
479
+
480
+ $output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
481
+
482
+ $current = $this->get_pagenum();
483
+
484
+ $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
485
+
486
+ $current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
487
+
488
+ $page_links = array();
489
+
490
+ $disable_first = $disable_last = '';
491
+ if ( $current == 1 )
492
+ $disable_first = ' disabled';
493
+ if ( $current == $total_pages )
494
+ $disable_last = ' disabled';
495
+
496
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
497
+ 'first-page' . $disable_first,
498
+ esc_attr__( 'Go to the first page' ),
499
+ esc_url( remove_query_arg( 'paged', $current_url ) ),
500
+ '&laquo;'
501
+ );
502
+
503
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
504
+ 'prev-page' . $disable_first,
505
+ esc_attr__( 'Go to the previous page' ),
506
+ esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
507
+ '&lsaquo;'
508
+ );
509
+
510
+ if ( 'bottom' == $which )
511
+ $html_current_page = $current;
512
+ else
513
+ $html_current_page = sprintf( "<input class='current-page' title='%s' type='text' name='%s' value='%s' size='%d' />",
514
+ esc_attr__( 'Current page' ),
515
+ esc_attr( 'paged' ),
516
+ $current,
517
+ strlen( $total_pages )
518
+ );
519
+
520
+ $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
521
+ $page_links[] = '<span class="paging-input">' . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . '</span>';
522
+
523
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
524
+ 'next-page' . $disable_last,
525
+ esc_attr__( 'Go to the next page' ),
526
+ esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
527
+ '&rsaquo;'
528
+ );
529
+
530
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
531
+ 'last-page' . $disable_last,
532
+ esc_attr__( 'Go to the last page' ),
533
+ esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
534
+ '&raquo;'
535
+ );
536
+
537
+ $output .= "\n" . join( "\n", $page_links );
538
+
539
+ $page_class = $total_pages < 2 ? ' one-page' : '';
540
+
541
+ $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
542
+
543
+ echo $this->_pagination;
544
+ }
545
+
546
+ /**
547
+ * Get a list of columns. The format is:
548
+ * 'internal-name' => 'Title'
549
+ *
550
+ * @since 3.1.0
551
+ * @access protected
552
+ * @abstract
553
+ *
554
+ * @return array
555
+ */
556
+ function get_columns() {
557
+ die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
558
+ }
559
+
560
+ /**
561
+ * Get a list of sortable columns. The format is:
562
+ * 'internal-name' => 'orderby'
563
+ * or
564
+ * 'internal-name' => array( 'orderby', true )
565
+ *
566
+ * The second format will make the initial sorting order be descending
567
+ *
568
+ * @since 3.1.0
569
+ * @access protected
570
+ *
571
+ * @return array
572
+ */
573
+ function get_sortable_columns() {
574
+ return array();
575
+ }
576
+
577
+ /**
578
+ * Get a list of all, hidden and sortable columns, with filter applied
579
+ *
580
+ * @since 3.1.0
581
+ * @access protected
582
+ *
583
+ * @return array
584
+ */
585
+ function get_column_info() {
586
+ if ( isset( $this->_column_headers ) )
587
+ return $this->_column_headers;
588
+
589
+ $screen = get_current_screen();
590
+
591
+ $columns = get_column_headers( $screen );
592
+ $hidden = get_hidden_columns( $screen );
593
+
594
+ $_sortable = apply_filters( "manage_{$screen->id}_sortable_columns", $this->get_sortable_columns() );
595
+
596
+ $sortable = array();
597
+ foreach ( $_sortable as $id => $data ) {
598
+ if ( empty( $data ) )
599
+ continue;
600
+
601
+ $data = (array) $data;
602
+ if ( !isset( $data[1] ) )
603
+ $data[1] = false;
604
+
605
+ $sortable[$id] = $data;
606
+ }
607
+
608
+ $this->_column_headers = array( $columns, $hidden, $sortable );
609
+
610
+ return $this->_column_headers;
611
+ }
612
+
613
+ /**
614
+ * Return number of visible columns
615
+ *
616
+ * @since 3.1.0
617
+ * @access public
618
+ *
619
+ * @return int
620
+ */
621
+ function get_column_count() {
622
+ list ( $columns, $hidden ) = $this->get_column_info();
623
+ $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
624
+ return count( $columns ) - count( $hidden );
625
+ }
626
+
627
+ /**
628
+ * Print column headers, accounting for hidden and sortable columns.
629
+ *
630
+ * @since 3.1.0
631
+ * @access protected
632
+ *
633
+ * @param bool $with_id Whether to set the id attribute or not
634
+ */
635
+ function print_column_headers( $with_id = true ) {
636
+ $screen = get_current_screen();
637
+
638
+ list( $columns, $hidden, $sortable ) = $this->get_column_info();
639
+
640
+ $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
641
+ $current_url = remove_query_arg( 'paged', $current_url );
642
+
643
+ if ( isset( $_GET['orderby'] ) )
644
+ $current_orderby = $_GET['orderby'];
645
+ else
646
+ $current_orderby = '';
647
+
648
+ if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] )
649
+ $current_order = 'desc';
650
+ else
651
+ $current_order = 'asc';
652
+
653
+ foreach ( $columns as $column_key => $column_display_name ) {
654
+ $class = array( 'manage-column', "column-$column_key" );
655
+
656
+ $style = '';
657
+ if ( in_array( $column_key, $hidden ) )
658
+ $style = 'display:none;';
659
+
660
+ $style = ' style="' . $style . '"';
661
+
662
+ if ( 'cb' == $column_key )
663
+ $class[] = 'check-column';
664
+ elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
665
+ $class[] = 'num';
666
+
667
+ if ( isset( $sortable[$column_key] ) ) {
668
+ list( $orderby, $desc_first ) = $sortable[$column_key];
669
+
670
+ if ( $current_orderby == $orderby ) {
671
+ $order = 'asc' == $current_order ? 'desc' : 'asc';
672
+ $class[] = 'sorted';
673
+ $class[] = $current_order;
674
+ } else {
675
+ $order = $desc_first ? 'desc' : 'asc';
676
+ $class[] = 'sortable';
677
+ $class[] = $desc_first ? 'asc' : 'desc';
678
+ }
679
+
680
+ $column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
681
+ }
682
+
683
+ $id = $with_id ? "id='$column_key'" : '';
684
+
685
+ if ( !empty( $class ) )
686
+ $class = "class='" . join( ' ', $class ) . "'";
687
+
688
+ echo "<th scope='col' $id $class $style>$column_display_name</th>";
689
+ }
690
+ }
691
+
692
+ /**
693
+ * Display the table
694
+ *
695
+ * @since 3.1.0
696
+ * @access public
697
+ */
698
+ function display() {
699
+ extract( $this->_args );
700
+
701
+ $this->display_tablenav( 'top' );
702
+
703
+ ?>
704
+ <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>" cellspacing="0">
705
+ <thead>
706
+ <tr>
707
+ <?php $this->print_column_headers(); ?>
708
+ </tr>
709
+ </thead>
710
+
711
+ <tfoot>
712
+ <tr>
713
+ <?php $this->print_column_headers( false ); ?>
714
+ </tr>
715
+ </tfoot>
716
+
717
+ <tbody id="the-list"<?php if ( $singular ) echo " class='list:$singular'"; ?>>
718
+ <?php $this->display_rows_or_placeholder(); ?>
719
+ </tbody>
720
+ </table>
721
+ <?php
722
+ $this->display_tablenav( 'bottom' );
723
+ }
724
+
725
+ /**
726
+ * Get a list of CSS classes for the <table> tag
727
+ *
728
+ * @since 3.1.0
729
+ * @access protected
730
+ *
731
+ * @return array
732
+ */
733
+ function get_table_classes() {
734
+ return array( 'widefat', 'fixed', $this->_args['plural'] );
735
+ }
736
+
737
+ /**
738
+ * Generate the table navigation above or below the table
739
+ *
740
+ * @since 3.1.0
741
+ * @access protected
742
+ */
743
+ function display_tablenav( $which ) {
744
+ if ( 'top' == $which )
745
+ wp_nonce_field( 'bulk-' . $this->_args['plural'] );
746
+ ?>
747
+ <div class="tablenav <?php echo esc_attr( $which ); ?>">
748
+
749
+ <div class="alignleft actions">
750
+ <?php $this->bulk_actions( $which ); ?>
751
+ </div>
752
+ <?php
753
+ $this->extra_tablenav( $which );
754
+ $this->pagination( $which );
755
+ ?>
756
+
757
+ <br class="clear" />
758
+ </div>
759
+ <?php
760
+ }
761
+
762
+ /**
763
+ * Extra controls to be displayed between bulk actions and pagination
764
+ *
765
+ * @since 3.1.0
766
+ * @access protected
767
+ */
768
+ function extra_tablenav( $which ) {}
769
+
770
+ /**
771
+ * Generate the <tbody> part of the table
772
+ *
773
+ * @since 3.1.0
774
+ * @access protected
775
+ */
776
+ function display_rows_or_placeholder() {
777
+ if ( $this->has_items() ) {
778
+ $this->display_rows();
779
+ } else {
780
+ list( $columns, $hidden ) = $this->get_column_info();
781
+ echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
782
+ $this->no_items();
783
+ echo '</td></tr>';
784
+ }
785
+ }
786
+
787
+ /**
788
+ * Generate the table rows
789
+ *
790
+ * @since 3.1.0
791
+ * @access protected
792
+ */
793
+ function display_rows() {
794
+ foreach ( $this->items as $item )
795
+ $this->single_row( $item );
796
+ }
797
+
798
+ /**
799
+ * Generates content for a single row of the table
800
+ *
801
+ * @since 3.1.0
802
+ * @access protected
803
+ *
804
+ * @param object $item The current item
805
+ */
806
+ function single_row( $item ) {
807
+ static $row_class = '';
808
+ $row_class = ( $row_class == '' ? ' class="alternate"' : '' );
809
+
810
+ echo '<tr' . $row_class . '>';
811
+ echo $this->single_row_columns( $item );
812
+ echo '</tr>';
813
+ }
814
+
815
+ /**
816
+ * Generates the columns for a single row of the table
817
+ *
818
+ * @since 3.1.0
819
+ * @access protected
820
+ *
821
+ * @param object $item The current item
822
+ */
823
+ function single_row_columns( $item ) {
824
+ list( $columns, $hidden ) = $this->get_column_info();
825
+
826
+ foreach ( $columns as $column_name => $column_display_name ) {
827
+ $class = "class='$column_name column-$column_name'";
828
+
829
+ $style = '';
830
+ if ( in_array( $column_name, $hidden ) )
831
+ $style = ' style="display:none;"';
832
+
833
+ $attributes = "$class$style";
834
+
835
+ if ( 'cb' == $column_name ) {
836
+ echo '<th scope="row" class="check-column">';
837
+ echo $this->column_cb( $item );
838
+ echo '</th>';
839
+ }
840
+ elseif ( method_exists( $this, 'column_' . $column_name ) ) {
841
+ echo "<td $attributes>";
842
+ echo call_user_func( array( &$this, 'column_' . $column_name ), $item );
843
+ echo "</td>";
844
+ }
845
+ else {
846
+ echo "<td $attributes>";
847
+ echo $this->column_default( $item, $column_name );
848
+ echo "</td>";
849
+ }
850
+ }
851
+ }
852
+
853
+ /**
854
+ * Handle an incoming ajax request (called from admin-ajax.php)
855
+ *
856
+ * @since 3.1.0
857
+ * @access public
858
+ */
859
+ function ajax_response() {
860
+ $this->prepare_items();
861
+
862
+ extract( $this->_args );
863
+ extract( $this->_pagination_args );
864
+
865
+ ob_start();
866
+ if ( ! empty( $_REQUEST['no_placeholder'] ) )
867
+ $this->display_rows();
868
+ else
869
+ $this->display_rows_or_placeholder();
870
+
871
+ $rows = ob_get_clean();
872
+
873
+ $response = array( 'rows' => $rows );
874
+
875
+ if ( isset( $total_items ) )
876
+ $response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) );
877
+
878
+ if ( isset( $total_pages ) ) {
879
+ $response['total_pages'] = $total_pages;
880
+ $response['total_pages_i18n'] = number_format_i18n( $total_pages );
881
+ }
882
+
883
+ die( json_encode( $response ) );
884
+ }
885
+
886
+ /**
887
+ * Send required variables to JavaScript land
888
+ *
889
+ * @access private
890
+ */
891
+ function _js_vars() {
892
+ $args = array(
893
+ 'class' => get_class( $this ),
894
+ 'screen' => get_current_screen()
895
+ );
896
+
897
+ printf( "<script type='text/javascript'>list_args = %s;</script>\n", json_encode( $args ) );
898
+ }
899
+ }
900
+
901
+
902
+ /**
903
+ * Get the current screen object
904
+ *
905
+ * @since 3.1.0
906
+ *
907
+ * @return object Current screen object
908
+ */
909
+ function get_current_screen() {
910
+ global $current_screen;
911
+
912
+ if ( !isset($current_screen) )
913
+ return null;
914
+
915
+ return $current_screen;
916
+ }
917
+ /**
918
+ * Echos a submit button, with provided text and appropriate class
919
+ *
920
+ * @since 3.1.0
921
+ *
922
+ * @param string $text The text of the button (defaults to 'Save Changes')
923
+ * @param string $type The type of button. One of: primary, secondary, delete
924
+ * @param string $name The HTML name of the submit button. Defaults to "submit". If no id attribute
925
+ * is given in $other_attributes below, $name will be used as the button's id.
926
+ * @param bool $wrap True if the output button should be wrapped in a paragraph tag,
927
+ * false otherwise. Defaults to true
928
+ * @param array|string $other_attributes Other attributes that should be output with the button,
929
+ * mapping attributes to their values, such as array( 'tabindex' => '1' ).
930
+ * These attributes will be ouput as attribute="value", such as tabindex="1".
931
+ * Defaults to no other attributes. Other attributes can also be provided as a
932
+ * string such as 'tabindex="1"', though the array format is typically cleaner.
933
+ */
934
+ function submit_button( $text = NULL, $type = 'primary', $name = 'submit', $wrap = true, $other_attributes = NULL ) {
935
+ echo get_submit_button( $text, $type, $name, $wrap, $other_attributes );
936
+ }
937
+ /**
938
+ * Returns a submit button, with provided text and appropriate class
939
+ *
940
+ * @since 3.1.0
941
+ *
942
+ * @param string $text The text of the button (defaults to 'Save Changes')
943
+ * @param string $type The type of button. One of: primary, secondary, delete
944
+ * @param string $name The HTML name of the submit button. Defaults to "submit". If no id attribute
945
+ * is given in $other_attributes below, $name will be used as the button's id.
946
+ * @param bool $wrap True if the output button should be wrapped in a paragraph tag,
947
+ * false otherwise. Defaults to true
948
+ * @param array|string $other_attributes Other attributes that should be output with the button,
949
+ * mapping attributes to their values, such as array( 'tabindex' => '1' ).
950
+ * These attributes will be ouput as attribute="value", such as tabindex="1".
951
+ * Defaults to no other attributes. Other attributes can also be provided as a
952
+ * string such as 'tabindex="1"', though the array format is typically cleaner.
953
+ */
954
+ function get_submit_button( $text = NULL, $type = 'primary', $name = 'submit', $wrap = true, $other_attributes = NULL ) {
955
+ switch ( $type ) :
956
+ case 'primary' :
957
+ case 'secondary' :
958
+ $class = 'button-' . $type;
959
+ break;
960
+ case 'delete' :
961
+ $class = 'button-secondary delete';
962
+ break;
963
+ default :
964
+ $class = $type; // Custom cases can just pass in the classes they want to be used
965
+ endswitch;
966
+ $text = ( NULL == $text ) ? __( 'Save Changes' ) : $text;
967
+
968
+ // Default the id attribute to $name unless an id was specifically provided in $other_attributes
969
+ $id = $name;
970
+ if ( is_array( $other_attributes ) && isset( $other_attributes['id'] ) ) {
971
+ $id = $other_attributes['id'];
972
+ unset( $other_attributes['id'] );
973
+ }
974
+
975
+ $attributes = '';
976
+ if ( is_array( $other_attributes ) ) {
977
+ foreach ( $other_attributes as $attribute => $value ) {
978
+ $attributes .= $attribute . '="' . esc_attr( $value ) . '" '; // Trailing space is important
979
+ }
980
+ } else if ( !empty( $other_attributes ) ) { // Attributes provided as a string
981
+ $attributes = $other_attributes;
982
+ }
983
+
984
+ $button = '<input type="submit" name="' . esc_attr( $name ) . '" id="' . esc_attr( $id ) . '" class="' . esc_attr( $class );
985
+ $button .= '" value="' . esc_attr( $text ) . '" ' . $attributes . ' />';
986
+
987
+ if ( $wrap ) {
988
+ $button = '<p class="submit">' . $button . '</p>';
989
+ }
990
+
991
+ return $button;
992
+ }
993
+ ?>
app/compatibility/list-table.php DELETED
@@ -1,667 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Base class for displaying a list of items as an ajaxified html table
5
- *
6
- * @package WordPress
7
- * @since 3.1.0
8
- */
9
- class WP_List_Table {
10
-
11
- /**
12
- * The current list of items
13
- *
14
- * @since 3.1.0
15
- * @var array
16
- * @access protected
17
- */
18
- var $items;
19
-
20
- /**
21
- * Various information about the current table
22
- *
23
- * @since 3.1.0
24
- * @var array
25
- * @access private
26
- */
27
- var $_args;
28
-
29
- /**
30
- * Various information needed for displaying the pagination
31
- *
32
- * @since 3.1.0
33
- * @var array
34
- * @access private
35
- */
36
- var $_pagination_args = array();
37
-
38
- /**
39
- * The current screen
40
- *
41
- * @since 3.1.0
42
- * @var object
43
- * @access private
44
- */
45
- var $_screen;
46
-
47
- /**
48
- * Cached bulk actions
49
- *
50
- * @since 3.1.0
51
- * @var array
52
- * @access private
53
- */
54
- var $_actions;
55
-
56
- /**
57
- * Cached pagination output
58
- *
59
- * @since 3.1.0
60
- * @var string
61
- * @access private
62
- */
63
- var $_pagination;
64
-
65
- /**
66
- * Constructor. The child class should call this constructor from it's own constructor
67
- *
68
- * @param array $args An associative array with information about the current table
69
- * @access protected
70
- */
71
- function WP_List_Table( $args ) {
72
-
73
- $args = wp_parse_args( $args, array(
74
- 'screen' => '',
75
- 'plural' => '',
76
- 'singular' => '',
77
- 'ajax' => false
78
- ) );
79
-
80
- $this->_screen = $args['screen'];
81
-
82
- if ( is_string( $this->_screen ) )
83
- $this->_screen = convert_to_screen( $this->_screen );
84
-
85
- if ( !$args['plural'] )
86
- $args['plural'] = $this->_screen->base;
87
-
88
- $this->_args = $args;
89
-
90
- if ( $args['ajax'] ) {
91
- wp_enqueue_script( 'admin-table' );
92
- add_action( 'admin_footer', array( $this, '_js_vars' ) );
93
- }
94
- }
95
-
96
- /**
97
- * Checks the current user's permissions
98
- * @uses wp_die()
99
- *
100
- * @since 3.1.0
101
- * @access public
102
- */
103
- function check_permissions() {
104
- die( 'function WP_List_Table::check_permissions() must be over-ridden in a sub-class.' );
105
- }
106
-
107
- /**
108
- * Prepares the list of items for displaying.
109
- * @uses WP_List_Table::set_pagination_args()
110
- *
111
- * @since 3.1.0
112
- * @access public
113
- */
114
- function prepare_items() {
115
- die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
116
- }
117
-
118
- /**
119
- * An internal method that sets all the necessary pagination arguments
120
- *
121
- * @param array $args An associative array with information about the pagination
122
- * @access protected
123
- */
124
- function set_pagination_args( $args ) {
125
- $args = wp_parse_args( $args, array(
126
- 'query_var' => 'paged',
127
- 'total_items' => 0,
128
- 'total_pages' => 0,
129
- 'per_page' => 0,
130
- ) );
131
-
132
- if ( !$args['total_pages'] && $args['per_page'] > 0 )
133
- $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
134
-
135
- $this->_pagination_args = $args;
136
- }
137
-
138
- /**
139
- * Access the pagination args
140
- *
141
- * @since 3.1.0
142
- * @access public
143
- *
144
- * @param string $key
145
- * @return array
146
- */
147
- function get_pagination_arg( $key ) {
148
- if ( 'page' == $key )
149
- return $this->get_pagenum();
150
-
151
- return @$this->_pagination_args[ $key ];
152
- }
153
-
154
- /**
155
- * Wether the table has items to display or not
156
- *
157
- * @since 3.1.0
158
- * @access public
159
- *
160
- * @return bool
161
- */
162
- function has_items() {
163
- return !empty( $this->items );
164
- }
165
-
166
- /**
167
- * Message to be displayed when there are no items
168
- *
169
- * @since 3.1.0
170
- * @access public
171
- */
172
- function no_items() {
173
- _e( 'No items found.' );
174
- }
175
-
176
- /**
177
- * Get an associative array ( option_name => option_title ) with the list
178
- * of bulk actions available on this table.
179
- *
180
- * @since 3.1.0
181
- * @access protected
182
- *
183
- * @return array
184
- */
185
- function get_bulk_actions() {
186
- return array();
187
- }
188
-
189
- /**
190
- * Display the bulk actions dropdown.
191
- *
192
- * @since 3.1.0
193
- * @access public
194
- */
195
- function bulk_actions() {
196
-
197
- if ( is_null( $this->_actions ) ) {
198
- $this->_actions = $this->get_bulk_actions();
199
- $this->_actions = apply_filters( 'bulk_actions-' . $this->_screen->base, $this->_actions );
200
- $two = '';
201
- }
202
- else {
203
- $two = '2';
204
- }
205
-
206
- if ( empty( $this->_actions ) )
207
- return;
208
-
209
- echo "<select name='action$two'>\n";
210
- echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions' ) . "</option>\n";
211
- foreach ( $this->_actions as $name => $title )
212
- echo "\t<option value='$name'>$title</option>\n";
213
- echo "</select>\n";
214
-
215
- echo "<input type='submit' value='" . esc_attr__( 'Apply' ) . "' name='doaction$two' id='doaction$two' class='button-secondary action' />\n";
216
- }
217
-
218
- /**
219
- * Display a monthly dropdown for filtering items
220
- *
221
- * @since 3.1.0
222
- * @access protected
223
- */
224
- function months_dropdown( $post_type ) {
225
- global $wpdb, $wp_locale;
226
-
227
- $months = $wpdb->get_results( $wpdb->prepare( "
228
- SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
229
- FROM $wpdb->posts
230
- WHERE post_type = %s
231
- ORDER BY post_date DESC
232
- ", $post_type ) );
233
-
234
- $month_count = count( $months );
235
-
236
- if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) )
237
- return;
238
-
239
- $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
240
- ?>
241
- <select name='m'>
242
- <option<?php selected( $m, 0 ); ?> value='0'><?php _e( 'Show all dates' ); ?></option>
243
- <?php
244
- foreach ( $months as $arc_row ) {
245
- if ( 0 == $arc_row->year )
246
- continue;
247
-
248
- $month = zeroise( $arc_row->month, 2 );
249
- $year = $arc_row->year;
250
-
251
- printf( "<option %s value='%s'>%s</option>\n",
252
- selected( $m, $year . $month, false ),
253
- esc_attr( $arc_row->year . $month ),
254
- $wp_locale->get_month( $month ) . " $year"
255
- );
256
- }
257
- ?>
258
- </select>
259
- <?php
260
- }
261
-
262
- /**
263
- * Display a view switcher
264
- *
265
- * @since 3.1.0
266
- * @access protected
267
- */
268
- function view_switcher( $current_mode ) {
269
- $modes = array(
270
- 'list' => __( 'List View' ),
271
- 'excerpt' => __( 'Excerpt View' )
272
- );
273
-
274
- ?>
275
- <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
276
- <div class="view-switch">
277
- <?php
278
- foreach ( $modes as $mode => $title ) {
279
- $class = ( $current_mode == $mode ) ? 'class="current"' : '';
280
- echo "<a href='" . esc_url( add_query_arg( 'mode', $mode, $_SERVER['REQUEST_URI'] ) ) . "' $class><img id='view-switch-$mode' src='" . esc_url( includes_url( 'images/blank.gif' ) ) . "' width='20' height='20' title='$title' alt='$title' /></a>\n";
281
- }
282
- ?>
283
- </div>
284
- <?php
285
- }
286
-
287
- /**
288
- * Display a comment count bubble
289
- *
290
- * @since 3.1.0
291
- * @access protected
292
- *
293
- * @param int $post_id
294
- * @param int $pending_comments
295
- */
296
- function comments_bubble( $post_id, $pending_comments ) {
297
- $pending_phrase = sprintf( __( '%s pending' ), number_format( $pending_comments ) );
298
-
299
- if ( $pending_comments )
300
- echo '<strong>';
301
-
302
- $link = "<a href='" . add_query_arg( 'p', $post_id, admin_url('edit-comments.php') ) . "' title='$pending_phrase' class='post-com-count'><span class='comment-count'>%s</span></a>";
303
-
304
- comments_number(
305
- sprintf( $link, /* translators: comment count link */ _x( '0', 'comment count' ) ),
306
- sprintf( $link, /* translators: comment count link */ _x( '1', 'comment count' ) ),
307
- sprintf( $link, /* translators: comment count link: % will be substituted by comment count */ _x( '%', 'comment count' ) )
308
- );
309
-
310
- if ( $pending_comments )
311
- echo '</strong>';
312
- }
313
-
314
- /**
315
- * Get the current page number
316
- *
317
- * @since 3.1.0
318
- * @access protected
319
- *
320
- * @return int
321
- */
322
- function get_pagenum( $query_var = 'paged' ) {
323
- $pagenum = isset( $_REQUEST[$query_var] ) ? absint( $_REQUEST[$query_var] ) : 0;
324
-
325
- return max( 1, $pagenum );
326
- }
327
-
328
- /**
329
- * Display the pagination.
330
- *
331
- * @since 3.1.0
332
- * @access protected
333
- */
334
- function pagination() {
335
- if ( $this->_pagination ) {
336
- echo $this->_pagination;
337
- return;
338
- }
339
-
340
- if ( empty( $this->_pagination_args ) )
341
- return;
342
-
343
- extract( $this->_pagination_args );
344
-
345
- if ( $total_pages < 2 )
346
- return;
347
-
348
- $output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
349
-
350
- $current = $this->get_pagenum( $query_var );
351
-
352
- $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
353
-
354
- $page_links = array();
355
-
356
- $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
357
- 'first-page',
358
- esc_attr__( 'Go to the first page' ),
359
- esc_url( remove_query_arg( $query_var, $current_url ) ),
360
- '&laquo;&laquo;'
361
- );
362
-
363
- $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
364
- 'prev-page',
365
- esc_attr__( 'Go to the previous page' ),
366
- esc_url( add_query_arg( $query_var, max( 1, $current-1 ), $current_url ) ),
367
- '&laquo;'
368
- );
369
-
370
- $html_current_page = sprintf( "<input class='current-page' title='%s' type='text' name='%s' value='%s' size='%d' />",
371
- esc_attr__( 'Current page' ),
372
- esc_attr( $query_var ),
373
- number_format_i18n( $current ),
374
- strlen( $total_pages )
375
- );
376
- $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
377
- $page_links[] = sprintf( _x( '%s of %s', 'paging' ), $html_current_page, $html_total_pages );
378
-
379
- $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
380
- 'next-page',
381
- esc_attr__( 'Go to the next page' ),
382
- esc_url( add_query_arg( $query_var, min( $total_pages, $current+1 ), $current_url ) ),
383
- '&raquo;'
384
- );
385
-
386
- $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
387
- 'last-page',
388
- esc_attr__( 'Go to the last page' ),
389
- esc_url( add_query_arg( $query_var, $total_pages, $current_url ) ),
390
- '&raquo;&raquo;'
391
- );
392
-
393
- $output .= join( "\n", $page_links );
394
-
395
- $this->_pagination = "<div class='tablenav-pages'>$output</div>";
396
-
397
- echo $this->_pagination;
398
- }
399
-
400
- /**
401
- * Get a list of columns. The format is internal_name => title
402
- *
403
- * @since 3.1.0
404
- * @access protected
405
- *
406
- * @return array
407
- */
408
- function get_columns() {
409
- die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
410
- }
411
-
412
- /**
413
- * Get a list of sortable columns. The format is internal_name => orderby
414
- *
415
- * @since 3.1.0
416
- * @access protected
417
- *
418
- * @return array
419
- */
420
- function get_sortable_columns() {
421
- return array();
422
- }
423
-
424
- /**
425
- * Get a list of hidden columns.
426
- *
427
- * @since 3.1.0
428
- * @access private
429
- *
430
- * @return array
431
- */
432
- function get_hidden_columns() {
433
- return (array) get_user_option( 'manage' . $this->_screen->id. 'columnshidden' );
434
- }
435
-
436
- /**
437
- * Get a list of all, hidden and sortable columns, with filter applied
438
- *
439
- * @since 3.1.0
440
- * @access protected
441
- *
442
- * @return array
443
- */
444
- function get_column_headers() {
445
- if ( !isset( $this->_column_headers ) ) {
446
- $columns = apply_filters( 'manage_' . $this->_screen->id . '_columns', $this->get_columns() );
447
- $sortable = apply_filters( 'manage_' . $this->_screen->id . '_sortable_columns', $this->get_sortable_columns() );
448
- $hidden = $this->get_hidden_columns();
449
-
450
- $this->_column_headers = array( $columns, $hidden, $sortable );
451
- }
452
-
453
- return $this->_column_headers;
454
- }
455
-
456
- /**
457
- * Print column headers, accounting for hidden and sortable columns.
458
- *
459
- * @since 3.1.0
460
- * @access protected
461
- *
462
- * @param bool $with_id Wether to set the id attribute or not
463
- */
464
- function print_column_headers( $with_id = true ) {
465
- $screen = $this->_screen;
466
-
467
- list( $columns, $hidden, $sortable ) = $this->get_column_headers();
468
-
469
- $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
470
-
471
- if ( isset( $_GET['orderby'] ) )
472
- $current_orderby = $_GET['orderby'];
473
- else
474
- $current_orderby = '';
475
-
476
- if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] )
477
- $current_order = 'desc';
478
- else
479
- $current_order = 'asc';
480
-
481
- foreach ( $columns as $column_key => $column_display_name ) {
482
- $class = array( 'manage-column', "column-$column_key" );
483
-
484
- $style = '';
485
- if ( in_array( $column_key, $hidden ) )
486
- $style = 'display:none;';
487
-
488
- $style = ' style="' . $style . '"';
489
-
490
- if ( 'cb' == $column_key )
491
- $class[] = 'check-column';
492
- elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
493
- $class[] = 'num';
494
-
495
- if ( isset( $sortable[$column_key] ) ) {
496
- $orderby = $sortable[$column_key];
497
- if ( $current_orderby == $orderby ) {
498
- $order = 'asc' == $current_order ? 'desc' : 'asc';
499
- $class[] = "sorted-$current_order";
500
- } else {
501
- $order = 'asc';
502
- $class[] = 'sortable';
503
- }
504
- $column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '">' . $column_display_name . '</a>';
505
- $column_display_name .= '<div class="sorting-indicator"></div>';
506
- }
507
-
508
- $id = $with_id ? "id='$column_key'" : '';
509
-
510
- if ( !empty( $class ) )
511
- $class = "class='" . join( ' ', $class ) . "'";
512
-
513
- echo "<th scope='col' $id $class $style>$column_display_name</th>";
514
- }
515
- }
516
-
517
- /**
518
- * Display the table or a message if there are no items
519
- *
520
- * @since 3.1.0
521
- * @access public
522
- */
523
- function display() {
524
- if ( $this->has_items() ) {
525
- $this->display_table();
526
- } else {
527
- echo '<br class="clear">';
528
- $this->extra_tablenav( 'top' );
529
- echo '<br class="clear">';
530
- echo '<p>';
531
- $this->no_items();
532
- echo '</p>';
533
- }
534
- }
535
-
536
- /**
537
- * Get a list of CSS classes for the <table> tag
538
- *
539
- * @since 3.1.0
540
- * @access protected
541
- *
542
- * @return array
543
- */
544
- function get_table_classes() {
545
- extract( $this->_args );
546
-
547
- return array( 'widefat', 'fixed', $plural );
548
- }
549
-
550
- /**
551
- * Display the full table
552
- *
553
- * @since 3.1.0
554
- * @access public
555
- */
556
- function display_table() {
557
- extract( $this->_args );
558
-
559
- $this->display_tablenav( 'top' );
560
-
561
- ?>
562
- <table class="<?php echo implode( ' ', $this->get_table_classes() ); ?>" cellspacing="0">
563
- <thead>
564
- <tr>
565
- <?php $this->print_column_headers(); ?>
566
- </tr>
567
- </thead>
568
-
569
- <tfoot>
570
- <tr>
571
- <?php $this->print_column_headers( false ); ?>
572
- </tr>
573
- </tfoot>
574
-
575
- <tbody id="the-list"<?php if ( $singular ) echo " class='list:$singular'"; ?>>
576
- <?php $this->display_rows(); ?>
577
- </tbody>
578
- </table>
579
- <?php
580
-
581
- $this->display_tablenav( 'bottom' );
582
- }
583
-
584
- /**
585
- * Generate the table navigation above or below the table
586
- *
587
- * @since 3.1.0
588
- * @access protected
589
- */
590
- function display_tablenav( $which ) {
591
- if ( 'top' == $which )
592
- wp_nonce_field( 'bulk-' . $this->_args['plural'] );
593
- ?>
594
- <div class="tablenav">
595
-
596
- <div class="alignleft actions">
597
- <?php $this->bulk_actions( $which ); ?>
598
- </div>
599
-
600
- <?php
601
- $this->extra_tablenav( $which );
602
- $this->pagination( $which );
603
- ?>
604
-
605
- <br class="clear">
606
- </div>
607
-
608
- <br class="clear">
609
- <?php
610
- }
611
-
612
- /**
613
- * Extra controls to be displayed between bulk actions and pagination
614
- *
615
- * @since 3.1.0
616
- * @access protected
617
- */
618
- function extra_tablenav( $which ) {}
619
-
620
- /**
621
- * Generate the <tbody> part of the table
622
- *
623
- * @since 3.1.0
624
- * @access protected
625
- */
626
- function display_rows() {
627
- die( 'function WP_List_Table::display_rows() must be over-ridden in a sub-class.' );
628
- }
629
-
630
- /**
631
- * Handle an incoming ajax request ( called from admin-ajax.php )
632
- *
633
- * @access public
634
- */
635
- function ajax_response() {
636
- $this->check_permissions();
637
- $this->prepare_items();
638
-
639
- extract( $this->_args );
640
- extract( $this->_pagination_args );
641
-
642
- ob_start();
643
- $this->display_rows();
644
- $rows = ob_get_clean();
645
-
646
- die( json_encode( array(
647
- 'rows' => $rows,
648
- 'total_items' => sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ),
649
- 'total_pages' => $total_pages
650
- ) ) );
651
- }
652
-
653
- /**
654
- * Send required variables to JavaScript land
655
- *
656
- * @access private
657
- */
658
- function _js_vars() {
659
- extract( $this->_args );
660
-
661
- $class = get_class( $this );
662
-
663
- printf( "<script type='text/javascript'>list_args = %s;</script>\n",
664
- json_encode( compact( 'screen', 'class' ) )
665
- );
666
- }
667
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/functions.php CHANGED
@@ -128,6 +128,7 @@ if ( !defined('ABSPATH') )
128
  require_once(dirname(__FILE__).'/options-save.php');
129
  backwpup_log_operations($_REQUEST['action']);
130
  }
 
131
  $table = new BackWPup_Logs_Table;
132
  $table->check_permissions();
133
  $table->prepare_items();
@@ -151,6 +152,7 @@ if ( !defined('ABSPATH') )
151
  require_once(dirname(__FILE__).'/options-save.php');
152
  backwpup_backups_operations($_REQUEST['action']);
153
  }
 
154
  $table = new BackWPup_Backups_Table;
155
  $table->check_permissions();
156
  $table->prepare_items();
@@ -164,6 +166,7 @@ if ( !defined('ABSPATH') )
164
  require_once(dirname(__FILE__).'/options-save.php');
165
  backwpup_job_operations($_REQUEST['action']);
166
  }
 
167
  $table = new BackWPup_Jobs_Table;
168
  $table->check_permissions();
169
  $table->prepare_items();
@@ -732,8 +735,6 @@ if ( !defined('ABSPATH') )
732
  return;
733
  //iclude php5 functions
734
  require_once(dirname(__FILE__).'/functions5.php');
735
- //load tables Classes
736
- require_once(dirname(__FILE__).'/list-tables.php');
737
  //Disabele WP_Corn
738
  $cfg=get_option('backwpup');
739
  if ($cfg['disablewpcron'])
128
  require_once(dirname(__FILE__).'/options-save.php');
129
  backwpup_log_operations($_REQUEST['action']);
130
  }
131
+ require_once(dirname(__FILE__).'/list-tables.php');
132
  $table = new BackWPup_Logs_Table;
133
  $table->check_permissions();
134
  $table->prepare_items();
152
  require_once(dirname(__FILE__).'/options-save.php');
153
  backwpup_backups_operations($_REQUEST['action']);
154
  }
155
+ require_once(dirname(__FILE__).'/list-tables.php');
156
  $table = new BackWPup_Backups_Table;
157
  $table->check_permissions();
158
  $table->prepare_items();
166
  require_once(dirname(__FILE__).'/options-save.php');
167
  backwpup_job_operations($_REQUEST['action']);
168
  }
169
+ require_once(dirname(__FILE__).'/list-tables.php');
170
  $table = new BackWPup_Jobs_Table;
171
  $table->check_permissions();
172
  $table->prepare_items();
735
  return;
736
  //iclude php5 functions
737
  require_once(dirname(__FILE__).'/functions5.php');
 
 
738
  //Disabele WP_Corn
739
  $cfg=get_option('backwpup');
740
  if ($cfg['disablewpcron'])
app/functions5.php CHANGED
@@ -193,6 +193,9 @@ if ( !defined('ABSPATH') )
193
 
194
  if (!isset($jobsettings['ftppasv']) or !is_bool($jobsettings['ftppasv']))
195
  $jobsettings['ftppasv']=true;
 
 
 
196
 
197
  if (!isset($jobsettings['awsAccessKey']) or !is_string($jobsettings['awsAccessKey']))
198
  $jobsettings['awsAccessKey']='';
@@ -236,6 +239,19 @@ if ( !defined('ABSPATH') )
236
  if (!isset($jobsettings['rscmaxbackups']) or !is_int($jobsettings['rscmaxbackups']))
237
  $jobsettings['rscmaxbackups']=0;
238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  if (!is_string($jobsettings['mailaddress']) or false === $pos=strpos($jobsettings['mailaddress'],'@') or false === strpos($jobsettings['mailaddress'],'.',$pos))
240
  $jobsettings['mailaddress']='';
241
 
@@ -337,24 +353,21 @@ if ( !defined('ABSPATH') )
337
  if (false !== strpos($jobvalue['ftphost'],':')) //look for port
338
  list($ftphost,$ftpport)=explode(':',$jobvalue,2);
339
 
340
- $SSL=false;
341
- if (function_exists('ftp_ssl_connect')) { //make SSL FTP connection
342
  $ftp_conn_id = ftp_ssl_connect($ftphost,$ftpport,10);
343
- if ($ftp_conn_id)
344
- $SSL=true;
345
- }
346
- if (!$ftp_conn_id) { //make normal FTP conection if SSL not work
347
  $ftp_conn_id = ftp_connect($ftphost,$ftpport,10);
348
  }
 
349
  if ($ftp_conn_id) {
350
  //FTP Login
351
- $loginok=false;
352
  if (@ftp_login($ftp_conn_id, $jobvalue['ftpuser'], base64_decode($jobvalue['ftppass']))) {
353
  $loginok=true;
354
  } else { //if PHP ftp login don't work use raw login
355
  ftp_raw($ftp_conn_id,'USER '.$jobvalue['ftpuser']);
356
- ftp_raw($ftp_conn_id,'PASS '.base64_decode($jobvalue['ftppass']));
357
- $loginok=true;
 
358
  }
359
  }
360
  if ($loginok) {
193
 
194
  if (!isset($jobsettings['ftppasv']) or !is_bool($jobsettings['ftppasv']))
195
  $jobsettings['ftppasv']=true;
196
+
197
+ if (!isset($jobsettings['ftpssl']) or !is_bool($jobsettings['ftpssl']))
198
+ $jobsettings['ftpssl']=false;
199
 
200
  if (!isset($jobsettings['awsAccessKey']) or !is_string($jobsettings['awsAccessKey']))
201
  $jobsettings['awsAccessKey']='';
239
  if (!isset($jobsettings['rscmaxbackups']) or !is_int($jobsettings['rscmaxbackups']))
240
  $jobsettings['rscmaxbackups']=0;
241
 
242
+
243
+ if (!isset($jobsettings['dropemail']) or !is_string($jobsettings['dropemail']))
244
+ $jobsettings['dropemail']='';
245
+
246
+ if (!isset($jobsettings['dropepass']) or !is_string($jobsettings['dropepass']))
247
+ $jobsettings['dropepass']='';
248
+
249
+ if (!isset($jobsettings['dropedir']) or !is_string($jobsettings['dropedir']) or $jobsettings['dropedir']=='/')
250
+ $jobsettings['dropedir']='';
251
+ $jobsettings['dropedir']=trailingslashit(str_replace('//','/',str_replace('\\','/',trim($jobsettings['dropedir']))));
252
+ if (substr($jobsettings['dropedir'],0,1)=='/')
253
+ $jobsettings['dropedir']=substr($jobsettings['dropedir'],1);
254
+
255
  if (!is_string($jobsettings['mailaddress']) or false === $pos=strpos($jobsettings['mailaddress'],'@') or false === strpos($jobsettings['mailaddress'],'.',$pos))
256
  $jobsettings['mailaddress']='';
257
 
353
  if (false !== strpos($jobvalue['ftphost'],':')) //look for port
354
  list($ftphost,$ftpport)=explode(':',$jobvalue,2);
355
 
356
+ if (function_exists('ftp_ssl_connect') and $jobvalue['ftpssl']) { //make SSL FTP connection
 
357
  $ftp_conn_id = ftp_ssl_connect($ftphost,$ftpport,10);
358
+ } elseif (!$jobvalue['ftpssl']) { //make normal FTP conection if SSL not work
 
 
 
359
  $ftp_conn_id = ftp_connect($ftphost,$ftpport,10);
360
  }
361
+ $loginok=false;
362
  if ($ftp_conn_id) {
363
  //FTP Login
 
364
  if (@ftp_login($ftp_conn_id, $jobvalue['ftpuser'], base64_decode($jobvalue['ftppass']))) {
365
  $loginok=true;
366
  } else { //if PHP ftp login don't work use raw login
367
  ftp_raw($ftp_conn_id,'USER '.$jobvalue['ftpuser']);
368
+ $return=ftp_raw($ftp_conn_id,'PASS '.base64_decode($jobvalue['ftppass']));
369
+ if (substr(trim($return[0]),0,3)<=400)
370
+ $loginok=true;
371
  }
372
  }
373
  if ($loginok) {
app/libs/DropboxUploader.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Dropbox Uploader
4
+ *
5
+ * Copyright (c) 2009 Jaka Jancar
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ *
25
+ * @author Jaka Jancar [jaka@kubje.org] [http://jaka.kubje.org/]
26
+ * @version 1.1.5
27
+ */
28
+ class DropboxUploader {
29
+ protected $email;
30
+ protected $password;
31
+ protected $caCertSourceType = self::CACERT_SOURCE_SYSTEM;
32
+ const CACERT_SOURCE_SYSTEM = 0;
33
+ const CACERT_SOURCE_FILE = 1;
34
+ const CACERT_SOURCE_DIR = 2;
35
+ protected $caCertSource;
36
+ protected $loggedIn = false;
37
+ protected $cookies = array();
38
+
39
+ /**
40
+ * Constructor
41
+ *
42
+ * @param string $email
43
+ * @param string|null $password
44
+ */
45
+ public function __construct($email, $password) {
46
+ // Check requirements
47
+ if (!extension_loaded('curl'))
48
+ throw new Exception('DropboxUploader requires the cURL extension.');
49
+
50
+ $this->email = $email;
51
+ $this->password = $password;
52
+ }
53
+
54
+ public function setCaCertificateFile($file)
55
+ {
56
+ $this->caCertSourceType = self::CACERT_SOURCE_FILE;
57
+ $this->caCertSource = $file;
58
+ }
59
+
60
+ public function setCaCertificateDir($dir)
61
+ {
62
+ $this->caCertSourceType = self::CACERT_SOURCE_DIR;
63
+ $this->caCertSource = $dir;
64
+ }
65
+
66
+ public function upload($filename, $remoteDir='/') {
67
+ if (!file_exists($filename) or !is_file($filename) or !is_readable($filename))
68
+ throw new Exception("File '$filename' does not exist or is not readable.");
69
+
70
+ if (!is_string($remoteDir))
71
+ throw new Exception("Remote directory must be a string, is ".gettype($remoteDir)." instead.");
72
+
73
+ if (!$this->loggedIn)
74
+ $this->login();
75
+
76
+ $data = $this->request('https://www.dropbox.com/home');
77
+ $token = $this->extractToken($data, 'https://dl-web.dropbox.com/upload');
78
+
79
+ $data = $this->request('https://dl-web.dropbox.com/upload', true, array('plain'=>'yes', 'file'=>'@'.$filename, 'dest'=>$remoteDir, 't'=>$token));
80
+ if (strpos($data, 'HTTP/1.1 302 FOUND') === false)
81
+ throw new Exception('Upload failed!');
82
+ }
83
+
84
+ protected function login() {
85
+ $data = $this->request('https://www.dropbox.com/login');
86
+ $token = $this->extractToken($data, '/login');
87
+
88
+ $data = $this->request('https://www.dropbox.com/login', true, array('login_email'=>$this->email, 'login_password'=>$this->password, 't'=>$token));
89
+
90
+ if (stripos($data, 'location: /home') === false)
91
+ throw new Exception('Login unsuccessful.');
92
+
93
+ $this->loggedIn = true;
94
+ }
95
+
96
+ protected function request($url, $post=false, $postData=array()) {
97
+ $ch = curl_init();
98
+ curl_setopt($ch, CURLOPT_URL, $url);
99
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
100
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
101
+ switch ($this->caCertSourceType) {
102
+ case self::CACERT_SOURCE_FILE:
103
+ curl_setopt($ch, CURLOPT_CAINFO, $this->caCertSource);
104
+ break;
105
+ case self::CACERT_SOURCE_DIR:
106
+ curl_setopt($ch, CURLOPT_CAPATH, $this->caCertSource);
107
+ break;
108
+ }
109
+ curl_setopt($ch, CURLOPT_HEADER, 1);
110
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
111
+ if ($post) {
112
+ curl_setopt($ch, CURLOPT_POST, $post);
113
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
114
+ }
115
+
116
+ // Send cookies
117
+ $rawCookies = array();
118
+ foreach ($this->cookies as $k=>$v)
119
+ $rawCookies[] = "$k=$v";
120
+ $rawCookies = implode(';', $rawCookies);
121
+ curl_setopt($ch, CURLOPT_COOKIE, $rawCookies);
122
+
123
+ $data = curl_exec($ch);
124
+
125
+ if ($data === false)
126
+ throw new Exception('Cannot execute request: '.curl_error($ch));
127
+
128
+ // Store received cookies
129
+ preg_match_all('/Set-Cookie: ([^=]+)=(.*?);/i', $data, $matches, PREG_SET_ORDER);
130
+ foreach ($matches as $match)
131
+ $this->cookies[$match[1]] = $match[2];
132
+
133
+ curl_close($ch);
134
+
135
+ return $data;
136
+ }
137
+
138
+ protected function extractToken($html, $formAction) {
139
+ if (!preg_match('/<form [^>]*'.preg_quote($formAction, '/').'[^>]*>.*?(<input [^>]*name="t" [^>]*value="(.*?)"[^>]*>).*?<\/form>/is', $html, $matches) || !isset($matches[2]))
140
+ throw new Exception("Cannot extract token! (form action=$formAction)");
141
+ return $matches[2];
142
+ }
143
+
144
+ }
app/list-tables.php CHANGED
@@ -1,10 +1,10 @@
1
  <?PHP
2
  //backwarts copatibility lower than wp 3.1
3
  if (!class_exists('WP_List_Table')) {
4
- if (is_file(trailingslashit(ABSPATH).'wp-admin/includes/list-table.php'))
5
- include_once( trailingslashit(ABSPATH).'wp-admin/includes/list-table.php' );
6
  else
7
- include_once('compatibility/list-table.php');
8
  }
9
 
10
  class BackWPup_Jobs_Table extends WP_List_Table {
@@ -79,7 +79,7 @@ class BackWPup_Jobs_Table extends WP_List_Table {
79
 
80
  function single_row( $jobid, $jobvalue, $style = '' ) {
81
  global $mode;
82
- list( $columns, $hidden ) = $this->get_column_headers();
83
  $r = "<tr id='jodid-$jobid'$style>";
84
  foreach ( $columns as $column_name => $column_display_name ) {
85
  $class = "class=\"$column_name column-$column_name\"";
@@ -206,7 +206,14 @@ class BackWPup_Logs_Table extends WP_List_Table {
206
  $logfiles[]=$file;
207
  }
208
  closedir( $dir );
209
- rsort($logfiles);
 
 
 
 
 
 
 
210
  }
211
  //by page
212
  $start=intval( ( $this->get_pagenum() - 1 ) * $per_page );
@@ -224,6 +231,12 @@ class BackWPup_Logs_Table extends WP_List_Table {
224
  ) );
225
 
226
  }
 
 
 
 
 
 
227
 
228
  function no_items() {
229
  _e( 'No Logs.','backwpup');
@@ -248,10 +261,6 @@ class BackWPup_Logs_Table extends WP_List_Table {
248
  return $posts_columns;
249
  }
250
 
251
- function get_sortable_columns() {
252
- return array();
253
- }
254
-
255
  function get_hidden_columns() {
256
  return (array) get_user_option( 'backwpup_logs_columnshidden' );
257
  }
@@ -267,7 +276,7 @@ class BackWPup_Logs_Table extends WP_List_Table {
267
  }
268
 
269
  function single_row( $logfile, $logdata, $style = '' ) {
270
- list( $columns, $hidden ) = $this->get_column_headers();
271
  $r = "<tr id='".basename($logfile)."'$style>";
272
  foreach ( $columns as $column_name => $column_display_name ) {
273
  $class = "class=\"$column_name column-$column_name\"";
@@ -429,7 +438,7 @@ class BackWPup_Backups_Table extends WP_List_Table {
429
  }
430
 
431
  function single_row( $backup, $jobvalue, $style = '' ) {
432
- list( $columns, $hidden ) = $this->get_column_headers();
433
  $r = "<tr id='$logfile'$style>";
434
  foreach ( $columns as $column_name => $column_display_name ) {
435
  $class = "class=\"$column_name column-$column_name\"";
1
  <?PHP
2
  //backwarts copatibility lower than wp 3.1
3
  if (!class_exists('WP_List_Table')) {
4
+ if (is_file(trailingslashit(ABSPATH).'wp-admin/includes/class-wp-list-table.php'))
5
+ include_once( trailingslashit(ABSPATH).'wp-admin/includes/class-wp-list-table.php' );
6
  else
7
+ include_once('compatibility/class-wp-list-table.php');
8
  }
9
 
10
  class BackWPup_Jobs_Table extends WP_List_Table {
79
 
80
  function single_row( $jobid, $jobvalue, $style = '' ) {
81
  global $mode;
82
+ list( $columns, $hidden, $sortable ) = $this->get_column_info();
83
  $r = "<tr id='jodid-$jobid'$style>";
84
  foreach ( $columns as $column_name => $column_display_name ) {
85
  $class = "class=\"$column_name column-$column_name\"";
206
  $logfiles[]=$file;
207
  }
208
  closedir( $dir );
209
+ if ( !isset( $_REQUEST['orderby'] ) or $_REQUEST['orderby']=='log') {
210
+ if ( $_REQUEST['order']=='asc')
211
+ sort($logfiles);
212
+ else
213
+ rsort($logfiles);
214
+ }
215
+ if (!isset( $_REQUEST['orderby'] ) and !isset( $_REQUEST['order'] ))
216
+ rsort($logfiles);
217
  }
218
  //by page
219
  $start=intval( ( $this->get_pagenum() - 1 ) * $per_page );
231
  ) );
232
 
233
  }
234
+
235
+ function get_sortable_columns() {
236
+ return array(
237
+ 'log' => 'log',
238
+ );
239
+ }
240
 
241
  function no_items() {
242
  _e( 'No Logs.','backwpup');
261
  return $posts_columns;
262
  }
263
 
 
 
 
 
264
  function get_hidden_columns() {
265
  return (array) get_user_option( 'backwpup_logs_columnshidden' );
266
  }
276
  }
277
 
278
  function single_row( $logfile, $logdata, $style = '' ) {
279
+ list( $columns, $hidden, $sortable ) = $this->get_column_info();
280
  $r = "<tr id='".basename($logfile)."'$style>";
281
  foreach ( $columns as $column_name => $column_display_name ) {
282
  $class = "class=\"$column_name column-$column_name\"";
438
  }
439
 
440
  function single_row( $backup, $jobvalue, $style = '' ) {
441
+ list( $columns, $hidden, $sortable ) = $this->get_column_info();
442
  $r = "<tr id='$logfile'$style>";
443
  foreach ( $columns as $column_name => $column_display_name ) {
444
  $class = "class=\"$column_name column-$column_name\"";
app/options-edit-job.php CHANGED
@@ -345,7 +345,8 @@ $todo=explode('+',$jobvalue['type']);
345
  <input name="ftpdir" type="text" value="<?PHP echo $jobvalue['ftpdir'];?>" class="large-text" /><br />
346
  <?PHP if (!is_numeric($jobvalue['ftpmaxbackups'])) $jobvalue['ftpmaxbackups']=0; ?>
347
  <?PHP _e('Max. Backup Files in FTP Folder:','backwpup'); ?> <input name="ftpmaxbackups" type="text" size="3" value="<?PHP echo $jobvalue['ftpmaxbackups'];?>" class="small-text" /><span class="description"><?PHP _e('(Oldest files will deleted first.)','backwpup');?></span><br />
348
- <input class="checkbox" value="1" type="checkbox" <?php checked($jobvalue['ftppasv'],true); ?> name="ftppasv" /> <?PHP _e('Use FTP Passiv mode.','backwpup'); ?><br />
 
349
  </div>
350
  </div>
351
 
@@ -392,7 +393,19 @@ $todo=explode('+',$jobvalue['type']);
392
  <?PHP } ?>
393
  </div>
394
  </div>
395
-
 
 
 
 
 
 
 
 
 
 
 
 
396
  <div id="tomail" class="postbox" <?PHP if (!in_array("FILE",$todo) and !in_array("DB",$todo) and !in_array("WPEXP",$todo)) echo 'style="display:none;"';?>>
397
  <h3 class="hndle"><span><?PHP _e('Backup to E-Mail','backwpup'); ?></span></h3>
398
  <div class="inside">
345
  <input name="ftpdir" type="text" value="<?PHP echo $jobvalue['ftpdir'];?>" class="large-text" /><br />
346
  <?PHP if (!is_numeric($jobvalue['ftpmaxbackups'])) $jobvalue['ftpmaxbackups']=0; ?>
347
  <?PHP _e('Max. Backup Files in FTP Folder:','backwpup'); ?> <input name="ftpmaxbackups" type="text" size="3" value="<?PHP echo $jobvalue['ftpmaxbackups'];?>" class="small-text" /><span class="description"><?PHP _e('(Oldest files will deleted first.)','backwpup');?></span><br />
348
+ <input class="checkbox" value="1" type="checkbox" <?php checked($jobvalue['ftpssl'],true); ?> name="ftpssl" /> <?PHP _e('Use SSL-FTP Connection.','backwpup'); ?><br />
349
+ <input class="checkbox" value="1" type="checkbox" <?php checked($jobvalue['ftppasv'],true); ?> name="ftppasv" /> <?PHP _e('Use FTP Passiv mode.','backwpup'); ?><br />
350
  </div>
351
  </div>
352
 
393
  <?PHP } ?>
394
  </div>
395
  </div>
396
+
397
+ <div id="toftp" class="postbox" <?PHP if (!in_array("FILE",$todo) and !in_array("DB",$todo) and !in_array("WPEXP",$todo)) echo 'style="display:none;"';?>>
398
+ <h3 class="hndle"><span><?PHP _e('Backup to Dropbox','backwpup'); ?></span></h3>
399
+ <div class="inside">
400
+ <b><?PHP _e('Email:','backwpup'); ?></b><br />
401
+ <input name="dropemail" type="text" value="<?PHP echo $jobvalue['dropemail'];?>" class="large-text" /><br />
402
+ <b><?PHP _e('Password:','backwpup'); ?></b><br />
403
+ <input name="dropepass" type="password" value="<?PHP echo base64_decode($jobvalue['dropepass']);?>" class="password large-text" /><br />
404
+ <b><?PHP _e('Directory:','backwpup'); ?></b><br />
405
+ <input name="dropedir" type="text" value="<?PHP echo $jobvalue['dropedir'];?>" class="user large-text" /><br />
406
+ </div>
407
+ </div>
408
+
409
  <div id="tomail" class="postbox" <?PHP if (!in_array("FILE",$todo) and !in_array("DB",$todo) and !in_array("WPEXP",$todo)) echo 'style="display:none;"';?>>
410
  <h3 class="hndle"><span><?PHP _e('Backup to E-Mail','backwpup'); ?></span></h3>
411
  <div class="inside">
app/options-runnow-iframe.php CHANGED
@@ -1,6 +1,6 @@
1
  <?PHP
2
- if (file_exists($_GET['wpabs'].'wp-load.php') and is_numeric(trim($_GET['jobid']))) {
3
- require_once($_GET['wpabs'].'wp-load.php'); /** Setup WordPress environment */
4
  check_admin_referer('dojob-now_' . (int)$_GET['jobid']);
5
  backwpup_send_no_cache_header();
6
  // flush any buffers and send the headers
1
  <?PHP
2
+ if (file_exists(trim($_GET['wpabs']).'wp-load.php') and is_numeric(trim($_GET['jobid']))) {
3
+ require_once(trim($_GET['wpabs']).'wp-load.php'); /** Setup WordPress environment */
4
  check_admin_referer('dojob-now_' . (int)$_GET['jobid']);
5
  backwpup_send_no_cache_header();
6
  // flush any buffers and send the headers
app/options-save.php CHANGED
@@ -159,26 +159,21 @@ function backwpup_backups_operations($action) {
159
  if (false !== strpos($jobvalue['ftphost'],':')) //look for port
160
  list($ftphost,$ftpport)=explode(':',$jobvalue,2);
161
 
162
- $SSL=false;
163
- if (function_exists('ftp_ssl_connect')) { //make SSL FTP connection
164
  $ftp_conn_id = ftp_ssl_connect($ftphost,$ftpport,10);
165
- if ($ftp_conn_id)
166
- $SSL=true;
167
- }
168
- if (!$ftp_conn_id) { //make normal FTP conection if SSL not work
169
  $ftp_conn_id = ftp_connect($ftphost,$ftpport,10);
170
  }
 
171
  if ($ftp_conn_id) {
172
  //FTP Login
173
- $loginok=false;
174
  if (@ftp_login($ftp_conn_id, $jobvalue['ftpuser'], base64_decode($jobvalue['ftppass']))) {
175
  $loginok=true;
176
  } else { //if PHP ftp login don't work use raw login
177
- if (substr(trim(ftp_raw($ftp_conn_id,'USER '.$jobvalue['ftpuser'])),0,3)<400) {
178
- if (substr(trim(ftp_raw($ftp_conn_id,'PASS '.base64_decode($jobvalue['ftppass']))),0,3)<400) {
179
- $loginok=true;
180
- }
181
- }
182
  }
183
  }
184
  if ($loginok) {
@@ -380,7 +375,11 @@ function backwpup_save_job() { //Save Job settings
380
  $jobs[$jobid]['ftppass']=base64_encode($_POST['ftppass']);
381
  $jobs[$jobid]['ftpdir']=stripslashes($_POST['ftpdir']);
382
  $jobs[$jobid]['ftpmaxbackups']=(int)$_POST['ftpmaxbackups'];
 
383
  $jobs[$jobid]['ftppasv']= $_POST['ftppasv']==1 ? true : false;
 
 
 
384
  $jobs[$jobid]['awsAccessKey']=$_POST['awsAccessKey'];
385
  $jobs[$jobid]['awsSecretKey']=$_POST['awsSecretKey'];
386
  $jobs[$jobid]['awsSSL']= $_POST['awsSSL']==1 ? true : false;
159
  if (false !== strpos($jobvalue['ftphost'],':')) //look for port
160
  list($ftphost,$ftpport)=explode(':',$jobvalue,2);
161
 
162
+ if (function_exists('ftp_ssl_connect') and $jobvalue['ftpssl']) { //make SSL FTP connection
 
163
  $ftp_conn_id = ftp_ssl_connect($ftphost,$ftpport,10);
164
+ } elseif (!$jobvalue['ftpssl']) { //make normal FTP conection if SSL not work
 
 
 
165
  $ftp_conn_id = ftp_connect($ftphost,$ftpport,10);
166
  }
167
+ $loginok=false;
168
  if ($ftp_conn_id) {
169
  //FTP Login
 
170
  if (@ftp_login($ftp_conn_id, $jobvalue['ftpuser'], base64_decode($jobvalue['ftppass']))) {
171
  $loginok=true;
172
  } else { //if PHP ftp login don't work use raw login
173
+ ftp_raw($ftp_conn_id,'USER '.$jobvalue['ftpuser']);
174
+ $return=ftp_raw($ftp_conn_id,'PASS '.base64_decode($jobvalue['ftppass']));
175
+ if (substr(trim($return[0]),0,3)<=400)
176
+ $loginok=true;
 
177
  }
178
  }
179
  if ($loginok) {
375
  $jobs[$jobid]['ftppass']=base64_encode($_POST['ftppass']);
376
  $jobs[$jobid]['ftpdir']=stripslashes($_POST['ftpdir']);
377
  $jobs[$jobid]['ftpmaxbackups']=(int)$_POST['ftpmaxbackups'];
378
+ $jobs[$jobid]['ftpssl']= $_POST['ftpssl']==1 ? true : false;
379
  $jobs[$jobid]['ftppasv']= $_POST['ftppasv']==1 ? true : false;
380
+ $jobs[$jobid]['dropemail']=$_POST['dropemail'];
381
+ $jobs[$jobid]['dropepass']=base64_encode($_POST['dropepass']);
382
+ $jobs[$jobid]['dropedir']=$_POST['dropedir'];
383
  $jobs[$jobid]['awsAccessKey']=$_POST['awsAccessKey'];
384
  $jobs[$jobid]['awsSecretKey']=$_POST['awsSecretKey'];
385
  $jobs[$jobid]['awsSSL']= $_POST['awsSSL']==1 ? true : false;
app/options-view_log-iframe.php CHANGED
@@ -1,8 +1,8 @@
1
  <?PHP
2
- if (file_exists($_GET['wpabs'].'wp-load.php') and file_exists($_GET['logfile'])) {
3
- require_once($_GET['wpabs'].'wp-load.php'); /** Setup WordPress environment */
4
  check_admin_referer('viewlognow_'.basename($_GET['logfile']));
5
- readfile($_GET['logfile']);
6
  } else {
7
  header("HTTP/1.0 404 Not Found");
8
  }
1
  <?PHP
2
+ if (file_exists(trim($_GET['wpabs']).'wp-load.php') and file_exists(trim($_GET['logfile']))) {
3
+ require_once(trim($_GET['wpabs']).'wp-load.php'); /** Setup WordPress environment */
4
  check_admin_referer('viewlognow_'.basename($_GET['logfile']));
5
+ readfile(trim($_GET['logfile']));
6
  } else {
7
  header("HTTP/1.0 404 Not Found");
8
  }
backwpup.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: BackWPup
4
  Plugin URI: http://danielhuesken.de/portfolio/backwpup/
5
  Description: Backup and more of your WordPress Blog Database and Files.
6
  Author: Daniel H&uuml;sken
7
- Version: 1.3.6
8
  Author URI: http://danielhuesken.de
9
  Text Domain: backwpup
10
  Domain Path: /lang/
@@ -34,7 +34,7 @@ if ( !defined('ABSPATH') )
34
  //Set plugin dirname
35
  define('BACKWPUP_PLUGIN_BASEDIR', dirname(plugin_basename(__FILE__)));
36
  //Set Plugin Version
37
- define('BACKWPUP_VERSION', '1.3.6');
38
  //load Text Domain
39
  load_plugin_textdomain('backwpup', false, BACKWPUP_PLUGIN_BASEDIR.'/lang');
40
  //Load functions file
4
  Plugin URI: http://danielhuesken.de/portfolio/backwpup/
5
  Description: Backup and more of your WordPress Blog Database and Files.
6
  Author: Daniel H&uuml;sken
7
+ Version: 1.4.1
8
  Author URI: http://danielhuesken.de
9
  Text Domain: backwpup
10
  Domain Path: /lang/
34
  //Set plugin dirname
35
  define('BACKWPUP_PLUGIN_BASEDIR', dirname(plugin_basename(__FILE__)));
36
  //Set Plugin Version
37
+ define('BACKWPUP_VERSION', '1.4.1');
38
  //load Text Domain
39
  load_plugin_textdomain('backwpup', false, BACKWPUP_PLUGIN_BASEDIR.'/lang');
40
  //Load functions file
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === BackWPup ===
2
- Contributors: danielhuesken
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=daniel%40huesken-net%2ede&item_name=Daniel%20Huesken%20Plugin%20Donation&item_number=BackWPup&no_shipping=0&no_note=1&tax=0&currency_code=EUR&lc=DE&bn=PP%2dDonationsBF&charset=UTF%2d8
4
  Tags: backup, admin, file, Database, mysql, cron, ftp, S3, export, xml, Rackspase, cloud, webdav
5
  Requires at least: 2.8
6
  Tested up to: 3.1.0
7
- Stable tag: 1.3.6
8
 
9
  Backup and more of your WordPress Blog Database and Files
10
 
@@ -22,6 +22,7 @@ Backup and more your Blog.
22
  * Store backup to FTP Server
23
  * Store backup to Amazon S3
24
  * Store backup to RackSpaceCloud
 
25
  * Send Log/Backup by eMail
26
 
27
 
@@ -85,6 +86,15 @@ Please deactivate Pasive Mode and try it again.
85
  1. Job Page
86
 
87
  == Changelog ==
 
 
 
 
 
 
 
 
 
88
  = 1.3.6 =
89
  * long file list not longer displayed in logs.
90
  * Added option to see detailed file list
1
  === BackWPup ===
2
+ Contributors: danielhuesken, zlli
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=daniel%40huesken-net%2ede&item_name=Daniel%20Huesken%20Plugin%20Donation&item_number=BackWPup&no_shipping=0&no_note=1&tax=0&currency_code=EUR&lc=DE&bn=PP%2dDonationsBF&charset=UTF%2d8
4
  Tags: backup, admin, file, Database, mysql, cron, ftp, S3, export, xml, Rackspase, cloud, webdav
5
  Requires at least: 2.8
6
  Tested up to: 3.1.0
7
+ Stable tag: 1.4.1
8
 
9
  Backup and more of your WordPress Blog Database and Files
10
 
22
  * Store backup to FTP Server
23
  * Store backup to Amazon S3
24
  * Store backup to RackSpaceCloud
25
+ * Store backup to DropBox
26
  * Send Log/Backup by eMail
27
 
28
 
86
  1. Job Page
87
 
88
  == Changelog ==
89
+ = 1.4.1 =
90
+ * Dropbox changes
91
+ * fixed problem on send log with mail
92
+ * Security fix (thanks Massa Danilo)
93
+
94
+ = 1.4.0 =
95
+ * make SSL-FTP as option
96
+ * added Dropbox support (zlli)
97
+
98
  = 1.3.6 =
99
  * long file list not longer displayed in logs.
100
  * Added option to see detailed file list