dolibarr  13.0.2
utils.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2016 Destailleur Laurent <eldy@users.sourceforge.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <https://www.gnu.org/licenses/>.
16  */
17 
28 class Utils
29 {
33  public $db;
34 
35  public $output; // Used by Cron method to return message
36  public $result; // Used by Cron method to return data
37 
43  public function __construct($db)
44  {
45  $this->db = $db;
46  }
47 
48 
57  public function purgeFiles($choices = 'tempfilesold,logfile', $nbsecondsold = 86400)
58  {
59  global $conf, $langs, $dolibarr_main_data_root;
60 
61  $langs->load("admin");
62 
63  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
64 
65  if (empty($choices)) $choices = 'tempfilesold,logfile';
66 
67  dol_syslog("Utils::purgeFiles choice=".$choices, LOG_DEBUG);
68 
69  $count = 0;
70  $countdeleted = 0;
71  $counterror = 0;
72 
73  $choicesarray = explode(',', $choices);
74  foreach ($choicesarray as $choice) {
75  $filesarray = array();
76 
77  if ($choice == 'tempfiles' || $choice == 'tempfilesold')
78  {
79  // Delete temporary files
80  if ($dolibarr_main_data_root)
81  {
82  $filesarray = dol_dir_list($dolibarr_main_data_root, "directories", 1, '^temp$', '', 'name', SORT_ASC, 2, 0, '', 1); // Do not follow symlinks
83 
84  if ($choice == 'tempfilesold')
85  {
86  $now = dol_now();
87  foreach ($filesarray as $key => $val)
88  {
89  if ($val['date'] > ($now - ($nbsecondsold))) unset($filesarray[$key]); // Discard temp dir not older than $nbsecondsold
90  }
91  }
92  }
93  }
94 
95  if ($choice == 'allfiles')
96  {
97  // Delete all files (except install.lock, do not follow symbolic links)
98  if ($dolibarr_main_data_root)
99  {
100  $filesarray = dol_dir_list($dolibarr_main_data_root, "all", 0, '', 'install\.lock$', 'name', SORT_ASC, 0, 0, '', 1);
101  }
102  }
103 
104  if ($choice == 'logfile')
105  {
106  // Define files log
107  if ($dolibarr_main_data_root)
108  {
109  $filesarray = dol_dir_list($dolibarr_main_data_root, "files", 0, '.*\.log[\.0-9]*(\.gz)?$', 'install\.lock$', 'name', SORT_ASC, 0, 0, '', 1);
110  }
111 
112  $filelog = '';
113  if (!empty($conf->syslog->enabled))
114  {
115  $filelog = $conf->global->SYSLOG_FILE;
116  $filelog = preg_replace('/DOL_DATA_ROOT/i', DOL_DATA_ROOT, $filelog);
117 
118  $alreadyincluded = false;
119  foreach ($filesarray as $tmpcursor)
120  {
121  if ($tmpcursor['fullname'] == $filelog) { $alreadyincluded = true; }
122  }
123  if (!$alreadyincluded) $filesarray[] = array('fullname'=>$filelog, 'type'=>'file');
124  }
125  }
126 
127  if (is_array($filesarray) && count($filesarray)) {
128  foreach ($filesarray as $key => $value)
129  {
130  //print "x ".$filesarray[$key]['fullname']."-".$filesarray[$key]['type']."<br>\n";
131  if ($filesarray[$key]['type'] == 'dir') {
132  $startcount = 0;
133  $tmpcountdeleted = 0;
134 
135  $result = dol_delete_dir_recursive($filesarray[$key]['fullname'], $startcount, 1, 0, $tmpcountdeleted);
136 
137  if (!in_array($filesarray[$key]['fullname'], array($conf->api->dir_temp, $conf->user->dir_temp))) { // The 2 directories $conf->api->dir_temp and $conf->user->dir_temp are recreated at end, so we do not count them
138  $count += $result;
139  $countdeleted += $tmpcountdeleted;
140  }
141  } elseif ($filesarray[$key]['type'] == 'file') {
142  // If (file that is not logfile) or (if mode is logfile)
143  if ($filesarray[$key]['fullname'] != $filelog || $choice == 'logfile')
144  {
145  $result = dol_delete_file($filesarray[$key]['fullname'], 1, 1);
146  if ($result)
147  {
148  $count++;
149  $countdeleted++;
150  } else {
151  $counterror++;
152  }
153  }
154  }
155  }
156 
157  // Update cachenbofdoc
158  if (!empty($conf->ecm->enabled) && $choice == 'allfiles')
159  {
160  require_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmdirectory.class.php';
161  $ecmdirstatic = new EcmDirectory($this->db);
162  $result = $ecmdirstatic->refreshcachenboffile(1);
163  }
164  }
165  }
166 
167  if ($count > 0) {
168  $this->output = $langs->trans("PurgeNDirectoriesDeleted", $countdeleted);
169  if ($count > $countdeleted) $this->output .= '<br>'.$langs->trans("PurgeNDirectoriesFailed", ($count - $countdeleted));
170  } else {
171  $this->output = $langs->trans("PurgeNothingToDelete").(in_array('tempfilesold', $choicesarray) ? ' (older than 24h for temp files)' : '');
172  }
173 
174  // Recreate temp dir that are not automatically recreated by core code for performance purpose, we need them
175  if (!empty($conf->api->enabled)) {
176  dol_mkdir($conf->api->dir_temp);
177  }
178  dol_mkdir($conf->user->dir_temp);
179 
180  //return $count;
181  return 0; // This function can be called by cron so must return 0 if OK
182  }
183 
184 
197  public function dumpDatabase($compression = 'none', $type = 'auto', $usedefault = 1, $file = 'auto', $keeplastnfiles = 0, $execmethod = 0)
198  {
199  global $db, $conf, $langs, $dolibarr_main_data_root;
200  global $dolibarr_main_db_name, $dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_port, $dolibarr_main_db_pass;
201 
202  $langs->load("admin");
203 
204  dol_syslog("Utils::dumpDatabase type=".$type." compression=".$compression." file=".$file, LOG_DEBUG);
205  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
206 
207  // Check compression parameter
208  if (!in_array($compression, array('none', 'gz', 'bz', 'zip')))
209  {
210  $langs->load("errors");
211  $this->error = $langs->transnoentitiesnoconv("ErrorBadValueForParameter", $compression, "Compression");
212  return -1;
213  }
214 
215  // Check type parameter
216  if ($type == 'auto') $type = $this->db->type;
217  if (!in_array($type, array('postgresql', 'pgsql', 'mysql', 'mysqli', 'mysqlnobin')))
218  {
219  $langs->load("errors");
220  $this->error = $langs->transnoentitiesnoconv("ErrorBadValueForParameter", $type, "Basetype");
221  return -1;
222  }
223 
224  // Check file parameter
225  if ($file == 'auto')
226  {
227  $prefix = 'dump';
228  $ext = 'sql';
229  if (in_array($type, array('mysql', 'mysqli'))) { $prefix = 'mysqldump'; $ext = 'sql'; }
230  //if ($label == 'PostgreSQL') { $prefix='pg_dump'; $ext='dump'; }
231  if (in_array($type, array('pgsql'))) { $prefix = 'pg_dump'; $ext = 'sql'; }
232  $file = $prefix.'_'.$dolibarr_main_db_name.'_'.dol_sanitizeFileName(DOL_VERSION).'_'.strftime("%Y%m%d%H%M").'.'.$ext;
233  }
234 
235  $outputdir = $conf->admin->dir_output.'/backup';
236  $result = dol_mkdir($outputdir);
237  $errormsg = '';
238 
239  // MYSQL
240  if ($type == 'mysql' || $type == 'mysqli')
241  {
242  $cmddump = $conf->global->SYSTEMTOOLS_MYSQLDUMP;
243 
244 
245  $outputfile = $outputdir.'/'.$file;
246  // for compression format, we add extension
247  $compression = $compression ? $compression : 'none';
248  if ($compression == 'gz') $outputfile .= '.gz';
249  if ($compression == 'bz') $outputfile .= '.bz2';
250  $outputerror = $outputfile.'.err';
251  dol_mkdir($conf->admin->dir_output.'/backup');
252 
253  // Parameteres execution
254  $command = $cmddump;
255  $command = preg_replace('/(\$|%)/', '', $command); // We removed chars that can be used to inject vars that contains space inside path of command without seeing there is a space to bypass the escapeshellarg.
256  if (preg_match("/\s/", $command)) $command = escapeshellarg($command); // If there is spaces, we add quotes on command to be sure $command is only a program and not a program+parameters
257 
258  //$param=escapeshellarg($dolibarr_main_db_name)." -h ".escapeshellarg($dolibarr_main_db_host)." -u ".escapeshellarg($dolibarr_main_db_user)." -p".escapeshellarg($dolibarr_main_db_pass);
259  $param = $dolibarr_main_db_name." -h ".$dolibarr_main_db_host;
260  $param .= " -u ".$dolibarr_main_db_user;
261  if (!empty($dolibarr_main_db_port)) $param .= " -P ".$dolibarr_main_db_port;
262  if (!GETPOST("use_transaction", "alpha")) $param .= " -l --single-transaction";
263  if (GETPOST("disable_fk", "alpha") || $usedefault) $param .= " -K";
264  if (GETPOST("sql_compat", "alpha") && GETPOST("sql_compat", "alpha") != 'NONE') $param .= " --compatible=".escapeshellarg(GETPOST("sql_compat", "alpha"));
265  if (GETPOST("drop_database", "alpha")) $param .= " --add-drop-database";
266  if (GETPOST("use_mysql_quick_param", "alpha"))$param .= " --quick";
267  if (GETPOST("sql_structure", "alpha") || $usedefault)
268  {
269  if (GETPOST("drop", "alpha") || $usedefault) $param .= " --add-drop-table=TRUE";
270  else $param .= " --add-drop-table=FALSE";
271  } else {
272  $param .= " -t";
273  }
274  if (GETPOST("disable-add-locks", "alpha")) $param .= " --add-locks=FALSE";
275  if (GETPOST("sql_data", "alpha") || $usedefault)
276  {
277  $param .= " --tables";
278  if (GETPOST("showcolumns", "alpha") || $usedefault) $param .= " -c";
279  if (GETPOST("extended_ins", "alpha") || $usedefault) $param .= " -e";
280  else $param .= " --skip-extended-insert";
281  if (GETPOST("delayed", "alpha")) $param .= " --delayed-insert";
282  if (GETPOST("sql_ignore", "alpha")) $param .= " --insert-ignore";
283  if (GETPOST("hexforbinary", "alpha") || $usedefault) $param .= " --hex-blob";
284  } else {
285  $param .= " -d"; // No row information (no data)
286  }
287  $param .= " --default-character-set=utf8"; // We always save output into utf8 charset
288  $paramcrypted = $param;
289  $paramclear = $param;
290  if (!empty($dolibarr_main_db_pass))
291  {
292  $paramcrypted .= ' -p"'.preg_replace('/./i', '*', $dolibarr_main_db_pass).'"';
293  $paramclear .= ' -p"'.str_replace(array('"', '`', '$'), array('\"', '\`', '\$'), $dolibarr_main_db_pass).'"';
294  }
295 
296  $handle = '';
297 
298  // Start call method to execute dump
299  $fullcommandcrypted = $command." ".$paramcrypted." 2>&1";
300  $fullcommandclear = $command." ".$paramclear." 2>&1";
301  if ($compression == 'none') $handle = fopen($outputfile, 'w');
302  if ($compression == 'gz') $handle = gzopen($outputfile, 'w');
303  if ($compression == 'bz') $handle = bzopen($outputfile, 'w');
304 
305  $ok = 0;
306  if ($handle)
307  {
308  if (!empty($conf->global->MAIN_EXEC_USE_POPEN)) $execmethod = $conf->global->MAIN_EXEC_USE_POPEN;
309  if (empty($execmethod)) $execmethod = 1;
310 
311  dol_syslog("Utils::dumpDatabase execmethod=".$execmethod." command:".$fullcommandcrypted, LOG_DEBUG);
312 
313  // TODO Replace with executeCLI function
314  if ($execmethod == 1)
315  {
316  $output_arr = array(); $retval = null;
317  exec($fullcommandclear, $output_arr, $retval);
318 
319  if ($retval != 0)
320  {
321  $langs->load("errors");
322  dol_syslog("Datadump retval after exec=".$retval, LOG_ERR);
323  $errormsg = 'Error '.$retval;
324  $ok = 0;
325  } else {
326  $i = 0;
327  if (!empty($output_arr))
328  {
329  foreach ($output_arr as $key => $read)
330  {
331  $i++; // output line number
332  if ($i == 1 && preg_match('/Warning.*Using a password/i', $read)) continue;
333  fwrite($handle, $read.($execmethod == 2 ? '' : "\n"));
334  if (preg_match('/'.preg_quote('-- Dump completed').'/i', $read)) $ok = 1;
335  elseif (preg_match('/'.preg_quote('SET SQL_NOTES=@OLD_SQL_NOTES').'/i', $read)) $ok = 1;
336  }
337  }
338  }
339  }
340  if ($execmethod == 2) // With this method, there is no way to get the return code, only output
341  {
342  $handlein = popen($fullcommandclear, 'r');
343  $i = 0;
344  while (!feof($handlein))
345  {
346  $i++; // output line number
347  $read = fgets($handlein);
348  // Exclude warning line we don't want
349  if ($i == 1 && preg_match('/Warning.*Using a password/i', $read)) continue;
350  fwrite($handle, $read);
351  if (preg_match('/'.preg_quote('-- Dump completed').'/i', $read)) $ok = 1;
352  elseif (preg_match('/'.preg_quote('SET SQL_NOTES=@OLD_SQL_NOTES').'/i', $read)) $ok = 1;
353  }
354  pclose($handlein);
355  }
356 
357 
358  if ($compression == 'none') fclose($handle);
359  if ($compression == 'gz') gzclose($handle);
360  if ($compression == 'bz') bzclose($handle);
361 
362  if (!empty($conf->global->MAIN_UMASK))
363  @chmod($outputfile, octdec($conf->global->MAIN_UMASK));
364  } else {
365  $langs->load("errors");
366  dol_syslog("Failed to open file ".$outputfile, LOG_ERR);
367  $errormsg = $langs->trans("ErrorFailedToWriteInDir");
368  }
369 
370  // Get errorstring
371  if ($compression == 'none') $handle = fopen($outputfile, 'r');
372  if ($compression == 'gz') $handle = gzopen($outputfile, 'r');
373  if ($compression == 'bz') $handle = bzopen($outputfile, 'r');
374  if ($handle)
375  {
376  // Get 2048 first chars of error message.
377  $errormsg = fgets($handle, 2048);
378  //$ok=0;$errormsg=''; To force error
379 
380  // Close file
381  if ($compression == 'none') fclose($handle);
382  if ($compression == 'gz') gzclose($handle);
383  if ($compression == 'bz') bzclose($handle);
384  if ($ok && preg_match('/^-- (MySql|MariaDB)/i', $errormsg)) { // No error
385  $errormsg = '';
386  } else {
387  // Renommer fichier sortie en fichier erreur
388  //print "$outputfile -> $outputerror";
389  @dol_delete_file($outputerror, 1, 0, 0, null, false, 0);
390  @rename($outputfile, $outputerror);
391  // Si safe_mode on et command hors du parametre exec, on a un fichier out vide donc errormsg vide
392  if (!$errormsg)
393  {
394  $langs->load("errors");
395  $errormsg = $langs->trans("ErrorFailedToRunExternalCommand");
396  }
397  }
398  }
399  // Fin execution commande
400 
401  $this->output = $errormsg;
402  $this->error = $errormsg;
403  $this->result = array("commandbackuplastdone" => $command." ".$paramcrypted, "commandbackuptorun" => "");
404  //if (empty($this->output)) $this->output=$this->result['commandbackuplastdone'];
405  }
406 
407  // MYSQL NO BIN
408  if ($type == 'mysqlnobin')
409  {
410  $outputfile = $outputdir.'/'.$file;
411  $outputfiletemp = $outputfile.'-TMP.sql';
412  // for compression format, we add extension
413  $compression = $compression ? $compression : 'none';
414  if ($compression == 'gz') $outputfile .= '.gz';
415  if ($compression == 'bz') $outputfile .= '.bz2';
416  $outputerror = $outputfile.'.err';
417  dol_mkdir($conf->admin->dir_output.'/backup');
418 
419  if ($compression == 'gz' or $compression == 'bz')
420  {
421  $this->backupTables($outputfiletemp);
422  dol_compress_file($outputfiletemp, $outputfile, $compression);
423  unlink($outputfiletemp);
424  } else {
425  $this->backupTables($outputfile);
426  }
427 
428  $this->output = "";
429  $this->result = array("commandbackuplastdone" => "", "commandbackuptorun" => "");
430  }
431 
432  // POSTGRESQL
433  if ($type == 'postgresql' || $type == 'pgsql')
434  {
435  $cmddump = $conf->global->SYSTEMTOOLS_POSTGRESQLDUMP;
436 
437  $outputfile = $outputdir.'/'.$file;
438  // for compression format, we add extension
439  $compression = $compression ? $compression : 'none';
440  if ($compression == 'gz') $outputfile .= '.gz';
441  if ($compression == 'bz') $outputfile .= '.bz2';
442  $outputerror = $outputfile.'.err';
443  dol_mkdir($conf->admin->dir_output.'/backup');
444 
445  // Parameteres execution
446  $command = $cmddump;
447  $command = preg_replace('/(\$|%)/', '', $command); // We removed chars that can be used to inject vars that contains space inside path of command without seeing there is a space to bypass the escapeshellarg.
448  if (preg_match("/\s/", $command)) $command = escapeshellarg($command); // If there is spaces, we add quotes on command to be sure $command is only a program and not a program+parameters
449 
450  //$param=escapeshellarg($dolibarr_main_db_name)." -h ".escapeshellarg($dolibarr_main_db_host)." -u ".escapeshellarg($dolibarr_main_db_user)." -p".escapeshellarg($dolibarr_main_db_pass);
451  //$param="-F c";
452  $param = "-F p";
453  $param .= " --no-tablespaces --inserts -h ".$dolibarr_main_db_host;
454  $param .= " -U ".$dolibarr_main_db_user;
455  if (!empty($dolibarr_main_db_port)) $param .= " -p ".$dolibarr_main_db_port;
456  if (GETPOST("sql_compat") && GETPOST("sql_compat") == 'ANSI') $param .= " --disable-dollar-quoting";
457  if (GETPOST("drop_database")) $param .= " -c -C";
458  if (GETPOST("sql_structure"))
459  {
460  if (GETPOST("drop")) $param .= " --add-drop-table";
461  if (!GETPOST("sql_data")) $param .= " -s";
462  }
463  if (GETPOST("sql_data"))
464  {
465  if (!GETPOST("sql_structure")) $param .= " -a";
466  if (GETPOST("showcolumns")) $param .= " -c";
467  }
468  $param .= ' -f "'.$outputfile.'"';
469  //if ($compression == 'none')
470  if ($compression == 'gz') $param .= ' -Z 9';
471  //if ($compression == 'bz')
472  $paramcrypted = $param;
473  $paramclear = $param;
474  /*if (! empty($dolibarr_main_db_pass))
475  {
476  $paramcrypted.=" -W".preg_replace('/./i','*',$dolibarr_main_db_pass);
477  $paramclear.=" -W".$dolibarr_main_db_pass;
478  }*/
479  $paramcrypted .= " -w ".$dolibarr_main_db_name;
480  $paramclear .= " -w ".$dolibarr_main_db_name;
481 
482  $this->output = "";
483  $this->result = array("commandbackuplastdone" => "", "commandbackuptorun" => $command." ".$paramcrypted);
484  }
485 
486  // Clean old files
487  if (!$errormsg && $keeplastnfiles > 0)
488  {
489  $tmpfiles = dol_dir_list($conf->admin->dir_output.'/backup', 'files', 0, '', '(\.err|\.old|\.sav)$', 'date', SORT_DESC);
490  $i = 0;
491  foreach ($tmpfiles as $key => $val)
492  {
493  $i++;
494  if ($i <= $keeplastnfiles) continue;
495  dol_delete_file($val['fullname'], 0, 0, 0, null, false, 0);
496  }
497  }
498 
499  return ($errormsg ? -1 : 0);
500  }
501 
502 
503 
512  public function executeCLI($command, $outputfile, $execmethod = 0)
513  {
514  global $conf, $langs;
515 
516  $result = 0;
517  $output = '';
518  $error = '';
519 
520  $command = escapeshellcmd($command);
521  $command .= " 2>&1";
522 
523  if (!empty($conf->global->MAIN_EXEC_USE_POPEN)) $execmethod = $conf->global->MAIN_EXEC_USE_POPEN;
524  if (empty($execmethod)) $execmethod = 1;
525  //$execmethod=1;
526 
527  dol_syslog("Utils::executeCLI execmethod=".$execmethod." system:".$command, LOG_DEBUG);
528  $output_arr = array();
529 
530  if ($execmethod == 1)
531  {
532  $retval = null;
533  exec($command, $output_arr, $retval);
534  $result = $retval;
535  if ($retval != 0)
536  {
537  $langs->load("errors");
538  dol_syslog("Utils::executeCLI retval after exec=".$retval, LOG_ERR);
539  $error = 'Error '.$retval;
540  }
541  }
542  if ($execmethod == 2) // With this method, there is no way to get the return code, only output
543  {
544  $handle = fopen($outputfile, 'w+b');
545  if ($handle)
546  {
547  dol_syslog("Utils::executeCLI run command ".$command);
548  $handlein = popen($command, 'r');
549  while (!feof($handlein))
550  {
551  $read = fgets($handlein);
552  fwrite($handle, $read);
553  $output_arr[] = $read;
554  }
555  pclose($handlein);
556  fclose($handle);
557  }
558  if (!empty($conf->global->MAIN_UMASK)) @chmod($outputfile, octdec($conf->global->MAIN_UMASK));
559  }
560 
561  // Update with result
562  if (is_array($output_arr) && count($output_arr) > 0)
563  {
564  foreach ($output_arr as $val)
565  {
566  $output .= $val.($execmethod == 2 ? '' : "\n");
567  }
568  }
569 
570  dol_syslog("Utils::executeCLI result=".$result." output=".$output." error=".$error, LOG_DEBUG);
571 
572  return array('result'=>$result, 'output'=>$output, 'error'=>$error);
573  }
574 
581  public function generateDoc($module)
582  {
583  global $conf, $langs, $user, $mysoc;
584  global $dirins;
585 
586  $error = 0;
587 
588  $modulelowercase = strtolower($module);
589  $now = dol_now();
590 
591  // Dir for module
592  $dir = $dirins.'/'.$modulelowercase;
593  // Zip file to build
594  $FILENAMEDOC = '';
595 
596  // Load module
597  dol_include_once($modulelowercase.'/core/modules/mod'.$module.'.class.php');
598  $class = 'mod'.$module;
599 
600  if (class_exists($class))
601  {
602  try {
603  $moduleobj = new $class($this->db);
604  } catch (Exception $e)
605  {
606  $error++;
607  dol_print_error($e->getMessage());
608  }
609  } else {
610  $error++;
611  $langs->load("errors");
612  dol_print_error($langs->trans("ErrorFailedToLoadModuleDescriptorForXXX", $module));
613  exit;
614  }
615 
616  $arrayversion = explode('.', $moduleobj->version, 3);
617  if (count($arrayversion))
618  {
619  $FILENAMEASCII = strtolower($module).'.asciidoc';
620  $FILENAMEDOC = strtolower($module).'.html';
621  $FILENAMEDOCPDF = strtolower($module).'.pdf';
622 
623  $dirofmodule = dol_buildpath(strtolower($module), 0);
624  $dirofmoduledoc = dol_buildpath(strtolower($module), 0).'/doc';
625  $dirofmoduletmp = dol_buildpath(strtolower($module), 0).'/doc/temp';
626  $outputfiledoc = $dirofmoduledoc.'/'.$FILENAMEDOC;
627  if ($dirofmoduledoc)
628  {
629  if (!dol_is_dir($dirofmoduledoc)) dol_mkdir($dirofmoduledoc);
630  if (!dol_is_dir($dirofmoduletmp)) dol_mkdir($dirofmoduletmp);
631  if (!is_writable($dirofmoduletmp))
632  {
633  $this->error = 'Dir '.$dirofmoduletmp.' does not exists or is not writable';
634  return -1;
635  }
636 
637  if (empty($conf->global->MODULEBUILDER_ASCIIDOCTOR) && empty($conf->global->MODULEBUILDER_ASCIIDOCTORPDF))
638  {
639  $this->error = 'Setup of module ModuleBuilder not complete';
640  return -1;
641  }
642 
643  // Copy some files into temp directory, so instruction include::ChangeLog.md[] will works inside the asciidoc file.
644  dol_copy($dirofmodule.'/README.md', $dirofmoduletmp.'/README.md', 0, 1);
645  dol_copy($dirofmodule.'/ChangeLog.md', $dirofmoduletmp.'/ChangeLog.md', 0, 1);
646 
647  // Replace into README.md and ChangeLog.md (in case they are included into documentation with tag __README__ or __CHANGELOG__)
648  $arrayreplacement = array();
649  $arrayreplacement['/^#\s.*/m'] = ''; // Remove first level of title into .md files
650  $arrayreplacement['/^#/m'] = '##'; // Add on # to increase level
651 
652  dolReplaceInFile($dirofmoduletmp.'/README.md', $arrayreplacement, '', 0, 0, 1);
653  dolReplaceInFile($dirofmoduletmp.'/ChangeLog.md', $arrayreplacement, '', 0, 0, 1);
654 
655 
656  $destfile = $dirofmoduletmp.'/'.$FILENAMEASCII;
657 
658  $fhandle = fopen($destfile, 'w+');
659  if ($fhandle)
660  {
661  $specs = dol_dir_list(dol_buildpath(strtolower($module).'/doc', 0), 'files', 1, '(\.md|\.asciidoc)$', array('\/temp\/'));
662 
663  $i = 0;
664  foreach ($specs as $spec)
665  {
666  if (preg_match('/notindoc/', $spec['relativename'])) continue; // Discard file
667  if (preg_match('/example/', $spec['relativename'])) continue; // Discard file
668  if (preg_match('/disabled/', $spec['relativename'])) continue; // Discard file
669 
670  $pathtofile = strtolower($module).'/doc/'.$spec['relativename'];
671  $format = 'asciidoc';
672  if (preg_match('/\.md$/i', $spec['name'])) $format = 'markdown';
673 
674  $filecursor = @file_get_contents($spec['fullname']);
675  if ($filecursor)
676  {
677  fwrite($fhandle, ($i ? "\n<<<\n\n" : "").$filecursor."\n");
678  } else {
679  $this->error = 'Failed to concat content of file '.$spec['fullname'];
680  return -1;
681  }
682 
683  $i++;
684  }
685 
686  fclose($fhandle);
687 
688  $contentreadme = file_get_contents($dirofmoduletmp.'/README.md');
689  $contentchangelog = file_get_contents($dirofmoduletmp.'/ChangeLog.md');
690 
691  include DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
692 
693  //var_dump($phpfileval['fullname']);
694  $arrayreplacement = array(
695  'mymodule'=>strtolower($module),
696  'MyModule'=>$module,
697  'MYMODULE'=>strtoupper($module),
698  'My module'=>$module,
699  'my module'=>$module,
700  'Mon module'=>$module,
701  'mon module'=>$module,
702  'htdocs/modulebuilder/template'=>strtolower($module),
703  '__MYCOMPANY_NAME__'=>$mysoc->name,
704  '__KEYWORDS__'=>$module,
705  '__USER_FULLNAME__'=>$user->getFullName($langs),
706  '__USER_EMAIL__'=>$user->email,
707  '__YYYY-MM-DD__'=>dol_print_date($now, 'dayrfc'),
708  '---Put here your own copyright and developer email---'=>dol_print_date($now, 'dayrfc').' '.$user->getFullName($langs).($user->email ? ' <'.$user->email.'>' : ''),
709  '__DATA_SPECIFICATION__'=>'Not yet available',
710  '__README__'=>dolMd2Asciidoc($contentreadme),
711  '__CHANGELOG__'=>dolMd2Asciidoc($contentchangelog),
712  );
713 
714  dolReplaceInFile($destfile, $arrayreplacement);
715  }
716 
717  // Launch doc generation
718  $currentdir = getcwd();
719  chdir($dirofmodule);
720 
721  require_once DOL_DOCUMENT_ROOT.'/core/class/utils.class.php';
722  $utils = new Utils($this->db);
723 
724  // Build HTML doc
725  $command = $conf->global->MODULEBUILDER_ASCIIDOCTOR.' '.$destfile.' -n -o '.$dirofmoduledoc.'/'.$FILENAMEDOC;
726  $outfile = $dirofmoduletmp.'/out.tmp';
727 
728  $resarray = $utils->executeCLI($command, $outfile);
729  if ($resarray['result'] != '0')
730  {
731  $this->error = $resarray['error'].' '.$resarray['output'];
732  }
733  $result = ($resarray['result'] == 0) ? 1 : 0;
734 
735  // Build PDF doc
736  $command = $conf->global->MODULEBUILDER_ASCIIDOCTORPDF.' '.$destfile.' -n -o '.$dirofmoduledoc.'/'.$FILENAMEDOCPDF;
737  $outfile = $dirofmoduletmp.'/outpdf.tmp';
738  $resarray = $utils->executeCLI($command, $outfile);
739  if ($resarray['result'] != '0')
740  {
741  $this->error = $resarray['error'].' '.$resarray['output'];
742  }
743  $result = ($resarray['result'] == 0) ? 1 : 0;
744 
745  chdir($currentdir);
746  } else {
747  $result = 0;
748  }
749 
750  if ($result > 0)
751  {
752  return 1;
753  } else {
754  $error++;
755  $langs->load("errors");
756  $this->error = $langs->trans("ErrorFailToGenerateFile", $outputfiledoc);
757  }
758  } else {
759  $error++;
760  $langs->load("errors");
761  $this->error = $langs->trans("ErrorCheckVersionIsDefined");
762  }
763 
764  return -1;
765  }
766 
774  public function compressSyslogs()
775  {
776  global $conf;
777 
778  if (empty($conf->loghandlers['mod_syslog_file'])) { // File Syslog disabled
779  return 0;
780  }
781 
782  if (!function_exists('gzopen')) {
783  $this->error = 'Support for gzopen not available in this PHP';
784  return -1;
785  }
786 
787  dol_include_once('/core/lib/files.lib.php');
788 
789  $nbSaves = empty($conf->global->SYSLOG_FILE_SAVES) ? 10 : intval($conf->global->SYSLOG_FILE_SAVES);
790 
791  if (empty($conf->global->SYSLOG_FILE)) {
792  $mainlogdir = DOL_DATA_ROOT;
793  $mainlog = 'dolibarr.log';
794  } else {
795  $mainlogfull = str_replace('DOL_DATA_ROOT', DOL_DATA_ROOT, $conf->global->SYSLOG_FILE);
796  $mainlogdir = dirname($mainlogfull);
797  $mainlog = basename($mainlogfull);
798  }
799 
800  $tabfiles = dol_dir_list(DOL_DATA_ROOT, 'files', 0, '^(dolibarr_.+|odt2pdf)\.log$'); // Also handle other log files like dolibarr_install.log
801  $tabfiles[] = array('name' => $mainlog, 'path' => $mainlogdir);
802 
803  foreach ($tabfiles as $file) {
804  $logname = $file['name'];
805  $logpath = $file['path'];
806 
807  if (dol_is_file($logpath.'/'.$logname) && dol_filesize($logpath.'/'.$logname) > 0) // If log file exists and is not empty
808  {
809  // Handle already compressed files to rename them and add +1
810 
811  $filter = '^'.preg_quote($logname, '/').'\.([0-9]+)\.gz$';
812 
813  $gzfilestmp = dol_dir_list($logpath, 'files', 0, $filter);
814  $gzfiles = array();
815 
816  foreach ($gzfilestmp as $gzfile) {
817  $tabmatches = array();
818  preg_match('/'.$filter.'/i', $gzfile['name'], $tabmatches);
819 
820  $numsave = intval($tabmatches[1]);
821 
822  $gzfiles[$numsave] = $gzfile;
823  }
824 
825  krsort($gzfiles, SORT_NUMERIC);
826 
827  foreach ($gzfiles as $numsave => $dummy) {
828  if (dol_is_file($logpath.'/'.$logname.'.'.($numsave + 1).'.gz')) {
829  return -2;
830  }
831 
832  if ($numsave >= $nbSaves) {
833  dol_delete_file($logpath.'/'.$logname.'.'.$numsave.'.gz', 0, 0, 0, null, false, 0);
834  } else {
835  dol_move($logpath.'/'.$logname.'.'.$numsave.'.gz', $logpath.'/'.$logname.'.'.($numsave + 1).'.gz', 0, 1, 0, 0);
836  }
837  }
838 
839  // Compress current file and recreate it
840 
841  if ($nbSaves > 0) { // If $nbSaves is 1, we keep 1 archive .gz file, If 2, we keep 2 .gz files
842  $gzfilehandle = gzopen($logpath.'/'.$logname.'.1.gz', 'wb9');
843 
844  if (empty($gzfilehandle)) {
845  $this->error = 'Failted to open file '.$logpath.'/'.$logname.'.1.gz';
846  return -3;
847  }
848 
849  $sourcehandle = fopen($logpath.'/'.$logname, 'r');
850 
851  if (empty($sourcehandle)) {
852  $this->error = 'Failed to open file '.$logpath.'/'.$logname;
853  return -4;
854  }
855 
856  while (!feof($sourcehandle)) {
857  gzwrite($gzfilehandle, fread($sourcehandle, 512 * 1024)); // Read 512 kB at a time
858  }
859 
860  fclose($sourcehandle);
861  gzclose($gzfilehandle);
862 
863  @chmod($logpath.'/'.$logname.'.1.gz', octdec(empty($conf->global->MAIN_UMASK) ? '0664' : $conf->global->MAIN_UMASK));
864  }
865 
866  dol_delete_file($logpath.'/'.$logname, 0, 0, 0, null, false, 0);
867 
868  // Create empty file
869  $newlog = fopen($logpath.'/'.$logname, 'a+');
870  fclose($newlog);
871 
872  //var_dump($logpath.'/'.$logname." - ".octdec(empty($conf->global->MAIN_UMASK)?'0664':$conf->global->MAIN_UMASK));
873  @chmod($logpath.'/'.$logname, octdec(empty($conf->global->MAIN_UMASK) ? '0664' : $conf->global->MAIN_UMASK));
874  }
875  }
876 
877  $this->output = 'Archive log files (keeping last SYSLOG_FILE_SAVES='.$nbSaves.' files) done.';
878  return 0;
879  }
880 
891  public function backupTables($outputfile, $tables = '*')
892  {
893  global $db, $langs;
894  global $errormsg;
895 
896  // Set to UTF-8
897  if (is_a($db, 'DoliDBMysqli')) {
899  $db->db->set_charset('utf8');
900  } else {
902  $db->query('SET NAMES utf8');
903  $db->query('SET CHARACTER SET utf8');
904  }
905 
906  //get all of the tables
907  if ($tables == '*')
908  {
909  $tables = array();
910  $result = $db->query('SHOW FULL TABLES WHERE Table_type = \'BASE TABLE\'');
911  while ($row = $db->fetch_row($result))
912  {
913  $tables[] = $row[0];
914  }
915  } else {
916  $tables = is_array($tables) ? $tables : explode(',', $tables);
917  }
918 
919  //cycle through
920  $handle = fopen($outputfile, 'w+');
921  if (fwrite($handle, '') === false)
922  {
923  $langs->load("errors");
924  dol_syslog("Failed to open file ".$outputfile, LOG_ERR);
925  $errormsg = $langs->trans("ErrorFailedToWriteInDir");
926  return -1;
927  }
928 
929  // Print headers and global mysql config vars
930  $sqlhead = '';
931  $sqlhead .= "-- ".$db::LABEL." dump via php with Dolibarr ".DOL_VERSION."
932 --
933 -- Host: ".$db->db->host_info." Database: ".$db->database_name."
934 -- ------------------------------------------------------
935 -- Server version ".$db->db->server_info."
936 ;;;;;;;;;;
947 
948 ";
949 
950  if (GETPOST("nobin_disable_fk")) $sqlhead .= "SET FOREIGN_KEY_CHECKS=0;\n";
951  //$sqlhead .= "SET SQL_MODE=\"NO_AUTO_VALUE_ON_ZERO\";\n";
952  if (GETPOST("nobin_use_transaction")) $sqlhead .= "SET AUTOCOMMIT=0;\nSTART TRANSACTION;\n";
953 
954  fwrite($handle, $sqlhead);
955 
956  $ignore = '';
957  if (GETPOST("nobin_sql_ignore")) $ignore = 'IGNORE ';
958  $delayed = '';
959  if (GETPOST("nobin_delayed")) $delayed = 'DELAYED ';
960 
961  // Process each table and print their definition + their datas
962  foreach ($tables as $table)
963  {
964  // Saving the table structure
965  fwrite($handle, "\n--\n-- Table structure for table `".$table."`\n--\n");
966 
967  if (GETPOST("nobin_drop")) fwrite($handle, "DROP TABLE IF EXISTS `".$table."`;\n"); // Dropping table if exists prior to re create it
968  fwrite($handle, "/*!40101 SET @saved_cs_client = @@character_set_client */;\n");
969  fwrite($handle, "/*!40101 SET character_set_client = utf8 */;\n");
970  $resqldrop = $db->query('SHOW CREATE TABLE '.$table);
971  $row2 = $db->fetch_row($resqldrop);
972  if (empty($row2[1]))
973  {
974  fwrite($handle, "\n-- WARNING: Show create table ".$table." return empy string when it should not.\n");
975  } else {
976  fwrite($handle, $row2[1].";\n");
977  //fwrite($handle,"/*!40101 SET character_set_client = @saved_cs_client */;\n\n");
978 
979  // Dumping the data (locking the table and disabling the keys check while doing the process)
980  fwrite($handle, "\n--\n-- Dumping data for table `".$table."`\n--\n");
981  if (!GETPOST("nobin_nolocks")) fwrite($handle, "LOCK TABLES `".$table."` WRITE;\n"); // Lock the table before inserting data (when the data will be imported back)
982  if (GETPOST("nobin_disable_fk")) fwrite($handle, "ALTER TABLE `".$table."` DISABLE KEYS;\n");
983  else fwrite($handle, "/*!40000 ALTER TABLE `".$table."` DISABLE KEYS */;\n");
984 
985  $sql = 'SELECT * FROM '.$table; // Here SELECT * is allowed because we don't have definition of columns to take
986  $result = $db->query($sql);
987  while ($row = $db->fetch_row($result))
988  {
989  // For each row of data we print a line of INSERT
990  fwrite($handle, 'INSERT '.$delayed.$ignore.'INTO `'.$table.'` VALUES (');
991  $columns = count($row);
992  for ($j = 0; $j < $columns; $j++) {
993  // Processing each columns of the row to ensure that we correctly save the value (eg: add quotes for string - in fact we add quotes for everything, it's easier)
994  if ($row[$j] == null && !is_string($row[$j])) {
995  // IMPORTANT: if the field is NULL we set it NULL
996  $row[$j] = 'NULL';
997  } elseif (is_string($row[$j]) && $row[$j] == '') {
998  // if it's an empty string, we set it as an empty string
999  $row[$j] = "''";
1000  } elseif (is_numeric($row[$j]) && !strcmp($row[$j], $row[$j] + 0)) { // test if it's a numeric type and the numeric version ($nb+0) == string version (eg: if we have 01, it's probably not a number but rather a string, else it would not have any leading 0)
1001  // if it's a number, we return it as-is
1002  // $row[$j] = $row[$j];
1003  } else { // else for all other cases we escape the value and put quotes around
1004  $row[$j] = addslashes($row[$j]);
1005  $row[$j] = preg_replace("#\n#", "\\n", $row[$j]);
1006  $row[$j] = "'".$row[$j]."'";
1007  }
1008  }
1009  fwrite($handle, implode(',', $row).");\n");
1010  }
1011  if (GETPOST("nobin_disable_fk")) fwrite($handle, "ALTER TABLE `".$table."` ENABLE KEYS;\n"); // Enabling back the keys/index checking
1012  if (!GETPOST("nobin_nolocks")) fwrite($handle, "UNLOCK TABLES;\n"); // Unlocking the table
1013  fwrite($handle, "\n\n\n");
1014  }
1015  }
1016 
1017  /* Backup Procedure structure*/
1018  /*
1019  $result = $db->query('SHOW PROCEDURE STATUS');
1020  if ($db->num_rows($result) > 0)
1021  {
1022  while ($row = $db->fetch_row($result)) { $procedures[] = $row[1]; }
1023  foreach($procedures as $proc)
1024  {
1025  fwrite($handle,"DELIMITER $$\n\n");
1026  fwrite($handle,"DROP PROCEDURE IF EXISTS '$name'.'$proc'$$\n");
1027  $resqlcreateproc=$db->query("SHOW CREATE PROCEDURE '$proc'");
1028  $row2 = $db->fetch_row($resqlcreateproc);
1029  fwrite($handle,"\n".$row2[2]."$$\n\n");
1030  fwrite($handle,"DELIMITER ;\n\n");
1031  }
1032  }
1033  */
1034  /* Backup Procedure structure*/
1035 
1036  // Write the footer (restore the previous database settings)
1037  $sqlfooter = "\n\n";
1038  if (GETPOST("nobin_use_transaction")) $sqlfooter .= "COMMIT;\n";
1039  if (GETPOST("nobin_disable_fk")) $sqlfooter .= "SET FOREIGN_KEY_CHECKS=1;\n";
1040  $sqlfooter .= "\n\n-- Dump completed on ".date('Y-m-d G-i-s');
1041  fwrite($handle, $sqlfooter);
1042 
1043  fclose($handle);
1044 
1045  return 1;
1046  }
1047 }
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname= '')
Make an include_once using default root and alternate root if it fails.
GETPOST($paramname, $check= 'alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
Class to manage utility methods.
Definition: utils.class.php:28
dol_copy($srcfile, $destfile, $newmask=0, $overwriteifexists=1)
Copy a file to another file.
Definition: files.lib.php:663
dol_now($mode= 'auto')
Return date for now.
dol_filesize($pathoffile)
Return size of a file.
Definition: files.lib.php:555
executeCLI($command, $outputfile, $execmethod=0)
Execute a CLI command.
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:432
compressSyslogs()
This saves syslog files and compresses older ones.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dumpDatabase($compression= 'none', $type= 'auto', $usedefault=1, $file= 'auto', $keeplastnfiles=0, $execmethod=0)
Make a backup of database CAN BE A CRON TASK.
$conf db
API class for accounts.
Definition: inc.php:54
dol_move($srcfile, $destfile, $newmask=0, $overwriteifexists=1, $testvirus=0, $indexdatabase=1)
Move a file into another name.
Definition: files.lib.php:817
purgeFiles($choices= 'tempfilesold, logfile', $nbsecondsold=86400)
Purge files into directory of data files.
Definition: utils.class.php:57
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories) ...
Definition: files.lib.php:1286
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1)
Remove a file or several files with a mask.
Definition: files.lib.php:1144
dol_sanitizeFileName($str, $newstr= '_', $unaccent=1)
Clean a string to use it as a file name.
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:60
dol_is_file($pathoffile)
Return if path is a file.
Definition: files.lib.php:457
dolReplaceInFile($srcfile, $arrayreplacement, $destfile= '', $newmask=0, $indexdatabase=0, $arrayreplacementisregex=0)
Make replacement of strings into a file.
Definition: files.lib.php:585
dol_print_date($time, $format= '', $tzoutput= 'auto', $outputlangs= '', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
generateDoc($module)
Generate documentation of a Module.
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dolMd2Asciidoc($content, $parser= 'dolibarr', $replaceimagepath=null)
Function to parse MD content into ASCIIDOC.
Definition: parsemd.lib.php:64
__construct($db)
Constructor.
Definition: utils.class.php:43
Class to manage ECM directories.
dol_mkdir($dir, $dataroot= '', $newmask=null)
Creation of a directory (this can create recursive subdir)