After GeSHi, restore the original error mask, not E_ALL.
[viewgit.git] / index.php
blob0b6a7dd343a0a0009b40909f252a4ed96592b23f
1 <?php
2 /** @file
3 * The main "controller" file of ViewGit.
5 * All requests come to this file. You can think of it as the controller in the
6 * Model-View-Controller pattern. It reads config, processes user input,
7 * fetches required data using git commandline, and finally passes the data to
8 * templates to be shown to the user.
9 */
10 // TODO include E_STRICT?
11 error_reporting(E_ALL);
13 require_once('inc/config.php');
14 require_once('inc/functions.php');
16 $old_error_handler = set_error_handler('vg_error_handler');
18 // Adjust error_reporting based on config.
19 if (!$conf['debug']) {
20 error_reporting(E_ALL ^ E_NOTICE);
23 if (isset($conf['auth_lib'])){
24 require_once("inc/auth_{$conf['auth_lib']}.php");
25 auth_check();
28 if (isset($conf['projects_glob'])) {
29 foreach ($conf['projects_glob'] as $glob) {
30 foreach (glob($glob) as $path) {
31 // Get the last part of the path before .git
32 $name = preg_replace(array('#/?\.git$#', '#^.*/#'), array('', ''), $path);
34 // Workaround against name collisions; proj, proj1, proj2, ...
35 $i = '';
36 while (in_array($name . $i, array_keys($conf['projects']))) {
37 @$i++;
39 $name = $name . $i;
40 $conf['projects'][$name] = array('repo' => $path);
45 $action = 'index';
46 $template = 'index';
47 $page['title'] = 'ViewGit';
49 if (isset($_REQUEST['a'])) {
50 $action = strtolower($_REQUEST['a']);
52 $page['action'] = $action;
55 * index - list of projects
57 if ($action === 'index') {
58 $template = 'index';
59 $page['title'] = 'List of projects - ViewGit';
61 foreach (array_keys($conf['projects']) as $p) {
62 $page['projects'][] = get_project_info($p);
67 * archive - send a tree as an archive to client
68 * @param p project
69 * @param h tree hash
70 * @param t type, "targz" or "zip"
71 * @param n OPTIONAL name suggestion
73 elseif ($action === 'archive') {
74 $project = validate_project($_REQUEST['p']);
75 $tree = validate_hash($_REQUEST['h']);
76 $type = $_REQUEST['t'];
78 $basename = "$project-tree-". substr($tree, 0, 7);
79 if (isset($_REQUEST['n'])) {
80 $basename = "$project-$_REQUEST[n]-". substr($tree, 0, 6);
83 if ($type === 'targz') {
84 header("Content-Type: application/x-tar-gz");
85 header("Content-Transfer-Encoding: binary");
86 header("Content-Disposition: attachment; filename=\"$basename.tar.gz\";");
87 run_git_passthru($project, "archive --format=tar $tree |gzip");
89 elseif ($type === 'zip') {
90 header("Content-Type: application/x-zip");
91 header("Content-Transfer-Encoding: binary");
92 header("Content-Disposition: attachment; filename=\"$basename.zip\";");
93 run_git_passthru($project, "archive --format=zip $tree");
95 else {
96 die('Invalid archive type requested');
99 die();
103 * blob - send a blob to browser with filename suggestion
104 * @param p project
105 * @param h blob hash
106 * @param n filename
108 elseif ($action === 'blob') {
109 $project = validate_project($_REQUEST['p']);
110 $hash = validate_hash($_REQUEST['h']);
111 $name = $_REQUEST['n'];
113 header('Content-type: application/octet-stream');
114 header("Content-Disposition: attachment; filename=$name"); // FIXME needs quotation
116 run_git_passthru($project, "cat-file blob $hash");
117 die();
121 * co - git checkout. These requests come from mod_rewrite, see the .htaccess file.
122 * @param p project
123 * @param r path
125 elseif ($action === 'co') {
126 if (!$conf['allow_checkout']) { die('Checkout not allowed'); }
128 // For debugging
129 debug("Project: $_REQUEST[p] Request: $_REQUEST[r]");
131 // eg. info/refs, HEAD
132 $p = validate_project($_REQUEST['p']); // project
133 $r = $_REQUEST['r']; // path
135 $gitdir = $conf['projects'][$p]['repo'];
136 $filename = $gitdir .'/'. $r;
138 // make sure the request is legit (no reading of other files besides those under git projects)
139 if ($r === 'HEAD' || $r === 'info/refs' || preg_match('!^objects/info/(packs|http-alternates|alternates)$!', $r) > 0 || preg_match('!^objects/[0-9a-f]{2}/[0-9a-f]{38}$!', $r) > 0) {
140 if (file_exists($filename)) {
141 debug('OK, sending');
142 readfile($filename);
143 } else {
144 debug('Not found');
145 header('HTTP/1.0 404 Not Found');
147 } else {
148 debug("Denied");
151 die();
155 * commit - view commit information
156 * @param p project
157 * @param h commit hash
159 elseif ($action === 'commit') {
160 $template = 'commit';
161 $page['project'] = validate_project($_REQUEST['p']);
162 $page['title'] = "$page[project] - Commit - ViewGit";
163 $page['commit_id'] = validate_hash($_REQUEST['h']);
165 $info = git_get_commit_info($page['project'], $page['commit_id']);
167 $page['author_name'] = $info['author_name'];
168 $page['author_mail'] = $info['author_mail'];
169 $page['author_datetime'] = strftime($conf['datetime_full'], $info['author_utcstamp']);
170 $page['author_datetime_local'] = strftime($conf['datetime_full'], $info['author_stamp']) .' '. $info['author_timezone'];
171 $page['committer_name'] = $info['committer_name'];
172 $page['committer_mail'] = $info['committer_mail'];
173 $page['committer_datetime'] = strftime($conf['datetime_full'], $info['committer_utcstamp']);
174 $page['committer_datetime_local'] = strftime($conf['datetime_full'], $info['committer_stamp']) .' '. $info['committer_timezone'];
175 $page['tree_id'] = $info['tree'];
176 $page['parents'] = $info['parents'];
177 $page['message'] = $info['message'];
178 $page['message_firstline'] = $info['message_firstline'];
179 $page['message_full'] = $info['message_full'];
184 * commitdiff - view diff of a commit
185 * @param p project
186 * @param h commit hash
188 elseif ($action === 'commitdiff') {
189 $template = 'commitdiff';
190 $page['project'] = validate_project($_REQUEST['p']);
191 $page['title'] = "$page[project] - Commitdiff - ViewGit";
192 $hash = validate_hash($_REQUEST['h']);
193 $page['commit_id'] = $hash;
195 $info = git_get_commit_info($page['project'], $hash);
197 $page['tree_id'] = $info['tree'];
199 $page['message'] = $info['message'];
200 $page['message_firstline'] = $info['message_firstline'];
201 $page['message_full'] = $info['message_full'];
202 $page['author_name'] = $info['author_name'];
203 $page['author_mail'] = $info['author_mail'];
204 $page['author_datetime'] = strftime($conf['datetime'], $info['author_utcstamp']);
206 $text = git_diff($page['project'], "$hash^", $hash);
207 list($page['files'], $page['diffdata']) = format_diff($text);
208 //$page['diffdata'] = format_diff($text);
211 elseif ($action === 'patch') {
212 $project = validate_project($_REQUEST['p']);
213 $hash = validate_hash($_REQUEST['h']);
214 $filename = "$project-". substr($hash, 0, 7) .".patch";
216 //header("Content-Type: text/x-diff");
217 header("Content-Type: application/octet-stream");
218 header("Content-Transfer-Encoding: binary");
219 // TODO git-style filename
220 header("Content-Disposition: attachment; filename=\"$filename\";");
222 run_git_passthru($project, "format-patch --stdout $hash^..$hash");
223 die();
227 * rss-log - RSS feed of project changes
228 * @param p project
230 elseif ($action === 'rss-log') {
231 $page['project'] = validate_project($_REQUEST['p']);
233 $ext_url = 'http://'. $_SERVER['HTTP_HOST'] . dirname($_SERVER['SCRIPT_NAME']) .'/';
235 $page['rss_title'] = "Log for $page[project]";
236 $page['rss_link'] = $ext_url . makelink(array('a' => 'summary', 'p' => $page['project']));
237 $page['rss_description'] = "Git log for project $page[project], generated by ViewGit.";
238 $page['rss_pubDate'] = rss_pubdate(time());
239 $page['rss_ttl'] = $conf['rss_ttl'];
241 $page['rss_items'] = array();
243 $diffstat = strstr($conf['rss_item_description'], '{DIFFSTAT}');
245 $revs = git_get_rev_list($page['project'], $conf['rss_max_items']);
246 foreach ($revs as $rev) {
247 $info = git_get_commit_info($page['project'], $rev);
248 $link = $ext_url . makelink(array('a' => 'commit', 'p' => $page['project'], 'h' => $rev));
249 if ($diffstat) {
250 $info['diffstat'] = git_diffstat($page['project'], $rev);
253 $page['rss_items'][] = array(
254 'title' => rss_item_format($conf['rss_item_title'], $info),
255 'guid' => $link,
256 'link' => $link,
257 'description' => rss_item_format($conf['rss_item_description'], $info),
258 'pubdate' => rss_pubdate($info['author_stamp']),
262 require('templates/rss.php');
263 die();
267 * search - search project history
268 * @param p project
269 * @param st search type: commit,grep,author,committer,pickaxe
270 * @param s string to search for
272 elseif ($action === 'search') {
273 $template = 'shortlog';
275 $page['project'] = validate_project($_REQUEST['p']);
277 $info = git_get_commit_info($page['project']);
278 $page['commit_id'] = $info['h'];
279 $page['tree_id'] = $info['tree'];
281 $type = $_REQUEST['st'];
282 $string = $_REQUEST['s'];
284 $page['search_t'] = $type;
285 $page['search_s'] = $string;
287 $commits = git_search_commits($page['project'], $type, $string);
288 $shortlog = array();
289 foreach ($commits as $c) {
290 $info = git_get_commit_info($page['project'], $c);
291 $shortlog[] = array(
292 'author' => $info['author_name'],
293 'date' => strftime($conf['datetime'], $info['author_utcstamp']),
294 'message' => $info['message'],
295 'commit_id' => $info['h'],
296 'tree' => $info['tree'],
297 'refs' => array(),
300 $page['shortlog'] = $shortlog;
304 * shortlog - project shortlog entries
305 * @param p project
306 * @param h OPTIONAL commit id to start showing log from
308 elseif ($action === 'shortlog') {
309 $template = 'shortlog';
310 $page['project'] = validate_project($_REQUEST['p']);
311 $page['title'] = "$page[project] - Shortlog - ViewGit";
312 if (isset($_REQUEST['h'])) {
313 $page['ref'] = validate_hash($_REQUEST['h']);
314 } else {
315 $page['ref'] = 'HEAD';
318 $info = git_get_commit_info($page['project'], $page['ref']);
319 $page['commit_id'] = $info['h'];
320 $page['tree_id'] = $info['tree'];
322 $page['shortlog'] = handle_shortlog($page['project'], $page['ref']);
324 elseif ($action === 'summary') {
325 $template = 'summary';
326 $page['project'] = validate_project($_REQUEST['p']);
327 $page['title'] = "$page[project] - Summary - ViewGit";
329 $info = git_get_commit_info($page['project']);
330 $page['commit_id'] = $info['h'];
331 $page['tree_id'] = $info['tree'];
333 $page['shortlog'] = handle_shortlog($page['project']);
335 $page['tags'] = handle_tags($page['project'], $conf['summary_tags']);
337 $heads = git_get_heads($page['project']);
338 $page['heads'] = array();
339 foreach ($heads as $h) {
340 $info = git_get_commit_info($page['project'], $h['h']);
341 $page['heads'][] = array(
342 'date' => strftime($conf['datetime'], $info['author_utcstamp']),
343 'h' => $h['h'],
344 'fullname' => $h['fullname'],
345 'name' => $h['name'],
349 elseif ($action === 'tags') {
350 $template = 'tags';
351 $page['project'] = validate_project($_REQUEST['p']);
352 $page['title'] = "$page[project] - Tags - ViewGit";
354 $info = git_get_commit_info($page['project']);
355 $page['commit_id'] = $info['h'];
356 $page['tree_id'] = $info['tree'];
358 $page['tags'] = handle_tags($page['project']);
361 * Shows a tree, with list of directories/files, links to them and download
362 * links to archives.
364 * @param p project
365 * @param h tree hash
366 * @param hb OPTIONAL base commit (trees can be part of multiple commits, this
367 * one denotes which commit the user navigated from)
368 * @param f OPTIONAL path the user has followed to view this tree
370 elseif ($action === 'tree') {
371 $template = 'tree';
372 $page['project'] = validate_project($_REQUEST['p']);
373 if (isset($_REQUEST['h'])) {
374 $page['tree_id'] = validate_hash($_REQUEST['h']);
377 else {
378 // TODO walk the tree
379 $page['tree_id'] = 'HEAD';
382 $page['title'] = "$page[project] - Tree - ViewGit";
384 // 'hb' optionally contains the commit_id this tree is related to
385 if (isset($_REQUEST['hb'])) {
386 $page['commit_id'] = validate_hash($_REQUEST['hb']);
388 else {
389 // for the header
390 $info = git_get_commit_info($page['project']);
391 $page['commit_id'] = $info['h'];
394 $page['path'] = '';
395 if (isset($_REQUEST['f'])) {
396 $page['path'] = $_REQUEST['f']; // TODO validate?
399 // get path info for the header
400 $page['pathinfo'] = git_get_path_info($page['project'], $page['commit_id'], $page['path']);
401 if (!isset($page['tree_id'])) {
402 // Take the last hash from the tree
403 if (count($page['pathinfo']) > 0) {
404 $page['tree_id'] = $page['pathinfo'][count($page['pathinfo']) - 1]['hash'];
405 } else {
406 $page['tree_id'] = 'HEAD';
410 $page['entries'] = git_ls_tree($page['project'], $page['tree_id']);
413 * View a blob as inline, embedded on the page.
414 * @param p project
415 * @param h blob hash
416 * @param hb OPTIONAL base commit
418 elseif ($action === 'viewblob') {
419 $template = 'blob';
420 $page['project'] = validate_project($_REQUEST['p']);
421 $page['hash'] = validate_hash($_REQUEST['h']);
422 $page['title'] = "$page[project] - Blob - ViewGit";
423 if (isset($_REQUEST['hb'])) {
424 $page['commit_id'] = validate_hash($_REQUEST['hb']);
426 else {
427 $page['commit_id'] = 'HEAD';
430 $page['path'] = '';
431 if (isset($_REQUEST['f'])) {
432 $page['path'] = $_REQUEST['f']; // TODO validate?
435 // For the header's pagenav
436 $info = git_get_commit_info($page['project'], $page['commit_id']);
437 $page['commit_id'] = $info['h'];
438 $page['tree_id'] = $info['tree'];
440 $page['pathinfo'] = git_get_path_info($page['project'], $page['commit_id'], $page['path']);
442 $page['data'] = join("\n", run_git($page['project'], "cat-file blob $page[hash]"));
444 // GeSHi support
445 if ($conf['geshi'] && strpos($page['path'], '.')) {
446 $old_mask = error_reporting(E_ALL ^ E_NOTICE);
447 require_once($conf['geshi_path']);
448 $ext = array_pop(explode('.', $page['path']));
449 $lang = Geshi::get_language_name_from_extension($ext);
450 if (strlen($lang) > 0) {
451 $geshi =& new Geshi($page['data'], $lang);
452 if (is_int($conf['geshi_line_numbers'])) {
453 if ($conf['geshi_line_numbers'] == 0) {
454 $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
456 else {
457 $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, $conf['geshi_line_numbers']);
460 $page['html_data'] = $geshi->parse_code();
462 error_reporting($old_mask);
465 else {
466 die('Invalid action');
469 require 'templates/header.php';
470 require "templates/$template.php";
471 require 'templates/footer.php';