MTrackWeb/Changeset.php
[web.mtrack] / MTrackWeb / Changeset.php
1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
3
4 // Browse.php - only for rendering the body..
5 // Tree.php - the actually tree..
6
7 require_once 'MTrackWeb.php';
8
9 class MTrackWeb_Changeset extends MTrackWeb
10 {
11     var $template = 'changeset.html'; 
12  
13  
14      
15     function getAuth() 
16     {
17         parent::getAuth();
18         
19         MTrackACL::requireAllRights('Browser', 'read');
20         return true;
21   
22     }
23  
24     function get($pi)
25     {
26         $this->pi = empty($pi) ? '' : ($pi . $this->bootLoader->ext);
27        
28         $this->repo = DB_DataObject::factory('repos');
29         $cs = $this->repo->loadFromPath($this->pi);
30         
31        
32         if (!$this->repo->repoid) {
33             return HTML_FlexyFramework::run('Browse');  
34         };
35         
36         
37         MTrackACL::requireAllRights("repo:{$this->repo->repoid}", 'read');
38
39         //$data = mtrack_cache('get_change_data', array($path), 864000);
40         $this->data = $this->get_change_data($cs);
41         
42         if (!empty($_REQUEST['part'])) {
43             foreach($this->data->ent->files as $f) {
44                 if ($f->name != $_REQUEST['part']) {
45                     continue;
46                     
47                 }
48                 echo $this->diff($this->repo->diff($f, $this->data->ent->rev));
49                 exit;
50             }
51             die("can not find part?");
52         }
53         
54         
55         $this->ent = $this->data->ent;
56         if ($this->ent === null) {
57           throw new Exception("invalid parameters");
58         }
59
60         //$rdata = mtrack_cache('get_change_data_relatives', array($path, $ent->rev));
61         $rdata = $this->get_change_data_relatives($cs, $this->ent->rev);
62         
63        // print_R($rdata);exit;
64         if (isset($_GET['fmt']) && $_GET['fmt'] == 'diff') {
65             $this->downloadDiff();
66         }
67         $this->title = "Changeset " . $this->ent->rev;
68    
69  
70
71
72         
73     }
74     function downloadDiff()
75     {
76         $filename = "$this->repo->shortname.$this->ent->rev.diff";
77         header("Content-Type: text/plain; name=\"$filename\"");
78         header("Content-Disposition: attachment; filename=\"$filename\"");
79
80         echo "Changeset: $repo->shortname $ent->rev\n";
81         echo "By: $ent->changeby\n";
82         echo "When: $ent->ctime\n";
83         echo "\n";
84         echo $data->changelog . "\n\n";
85
86         if (is_array($ent->files) && count($ent->files)) {
87           foreach ($ent->files as $id => $file) {
88             echo "$file->status $file->name\n";
89           }
90           echo "\n";
91
92           foreach ($ent->files as $id => $file) {
93             $fpath = $file->name;
94             if ($fpath[0] != '/') $fpath = '/' . $fpath;
95             $diff = $repo->diff($file, $ent->rev);
96             if (is_resource($diff)) {
97               echo stream_get_contents($diff);
98             } elseif (is_array($diff)) {
99               echo join("\n", $diff);
100             } else {
101               echo $diff;
102             }
103           }
104         }
105         exit;
106     }
107
108     function get_change_data($pi)
109     {
110         $ents = $this->repo->history(null, 1, 'rev', $pi);
111         $data = new stdclass;
112         if (!count($ents)) {
113             $data->ent = null;
114         } else {
115             $ent = $ents[0];
116             $data->ent = $ent;
117
118         // Determine project from the file list
119         $the_proj = $this->repo->projectFromPath($ent->files);
120         if ($the_proj > 1) {
121             $proj = MTrackProject::loadById($the_proj);
122             $changelog = $proj->adjust_links($ent->changelog, true);
123         } else {
124             $changelog = $ent->changelog;
125         }
126         $data->changelog = $changelog;
127         $ent->files = is_array($ent->files) ? array_values($ent->files) : array();
128         
129         // we should not do this here..
130         // it's to slow on huge commits..
131         
132         //foreach ($ent->files as $file) {
133         //    $file->diff = $this->diff($this->repo->diff($file, $ent->rev));
134         //}
135       }
136
137       return $data;
138     }
139
140     function get_change_data_relatives($pi, $rev)
141     {
142       
143       $data = new stdclass;
144       list($data->parents, $data->kids) = $this->repo->getRelatedChanges($rev);
145       return $data;
146     }
147  
148
149
150     function diff($diffstr) // fixme... code should be in template..
151     {
152         $nlines = 0;
153     
154         if (is_resource($diffstr)) {
155             $lines = array();
156             while (($line = fgets($diffstr)) !== false) {
157                 $lines[] = rtrim($line, "\r\n");
158             }
159             $diffstr = $lines;
160         }
161
162         if (is_string($diffstr)) {
163             $abase = md5($diffstr);
164             $diffstr = preg_split("/\r?\n/", $diffstr);
165         } else {
166             $abase = md5(join("\n", $diffstr));
167         }
168         
169         // diffstr should now contain a string...
170
171         /* we could use toggle() below, but it is much faster to determine
172          * if we are hiding or showing based on a single variable than evaluating
173          * that for each possible cell */
174         $html = "
175             <button class='togglediffcopy' type='button'>Toggle Diff Line Numbers</button>
176             <table class='code diff'>
177         ";
178         //$html = "<pre class='code diff'>";
179
180         while (true) {
181             if (!count($diffstr)) {
182                 break;
183             }
184             $line = array_shift($diffstr);
185             $nlines++;
186             if (!strncmp($line, '@@ ', 3)) {
187                 /* done with preamble */
188                 break;
189             }
190             $line = htmlspecialchars($line, ENT_QUOTES, 'utf-8');
191             $line = "<tr class='meta'><td class='lineno'></td><td class='lineno'></td><td class='lineno'></td><td width='100%'>$line</tr>";
192             $html .= $line . "\n";
193         }
194
195         $lines = array(0, 0);
196         $first = false;
197         while (true) {
198             $class = 'unmod';
199
200             if (preg_match("/^@@\s+-(\pN+)(?:,\pN+)?\s+\+(\pN+)(?:,\pN+)?\s*@@/",
201                     $line, $M)) {
202                 $lines[0] = (int)$M[1] - 1;
203                 $lines[1] = (int)$M[2] - 1;
204                 $class = 'meta';
205                 $first = true;
206             } elseif (strlen($line)) {
207                 if ($line[0] == '-') {
208                     $lines[0]++;
209                     $class = 'removed';
210                 } elseif ($line[0] == '+') {
211                     $lines[1]++;
212                     $class = 'added';
213                 } else {
214                     $lines[0]++;
215                     $lines[1]++;
216                 }
217             } else {
218                 $lines[0]++;
219                 $lines[1]++;
220             }
221             $row = "<tr class='$class";
222             if ($first) {
223                 $row .= ' first';
224             }
225             if ($class != 'meta' && $first) {
226                 $first = false;
227             }
228             $row .= "'>";
229
230             switch ($class) {
231                 case 'meta':
232                     $line_info = '';
233                     $row .= "<td class='lineno'></td><td class='lineno'></td>";
234                     break;
235                 case 'added':
236                     $row .= "<td class='lineno'></td><td class='lineno'>" . $lines[1] . "</td>";
237                     break;
238                 case 'removed':
239                     $row .= "<td class='lineno'>" . $lines[0] . "</td><td class='lineno'></td>";
240                     break;
241                 default:
242                     $row .= "<td class='lineno'>" . $lines[0] . "</td><td class='lineno'>" . $lines[1] . "</td>";
243             }
244             $anchor = $abase . '.' . $nlines;
245             $row .= "<td class='linelink'><a name='$anchor'></a><a href='#$anchor' title='link to this line'>#</a></td>";
246
247             $line = htmlspecialchars($line, ENT_QUOTES, 'utf-8');
248             $row .= "<td class='line' width='100%'>$line</td></tr>\n";
249             $html .= $row;
250
251             if (!count($diffstr)) {
252                 break;
253             }
254             $line = array_shift($diffstr);
255             $nlines++;
256         }
257
258         if ($nlines == 0) {
259             return null;
260         }
261
262         $html .= "</table>";
263         return $html;
264     }
265 }