Rev Author Line No. Line
185 miho 1 <?php
2 // WebSVN - Subversion repository viewing via the web using PHP
4988 kaklik 3 // Copyright (C) 2004-2006 Tim Armes
185 miho 4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
4988 kaklik 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
185 miho 13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
4988 kaklik 17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
185 miho 18 //
19 // --
20 //
21 // blame.php
22 //
23 // Show the blame information of a file.
24 //
25  
4988 kaklik 26 require_once 'include/setup.php';
27 require_once 'include/svnlook.php';
28 require_once 'include/utils.php';
29 require_once 'include/template.php';
185 miho 30  
31 $vars['action'] = $lang['BLAME'];
32  
4988 kaklik 33 // Make sure that we have a repository
34 if (!$rep)
35 {
36 renderTemplate404('blame','NOREP');
37 }
38  
185 miho 39 $svnrep = new SVNRepository($rep);
40  
41 // If there's no revision info, go to the lastest revision for this path
4988 kaklik 42 $history = $svnrep->getLog($path, 'HEAD', 1, false, 2, ($path == '/') ? '' : $peg);
185 miho 43  
4988 kaklik 44 if (!$history)
45 {
46 unset($vars['error']);
47 $history = $svnrep->getLog($path, '', '', false, 2, ($path == '/') ? '' : $peg);
185 miho 48  
4988 kaklik 49 if (!$history)
50 {
51 renderTemplate404('blame','NOPATH');
52 }
53 }
54 $youngest = ($history && isset($history->entries[0])) ? $history->entries[0]->rev : false;
185 miho 55  
4988 kaklik 56 if (empty($rev))
57 {
58 $rev = $youngest;
59 }
60 else
61 {
62 $history = $svnrep->getLog($path, $rev, '', false, 2, $peg);
63 if (!$history)
64 {
65 renderTemplate404('blame','NOPATH');
66 }
67 }
68  
69 if ($path[0] != '/')
70 {
71 $ppath = '/'.$path;
72 }
73 else
74 {
75 $ppath = $path;
76 }
77  
185 miho 78 // Find the parent path (or the whole path if it's already a directory)
79 $pos = strrpos($ppath, '/');
80 $parent = substr($ppath, 0, $pos + 1);
81  
82 $vars['rev'] = $rev;
4988 kaklik 83 $vars['peg'] = $peg;
84 $vars['path'] = str_replace('%2F', '/', rawurlencode($ppath));
85 $vars['safepath'] = escape($ppath);
185 miho 86  
4988 kaklik 87 if (isset($history->entries[0]))
88 {
89 $vars['log'] = xml_entities($history->entries[0]->msg);
90 $vars['date'] = $history->entries[0]->date;
91 $vars['age'] = datetimeFormatDuration(time() - strtotime($history->entries[0]->date));
92 $vars['author'] = $history->entries[0]->author;
93 }
185 miho 94  
4988 kaklik 95 createPathLinks($rep, $ppath, $passrev, $peg);
96 $passRevString = createRevAndPegString($rev, $peg);
185 miho 97  
4988 kaklik 98 if ($rev != $youngest)
99 {
100 $vars['goyoungesturl'] = $config->getURL($rep, $path, 'blame').createRevAndPegString('', $peg);
101 $vars['goyoungestlink'] = '<a href="'.$vars['goyoungesturl'].'"'.($youngest ? ' title="'.$lang['REV'].' '.$youngest.'"' : '').'>'.$lang['GOYOUNGEST'].'</a>';
102 }
185 miho 103  
4988 kaklik 104 $revurl = $config->getURL($rep, $path, 'blame');
185 miho 105  
4988 kaklik 106 if ($rev < $youngest)
185 miho 107 {
4988 kaklik 108 $history2 = $svnrep->getLog($path, $rev, $youngest, true, 2, $peg);
185 miho 109  
4988 kaklik 110 if (isset($history2->entries[1]))
111 {
112 $nextRev = $history2->entries[1]->rev;
113 if ($nextRev != $youngest)
114 {
115 $vars['nextrev'] = $nextRev;
116 $vars['nextrevurl'] = $revurl.createRevAndPegString($nextRev, $peg);
117 }
118 }
185 miho 119  
4988 kaklik 120 unset($vars['error']);
121 }
185 miho 122  
4988 kaklik 123 if (isset($history->entries[1]))
124 {
125 $prevRev = $history->entries[1]->rev;
126 $prevPath = $history->entries[1]->path;
127 $vars['prevrev'] = $prevRev;
128 $vars['prevrevurl'] = $revurl.createRevAndPegString($prevRev, $peg);
185 miho 129 }
130  
4988 kaklik 131 $vars['revurl'] = $config->getURL($rep, $path, 'revision').$passRevString;
132 $vars['revlink'] = '<a href="'.$vars['revurl'].'">'.$lang['LASTMOD'].'</a>';
185 miho 133  
4988 kaklik 134 $vars['logurl'] = $config->getURL($rep, $path, 'log').$passRevString;
135 $vars['loglink'] = '<a href="'.$vars['logurl'].'">'.$lang['VIEWLOG'].'</a>';
185 miho 136  
4988 kaklik 137 $vars['filedetailurl'] = $config->getURL($rep, $path, 'file').$passRevString;
138 $vars['filedetaillink'] = '<a href="'.$vars['filedetailurl'].'">'.$lang['FILEDETAIL'].'</a>';
185 miho 139  
4988 kaklik 140 if ($history == null || count($history->entries) > 1)
141 {
142 $vars['diffurl'] = $config->getURL($rep, $path, 'diff').$passRevString;
143 $vars['difflink'] = '<a href="'.$vars['diffurl'].'">'.$lang['DIFFPREV'].'</a>';
144 }
145  
146 if ($rep->isRssEnabled())
147 {
148 $vars['rssurl'] = $config->getURL($rep, $path, 'rss').createRevAndPegString('', $peg);
149 $vars['rsslink'] = '<a href="'.$vars['rssurl'].'">'.$lang['RSSFEED'].'</a>';
150 }
151  
152 // Check for binary file type before grabbing blame information.
153 $svnMimeType = $svnrep->getProperty($path, 'svn:mime-type', $rev, $peg);
154  
155 if (!$rep->getIgnoreSvnMimeTypes() && preg_match('~application/*~', $svnMimeType))
156 {
157 $vars['warning'] = 'Cannot display blame info for binary file. (svn:mime-type = '.$svnMimeType.')';
158 $vars['javascript'] = '';
159 }
160 else
161 {
162 // Get the contents of the file
163 $tfname = tempnamWithCheck($config->getTempDir(), '');
164 $highlighted = $svnrep->getFileContents($path, $tfname, $rev, $peg, '', 'line');
165  
166 if ($file = fopen($tfname, 'r'))
167 {
168 // Get the blame info
169 $tbname = tempnamWithCheck($config->getTempDir(), '');
170  
171 $svnrep->getBlameDetails($path, $tbname, $rev, $peg);
172  
173 if ($blame = fopen($tbname, 'r'))
174 {
175 // Create an array of version/author/line
176  
177 $index = 0;
178 $seen_rev = array();
179 $last_rev = '';
180 $row_class = '';
181  
182 while (!feof($blame) && !feof($file))
183 {
184 $blameline = rtrim(fgets($blame), "\n\r");
185  
186 if ($blameline != '')
187 {
188 list($revision, $author, $remainder) = sscanf($blameline, '%d %s %s');
189 $empty = !$remainder;
190  
191 $listvar = &$listing[$index];
192 $listvar['lineno'] = $index + 1;
193  
194 if ($last_rev != $revision)
195 {
196 $listvar['revision'] = '<a id="l'.$index.'-rev" class="blame-revision" href="'.$config->getURL($rep, $path, 'blame').createRevAndPegString($revision, $peg ? $peg : $rev).'">'.$revision.'</a>';
197 $seen_rev[$revision] = 1;
198 $row_class = ($row_class == 'light') ? 'dark' : 'light';
199 $listvar['author'] = $author;
200 }
201 else
202 {
203 $listvar['revision'] = '';
204 $listvar['author'] = '';
205 }
206  
207 $listvar['row_class'] = $row_class;
208 $last_rev = $revision;
209  
210 $line = rtrim(fgets($file), "\n\r");
211 if (!$highlighted)
212 {
213 $line = escape(toOutputEncoding($line));
214 }
215 $listvar['line'] = ($empty) ? '&nbsp;' : wrapInCodeTagIfNecessary($line);
216 $index++;
217 }
218 }
219 fclose($blame);
220 }
221  
222 fclose($file);
223 @unlink($tbname);
224 }
225 @unlink($tfname);
226  
227 // Build the necessary JavaScript as an array of lines, then join them with \n
228 $javascript = array();
229 $javascript[] = '<script type="text/javascript" src="'.$locwebsvnhttp.'/javascript/blame-popup.js"></script>';
230 $javascript[] = '<script type="text/javascript">';
231 $javascript[] = '/* <![CDATA[ */';
232 $javascript[] = 'var rev = new Array();';
233  
234 ksort($seen_rev); // Sort revisions in descending order by key
235 if (empty($peg))
236 {
237 $peg = $rev;
238 }
239 if (!isset($vars['warning']))
240 {
241 foreach ($seen_rev as $key => $val)
242 {
243 $history = $svnrep->getLog($path, $key, $key, false, 1, $peg);
244  
245 if ($history && $history->curEntry)
246 {
247 $javascript[] = 'rev['.$key.'] = \'<div class="date">'.$history->curEntry->date.'</div><div class="msg">'.addslashes(preg_replace('/\n/', ' ', $history->curEntry->msg)).'</div>\';';
248 }
249 }
250 }
251  
252 $javascript[] = '/* ]]> */';
253 $javascript[] = '</script>';
254 $vars['javascript'] = implode("\n", $javascript);
255 }
256  
257 if (!$rep->hasReadAccess($path, false))
258 {
259 $vars['error'] = $lang['NOACCESS'];
260 sendHeaderForbidden();
261 }
262  
263 renderTemplate('blame');