1 <?php
2 /**
3  * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4  * @author     Myron Turner <turnermm02@shaw.ca>
5  */
6 // must be run within Dokuwiki
7 if(!defined('DOKU_INC')) die();
8 
9 if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
10 if(!defined('DW_COMMITS')) define('DW_COMMITS',DOKU_INC.'lib/plugins/dwcommits/');
11 global $dwc_dbg_log;
12 
13 class helper_plugin_dwcommits extends DokuWiki_Plugin {
14 
15   private $path;
16   private $status_message;
17   private $branches;
18   private $selected_branch ='master';
19   private $sqlite;
20   private $git = '/usr/bin/git';
21   private $repros;
22   private $default_repro;
23   private $db_name;
24   private $remote_url;
25   private $commit_url;
26 
27     function __construct() {
28         global $dwc_dbg_log;
29         if(isset($_REQUEST['dwc__repro']) && $_REQUEST['dwc__repro']) {
30              $this->path = $_REQUEST['dwc__repro'];
31         }
32         else {
33            $this->path = $this->get_conf_repro();
34         }
35         $this->default_repro = $this->path;
36         $this->status_message = array();
37         $this->selected_branch="";
38         $this->sqlite = 0;
39         $binary = $this->getConf('git_binary');
40         if(isset($binary)) $this->git=$binary;
41         $fname_hash = md5($this->path);
42         $names_fname = dirname(__FILE__).'/db/dbnames.ser';
43 
44         if(file_exists($names_fname)) {
45             $inf_str = file_get_contents ($names_fname);
46             $inf = unserialize($inf_str);
47             if($inf[$fname_hash]) {
48                  $this->db_name=$inf[$fname_hash];
49                  $this->remote_url=$this->set_githubURL();
50             }
51             else {
52                $this->db_name=$this->new_dbname($fname_hash,$names_fname,$inf);
53             }
54         }
55         else {
56           $this->db_name=$this->new_dbname($fname_hash,$names_fname, false);
57         }
58         $dwc_dbg_log  =  DW_COMMITS . 'dwc_debug.log';
59 
60     }
61 
62     function new_dbname($fname_hash,$names_fname,$inf_array) {
63 
64        if($inf_array && $inf_array['count']) {
65           $count =  $inf_array['count'] + 1;
66        }
67        else {
68          $count = 1;
69          $inf_array=array();
70        }
71        $inf_array['count'] = $count;
72        $new_fname = 'dwcommits_' . $count;
73        $inf_array[$fname_hash] = $new_fname;
74        $inf_array['git' . $count] = $this->path;
75        file_put_contents($names_fname, serialize($inf_array));
76        return $new_fname ;
77 
78     }
79 
80     function save_dbnames_ser($fname,$content) {
81        if(function_exists(io_saveFile)){
82           return io_saveFile($fname,$content);
83        }
84        else {
85          return file_put_contents($names_fname, $content);
86        }
87     }
88 
89     function set_githubURL($remote_url="") {
90         $names_fname = dirname(__FILE__).'/db/dbnames.ser';
91         $inf_str = file_get_contents ($names_fname);
92         $inf = unserialize($inf_str);
93         if(!$inf) return;
94         $fname_hash = md5($this->path);
95         if(!$inf[$fname_hash]) return;
96         $db = $inf[$fname_hash];
97         list($base,$count) = explode('_',$db);
98         $slot = 'url'.$count;
99         if($remote_url) {
100            $inf[$slot] = $remote_url;
101            if($this->save_dbnames_ser($names_fname,serialize($inf))) {
102                   return "$remote_url saved";
103            }
104            else $this->error(5);
105         }
106         else {
107            if($inf[$slot]) return $inf[$slot];
108         }
109 
110         return "";
111 
112     }
113 
114     function current_dbname() {
115       return $this->db_name;
116     }
117 
118    /* must be called from syntax.php before _getDB() */
119     function setup_syntax($name) {
120         $this->db_name = $name;
121         $names_fname = dirname(__FILE__).'/db/dbnames.ser';
122         $inf_str = file_get_contents ($names_fname);
123         $inf = unserialize($inf_str);
124         if(!$inf) return;
125 
126         list($base,$count) = explode('_',$this->db_name);
127         $slot = 'url'.$count;
128         if($inf[$slot]) {
129              $this->remote_url = $inf[$slot];
130        }
131        else $this->remote_url = "";
132        return $this->remote_url . " slot=$slot";
133     }
134 
135 
136     function get_conf_repro(){
137             $repro = $this->getConf('default_git');
138             if(isset($repro) && $repro) {
139                   return $repro;
140             }
141             else {
142                 return DW_COMMITS . 'db/dokuwiki';
143             }
144     }
145 
146     /**
147      * load the sqlite helper
148      */
149     function _getDB(){
150         static $db = null;
151         if ($db === null) {
152             $db =& plugin_load('helper', 'sqlite');
153             if ($db === null) {
154                 msg('The data plugin needs the sqlite plugin', -1);
155                 return false;
156             }
157 
158             if(!$db->init($this->db_name,dirname(__FILE__).'/db/')){
159                 return false;
160             }
161             if(defined('DOKU_SQLITE_ASSOC'))
162                 $db->fetchmode = DOKU_SQLITE_ASSOC;
163             else $db->fetchmode = 'DOKU_SQLITE_ASSOC';
164         }
165         $this->sqlite = $db;
166         return $db;
167     }
168     function wearehere() {
169         echo 'wearehere';
170     }
171 
172    function chdir() {
173       if(!chdir($this->path)) {
174          if(file_exists($this->path)) {
175                $this->error(1);
176          }
177          else $this->error(0);
178          return false;
179      }
180       return true;
181     }
182 
183    function update_commits($which) {
184         $this->status_message = array();
185         if(!$this->chdir()) return false;
186 
187         if($which == 'fetch') {
188             exec("$this->git fetch origin",$retv,$exit_code);
189             $status = "exit code: " . $exit_code . " ";
190             $this->status_message = array_merge(array(getcwd(),"$this->git fetch origin", $status),$this->status_message,$retv);
191         }
192         elseif($which == 'merge') {
193             exec("$this->git merge origin",$retv, $exit_code);
194             $status = "exit code: " . $exit_code;
195             $this->status_message = array_merge(array(getcwd(),"$this->git merge origin", $status),$this->status_message,$retv);
196         }
197         elseif($which == 'commit') {
198             exec("git commit -mdbupgrade",$retv, $exit_code);
199             $status = "exit code: " . $exit_code;
200             $this->status_message = array_merge(array(getcwd(),"git commit", $status),$this->status_message,$retv);
201             if($exit_code <=1 ) return true;
202         }
203         elseif($which == 'add') {
204             exec("$this->git add .",$retv_add, $exit_code);
205             $status = "exit code: " . $exit_code;
206             $this->status_message = array_merge(array(getcwd(),"git add . ", $status),$this->status_message,$retv_add);
207         }
208         elseif($which == 'pull') {
209             exec("$this->git pull",$retv, $exit_code);
210             $status = "exit code: " . $exit_code;
211             $this->status_message = array_merge(array(getcwd(),"git pull", $status),$this->status_message,$retv);
212         }
213        elseif($which == 'branch') {
214             $branch = $_REQUEST['dwc__branch'];
215             exec("$this->git checkout $branch",$retv, $exit_code);
216             $status = "exit code: " . $exit_code;
217             $this->status_message = array_merge(array(getcwd(),"git checkout $branch", $status),$this->status_message,$retv);
218             $this->set_branches();
219         }
220        elseif($which == 'remote_url') {
221              exec("$this->git  config --get remote.origin.url",$retv, $exit_code);
222              $this->remote_url = $retv[0];
223              $this->remote_url = preg_replace('/:(?!\/)/',"/",$this->remote_url);
224              $this->remote_url = preg_replace('/^\s*.*?@/',"",$this->remote_url);
225              if(!preg_match('/http/',$this->remote_url)) {
226                    if(preg_match('/github/',$this->remote_url)) {
227                      $this->remote_url = 'https://'. $this->remote_url;
228                    }
229                    else $this->remote_url = 'http://'. $this->remote_url;
230              }
231 
232              $this->status_message = array_merge(array(getcwd(),"git checkout $branch", $status),$this->status_message,$retv);
233              if($this->remote_url) {
234                 $this->status_message[] = "Remote URL: $this->remote_url";
235              }
236        }
237             if($exit_code > 0) return false;
238             return true;
239 
240    }
241 
242     function set_commit_url() {
243        if(!$this->remote_url) return false;
244        if($this->commit_url) return $this->commit_url;
245        $url = preg_replace('/\.git$/',"", $this->remote_url) . '/commit/';
246        $this->commit_url=$url;
247        return $url;
248     }
249 
250     function get_remote_url() {
251       return $this->remote_url;
252     }
253 
254    function set_repros() {
255       $this->repros = array();
256       $this->repros[] = $this->html_option($this->path,true);
257       $conf_repro = $this->get_conf_repro();
258       if($this->path != $conf_repro) {
259              $this->repros[] = $this->html_option($conf_repro);
260       }
261       if(file_exists(DOKU_PLUGIN . 'dwcommits/conf/default.local.ini')) {
262          $ini_array = parse_ini_file(DOKU_PLUGIN . 'dwcommits/conf/default.local.ini', true);
263             foreach ($ini_array as $name=>$a) {
264                 if($name == 'other_gits') {
265                     foreach($a as $git_dir) {
266                         $this->repros[] = $this->html_option($git_dir);
267                    }
268                 }
269                 if($name == 'dwc_gits') {
270                     foreach($a as $git_dir) {
271                         $this->repros[] = $this->html_option(DW_COMMITS_DB . $git_dir);
272                    }
273                }
274            }
275         }
276    }
277 
278    function get_repros() {
279       echo implode("\n",$this->repros);
280    }
281 
282    function set_branches() {
283        if(!$this->chdir()) return false;
284        $this->branches = array();
285        exec("$this->git branch",$retv, $exit_code);
286        if($exit_code) return false;
287        foreach ($retv as $branch) {
288         if(preg_match('/\*(.*)/',$branch,$matches)) {
289            $this->selected_branch = $matches[1];
290              $this->branches[] = $this->html_option($matches[1],true);
291         }
292         else {
293             $this->branches[] = $this->html_option($branch);
294         }
295        }
296    }
297 
298    function html_option($val, $selected=false) {
299       $val = trim($val);
300       if(!$selected) {
301         return '<option value="'. $val .'">' . $val .'</option>';
302       }
303       return '<option value="' .$val . '" selected>' . $val . '</option>';
304    }
305 
306    function get_branches() {
307       echo implode("\n",$this->branches);
308    }
309 
310    function selected_branch() {
311           if($this->selected_branch) return $this->selected_branch;
312           $this->selected_branch = 'master'; //needed for db column if and when implemented
313           return 'master';
314    }
315 
316   function selected_repro() {
317           if(isset($_REQUEST['dwc__repro']) && $_REQUEST['dwc__repro']) return $_REQUEST['dwc__repro'];
318           return $this->default_repro;
319    }
320 
321   /*  Seems git status sometimes returns exit code of 1 even when 0 is expected
322       So exit code > 0 can't be trusted to report genuine error. Confirmed via Google
323   */
324    function get_status() {
325       $this->status_message = array();
326       if(!$this->chdir()) return false;
327          exec("$this->git status",$retv, $exit_code);
328          $this->status_message =
329               array_merge(array(getcwd(),"git status"),$this->status_message,$retv);
330         return true;
331 
332    }
333 
334    function error($which, $type=-1) {
335       $path = $this->path;
336       $msgs = array(
337            "Cannot find cloned git at $path",  // 0
338            "Cannot access $path. The entire directory and all its contents must be read/write for the web server.", // 1
339            "Cannot fetch from github",  // 2
340            "Unable to merge",  // 3
341            "Bad Query Construct. Please notify the plugin author.",  // 4
342            "Unable to write to dbnames.ser file.",  // 5
343            "Please check your query. You seem not to have entered any search terms.", // 6
344            "Unable to restore backup; you may not have a backup saved." // 7
345       );
346 
347       msg($msgs[$which],$type);
348 
349    }
350    function get_status_msg() {
351        $status = $this->status_message;
352        $this->status_message = array();
353 
354        $current_git = "<b>Git:</b> $this->path<br/>";
355        if(!is_array($status)) return $current_git;
356        return $current_git . implode('<br />',$status);
357 
358    }
359 
360 function populate($timestamp_start=0,$table='git_commits') {
361 
362      if(!$this->chdir()) return false;
363 
364      $months = array('Jan'=>1,'Feb'=>2,'Mar'=>3,'Apr'=>4,'May'=>5,'Jun'=>6,'Jul'=>7,
365               'Aug'=>8,'Sep'=>9,'Oct'=>10,'Nov'=>11,'Dec'=>12);
366 
367     $count = 0;
368     $start_number = 0;
369     if(!$timestamp_start) {
370        $timestamp_start = mktime(0,0,0,11,11,2010);
371     }
372 
373      $results = $this->sqlite->query("select count(*) from git_commits");
374 
375    $start_number = $this->res2single($results);
376      $since =  date('Y-m-d',$timestamp_start);
377      if(!preg_match('/^\d\d\d\d-\d\d-\d\d$/',$since)) {
378           $since = '2010-11-11';
379      }
380     $handle = popen("$this->git log --since=$since", "r");
381     $msg = "";
382     $author="";
383     $timestamp=0;
384     $gitid="";
385     $record_done = false;
386     $done = false;
387     if (!$handle) {
388       echo "can't open git\n";
389       exit;
390     }
391 
392         while (($buffer = fgets($handle, 4096)) !== false) {
393 
394 
395            if(preg_match('/^([A-Z]\w+):(.*)/',$buffer, $matches)) {
396 
397                switch($matches[1]){
398 
399                  case 'Date':
400                      preg_match('/(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/',$matches[2],$date_matches);
401                      list($dstr,$mon,$day,$hour,$min,$sec,$year) = $date_matches;
402                      $timestamp = mktime ($hour,$min,$sec, $months[$mon], $day, $year);
403                      $count++ ;
404                      if($timestamp < $timestamp_start) {
405                        $done = true;
406                     }
407                     break;
408 
409                  case 'Merge':
410                    break;
411 
412                  case 'Author':
413                     $author = $matches[2];
414                     break;
415 
416                  default:
417                     break;
418                }
419 
420            }
421            elseif (preg_match('/^commit\s(.*)/',$buffer,$commit)) {
422                if($msg) {
423                      $this->insert($author,$timestamp,$gitid,$msg,$table);
424                }
425                $msg = "";
426                $gitid=$commit[1];
427 
428            }
429            else {
430             $msg .= $buffer;
431            }
432           if($done) break;
433         }
434 
435      pclose($handle);
436      $results = $this->sqlite->query("select count(*) from git_commits");
437      $end_number = $this->res2single($results);
438 
439      return array($end_number-$start_number, $end_number);
440 
441 }
442 
443     function insert($author,$timestamp,$gitid,$msg,$table) {
444 
445         $prefix =  substr( $gitid , 0, 15 );
446 
447         if($this->sqlite->query("INSERT OR IGNORE INTO $table (author,timestamp,gitid,msg,prefix,gitbranch) VALUES (?,?,?,?,?,?)",
448                      $author,$timestamp,$gitid,$msg,$prefix,$this->selected_branch)){
449               return true;
450         }
451         else {
452           return false;
453         }
454 
455     }
456 
457 
458   function select_all($q=array()) {
459         $temp_str = "";
460         $query = count($q) ? $q: $_REQUEST['dwc_query'];
461         $msg = "";
462         $author = "";
463         $branch = "";
464         $term1 = "";
465         $term2 = "";
466         $date_1 = "";
467         $date_2 = "";
468 
469         foreach($query as $col=>$val) {
470              switch ($col) {
471                 case 'author':
472                    $author = $this->construct_term('author',$val);
473                    break;
474                 case 'branch':
475                    if($val != 'any') {
476                      $branch = $this->construct_term('gitbranch',$val);
477                    }
478                    break;
479                case 'terms_1':
480                   $term1 = $this->construct_term('msg',$val);
481                   break;
482                case 'terms_2':
483                  $term2 = $this->construct_term('msg',$val);
484                  break;
485                case 'd1':
486                  $date_1 =  $this->get_timestamp($val);
487                  break;
488                case 'd2':
489                  $date_2 =  $this->get_timestamp($val);
490                  break;
491 
492              }
493         }
494 
495         $msg = $this->construct_msg_clause($term1,$term2,$query['OP_1']);
496         $ab_clause = $this->construct_ab_clause($author,$branch,$query['OP_2'],$msg);
497         $attach = ($ab_clause || $msg) ? true : false;
498         $date_clause = $this->construct_date_clause($date_1,$date_2,$attach);
499         $q = $msg . $ab_clause . $date_clause;
500         if(!$q) {
501          $this->error(6,1);
502          return array();
503         }
504         $res = $this->sqlite->query("SELECT timestamp,author,msg,gitid,gitbranch FROM git_commits WHERE $q ORDER BY timestamp DESC");
505         if(!$res) {
506           $this->error(4);
507           return false;
508         }
509 
510         $arr = $this->sqlite->res2arr($res);
511         if($arr) $q .= " [Rows:  " . count($arr) . "] ";
512         return array($arr,  $q);
513 
514   }
515 
516   function format_result_table($arr, $q=false) {
517 
518         $this->set_commit_url();
519         $query = $q ? $q: $_REQUEST['dwc_query'];
520 
521         $regex = $this->get_hilite_regex($query);
522 
523         $output = "";
524         foreach($arr as $row) {
525            $output .= $this->format_tablerow($row,$regex);
526            $output .= "<tr><td colspan='3' style='border-bottom: 1px solid black;'>\n";
527         }
528         return '<table width="85%" cellspacing="4">' . $output . '</table>';
529   }
530 
531  function format_tablerow($row,$regex) {
532         $result = "";
533 
534         $msg = "";
535         $date = "";
536         $commit = "";
537         $branch = "";
538         $author = "";
539         global $conf;
540         foreach ($row as $col=>$val) {
541 
542 
543             if($col == 'msg'){
544                 $msg = hsc($val);
545                 if($regex) {
546                     $msg = preg_replace($regex,"<span class='dwc_hilite'>$1</span>",$val);
547                 }
548 
549             }
550             elseif($col == 'timestamp') {
551                 $date = date("D M d H:i:s Y" ,$val);
552             }
553             elseif($col == 'gitid') {
554                 if($this->commit_url) {
555                   $commit = $this->format_commit_url($val);
556                 }
557                 else $commit = $val;
558             }
559             elseif($col == 'gitbranch') {
560                 $branch = $val;
561             }
562            elseif($col == 'author') {
563                 list($name,$email) = explode('<',$val);
564                 $email = trim($email,'>');
565                 $author = "<a href ='mailto: $email' title='$email'>$name</a>";
566             }
567 
568 
569         }
570 
571         return "<tr><td rowspan='2'><td nowrap style='padding-right:2px;'>$date</td><td><b>Commit:&nbsp;&nbsp;</b> $commit</td>" .
572               "<tr><td rowspan = '2' valign='top' style='padding-right:2px;'><b>Author:&nbsp;&nbsp;</b>$author<br /><b>Branch:&nbsp;&nbsp;</b>$branch</td>" .
573                "<td style='padding:2px;'>$msg</td>";
574 
575 
576   }
577 
578 
579  function get_hilite_regex($query) {
580         $term1 = $query['terms_1'];
581         $term2 = $query['terms_2'];
582 
583         $regex = "";
584 
585         if($term1 && $term2) {
586               $regex = "/($term1|$term2)/ims";
587         }
588         elseif($term1) {
589              $regex = "/($term1)/ims";
590         }
591        return $regex;
592  }
593   function format_result_plain($arr, $q=false) {
594         $this->set_commit_url();
595         $query = $q ? $q: $_REQUEST['dwc_query'];
596 
597         $regex = $this->get_hilite_regex($query);
598 
599         $output = "";
600         foreach($arr as $row) {
601            $output .= $this->format_row($row,$regex);
602            $output .= "\n---------\n";
603         }
604 
605         return '<pre>' . $output . '</pre>';
606   }
607 
608   function format_row($row,$regex) {
609         $result = "";
610 
611         foreach ($row as $col=>$val) {
612 
613 
614             if($col == 'msg'){
615                 $val = wordwrap($val, 80,"\n");
616                 $val = hsc($val);
617                 if($regex) {
618                     $val = preg_replace($regex,"<span class='dwc_hilite'>$1</span>",$val);
619                 }
620             }
621             elseif($col == 'timestamp') {
622                 $result .= "<b>Date: </b>";
623                 $val = date("D M d H:i:s Y" ,$val);
624             }
625             elseif($col == 'gitid') {
626                 if($this->commit_url) {
627                   $result .= 'Commit (URL): ';
628                   $result .= $this->format_commit_url($val);
629                   continue;
630                 }
631                 else $result .= '<b>Commit: </b>';
632             }
633             elseif($col == 'gitbranch') {
634                 $result .= '<b>Branch: </b>';
635             }
636            elseif($col == 'author') {
637                 $result .= '<b>Author: </b>';
638             }
639             else {
640                $result .= "<b>$col: </b>";
641             }
642 
643             $result .= "$val\n";
644         }
645         return $result;
646   }
647 
648 
649   function format_commit_url($val) {
650      $url = $this->commit_url . $val;
651      return "<a href='$url' target='commitwin'>$val</a>\n";
652   }
653 
654   function construct_term($col,$val) {
655          if($val) return " $col LIKE '%$val%' ";
656          return "";
657   }
658 
659   function construct_date_clause($d1,$d2, $attach) {
660      $q = "";
661      $op  = $attach ? 'AND' : "";
662      if($d1 && $d2) {
663        $q = " $op ( timestamp > $d1 AND timestamp < $d2 )";
664      }
665      elseif($d1) {
666         $q = " $op timestamp > $d1 ";
667      }
668      elseif($d2) {
669        $q = " $op timestamp < $d2 ";
670      }
671      return $q;
672 
673   }
674   function construct_ab_clause($author,$branch,$op,$msg){
675 
676         $q = "";
677         if($author) {
678             if($msg) {
679               $OP = ($op == 'AND') ? ' AND ' : ' OR ';
680               $q = " $OP $author ";
681             }
682             else $q = " $author ";
683         }
684         if($branch) {
685             if($author || $msg){
686                  $q .= " AND $branch ";
687             }
688             else
689                $q = " $branch ";
690 
691         }
692         return $q;
693 
694   }
695 
696   function construct_msg_clause($phrase1,$phrase2,$op) {
697         if(!$phrase1) return "";
698         if(!$phrase2) return $phrase1;
699         $OP = ($op == 'AND') ? ' AND ' : ' OR ';
700         return " (($phrase1) $OP ($phrase2)) ";
701   }
702 
703   function get_timestamp($dstr) {
704       if(!$dstr) return "";
705       if(preg_match('/[a-zA-Z]/',$dstr)){
706          msg('Date wasn\'t set:' . $dstr , -1);
707          return false;
708       }
709 
710       list($month,$day,$year) = explode('-',$dstr);
711       if((strlen($month) < 2) || (strlen($year) < 4)||(strlen($day) < 2)  ) {
712                 msg('Incorrect date format: ' . $dstr  , -1);
713       }
714 
715       if((strlen($month) + strlen($year) + strlen($day) > 8 )  ) {
716                 msg('Incorrect date format: ' . $dstr , -1);
717       }
718 
719       return mktime (0,0,0, $month, $day, $year);
720    }
721 
722    function res2single($res) {
723         if(method_exists($this->sqlite,res2single)){
724             return $this->sqlite->res2single($res);
725         }
726         $arr = $this->sqlite->res2row($res);
727         list($key,$val) = each($arr);
728         return($val);
729 }
730 
731 function restore_backup() {
732    $names_fname = dirname(__FILE__).'/db/dbnames.ser';
733    $backup = $names_fname . '.prev';
734    if(!file_exists($names_fname)) {
735       return "backup file not found";
736    }
737    if(file_exists($names_fname) && file_exists($backup)) {
738           @unlink($names_fname);
739   }
740   else {
741          $this->error(7);
742          return;
743   }
744   if(rename($backup,$names_fname)) {
745        return "Backup restored";
746    }
747 
748   $this->error(7);
749    return "";
750 }
751 
752 function prune($del) {
753    $names_fname = dirname(__FILE__).'/db/dbnames.ser';
754    $msg = "";
755    $meta = DOKU_INC . 'data/meta/';
756    if(file_exists($names_fname)) {
757         $inf_str = file_get_contents ($names_fname);
758         $inf = unserialize($inf_str);
759         if(!$inf) return;
760         foreach($_REQUEST[prune] as $db=>$key) {
761            unset($inf[$key]);
762            list($prefix,$index) = explode('_',$db);
763            $url = 'url' . $index;
764            $git = 'git' . $index;
765            if(isset($inf[$url])) {
766               unset($inf[$url]);
767            }
768            if(isset($inf[$git])) {
769               unset($inf[$git]);
770            }
771 
772            $sql = $meta . $db . '.sqlite';
773            if($del && file_exists($sql)) {
774              if(!unlink($sql)) {
775                 $msg .= "Could not delete $sql<br />";
776 
777              }
778            }
779         }
780 
781        $backup = $names_fname . '.prev';
782        if(file_exists($backup)) {
783            @unlink($backup);
784        }
785        if(rename($names_fname, $backup)) {
786           $msg .= "Old data saved in backup $backup";
787           if($del) {
788             $msg .= "<br />This backup may contain references to deleted sqlite dbase files<br />";
789           }
790        }
791        $this->save_dbnames_ser($names_fname,serialize($inf));
792    }
793 
794   return $msg;
795 }
796 
797 /* Read dbnames.ser and return data found there */
798 function db_data($inf = false) {
799      $output = "";
800      $filename = DW_COMMITS . 'db/dbnames.ser';
801      if(!$inf) {
802         $inf_str = file_get_contents ($filename);
803         $inf = unserialize($inf_str);
804      }
805 
806     foreach($inf as $val=>$entry) {
807        if(preg_match('/dwcommits_(\d+)/',$entry, $matches)) {
808            $output .= "<b>" . $this->getLang('db_file'). "</b> $entry";
809            $output .= '&nbsp;&nbsp;&nbsp;<input type = "checkbox" value = "'. $val. '" name="prune[' .$entry. ']">';
810            $output .= "<br />";
811            if(($url = $this->dwc_element('url', $matches[1], $inf))!== false) {
812                $output .= "<b>" . $this->getLang('remote_url'). "</b> $url<br />";
813            }
814 
815            $git = $this->dwc_element('git', $matches[1], $inf);
816            if($git !== false) {
817              if(!file_exists($git)) {
818                 $output .= "<b>". $this->getLang('git_missing'). "</b>  $git<br />";
819              }
820              else $output .= "<b>". $this->getLang('git_local'). "</b> $git<br />";
821            }
822           $output .= "<br />";
823        }
824     }
825     return $output;
826  }
827 
828  function dwc_element($prefix, $suffix, $ar) {
829     $inx = $prefix . $suffix;
830     if(isset($ar[$inx])) {
831          return $ar[$inx];
832     }
833     return false;
834 
835  }
836 
837   function recreate_table($timestamp_start) {
838 
839      $this->sqlite->query("DROP TABLE git_commits");
840      $this->sqlite->query('CREATE TABLE git_commits(author TEXT,timestamp INTEGER,gitid TEXT,msg TEXT, prefix TEXT, gitbranch TEXT, PRIMARY KEY(prefix,timestamp))');
841      $this->populate($timestamp_start);
842      $results = $this->sqlite->query("select count(*) from git_commits");
843      $res = $this->res2single($results);
844      return $res;
845 
846   }
847 
848     function write_debug($data) {
849       return;
850       global $dwc_dbg_log;
851       static $handle;
852       if(!$handle) {
853       if(!$handle = fopen($dwc_dbg_log, 'a')) {
854         return;
855         }
856       }
857        if(is_array($data)) $data = print_r($data,true);
858        fwrite($handle, "$data\n");
859     }
860 }
861 
862 
863