Set default timezone to UTC.
[viewgit.git] / index.php
blobb183c62fb2ce58b719007d0f74d4449e4977f19f
1 <?php
2 header('Content-type: text/html; charset=UTF-8');
3 /** @file
4 * The main "controller" file of ViewGit.
6 * All requests come to this file. You can think of it as the controller in the
7 * Model-View-Controller pattern. It reads config, processes user input,
8 * fetches required data using git commandline, and finally passes the data to
9 * templates to be shown to the user.
11 error_reporting(E_ALL | E_STRICT);
13 require_once('inc/config.php');
14 require_once('inc/functions.php');
15 require_once('inc/plugins.php');
17 // Include all plugins
18 foreach (glob('plugins/*/main.php') as $plugin) {
19 require_once($plugin);
21 $parts = explode('/', $plugin);
22 $name = $parts[1];
24 $classname = "${name}plugin";
25 $inst = new $classname;
28 $old_error_handler = set_error_handler('vg_error_handler');
30 // Adjust error_reporting based on config.
31 if (!$conf['debug']) {
32 error_reporting(E_ALL ^ E_NOTICE);
35 // Timezone
36 date_default_timezone_set($conf['timezone']);
38 if (isset($conf['auth_lib'])){
39 require_once("inc/auth_{$conf['auth_lib']}.php");
40 auth_check();
43 if (isset($conf['projects_glob'])) {
44 if (!isset($conf['projects_exclude'])) {
45 $conf['projects_exclude'] = array();
47 foreach ($conf['projects_glob'] as $glob) {
48 foreach (glob($glob) as $path) {
49 // Get the last part of the path before .git
50 $name = preg_replace(array('#/?\.git$#', '#^.*/#'), array('', ''), $path);
52 // Workaround against name collisions; proj, proj1, proj2, ...
53 $i = '';
54 while (in_array($name . $i, array_keys($conf['projects']))) {
55 @$i++;
57 $name = $name . $i;
58 if (!in_array($name, $conf['projects_exclude'])) {
59 $conf['projects'][$name] = array('repo' => $path);
65 $action = 'index';
66 $template = 'index';
67 $page['title'] = 'ViewGit';
69 if (isset($_REQUEST['a'])) {
70 $action = strtolower($_REQUEST['a']);
72 $page['action'] = $action;
75 * index - list of projects
77 if ($action === 'index') {
78 $template = 'index';
79 $page['title'] = 'List of projects - ViewGit';
81 foreach (array_keys($conf['projects']) as $p) {
82 $page['projects'][] = get_project_info($p);
87 * archive - send a tree as an archive to client
88 * @param p project
89 * @param h tree hash
90 * @param hb OPTIONAL base commit (trees can be part of multiple commits, this
91 * one denotes which commit the user navigated from)
92 * @param t type, "targz" or "zip"
93 * @param n OPTIONAL name suggestion
95 elseif ($action === 'archive') {
96 $project = validate_project($_REQUEST['p']);
97 $info = get_project_info($project);
98 $tree = validate_hash($_REQUEST['h']);
99 $type = $_REQUEST['t'];
100 if (isset($_REQUEST['hb'])) {
101 $hb = validate_hash($_REQUEST['hb']);
102 $describe = git_describe($project, $hb);
105 // Archive prefix
106 $archive_prefix = '';
107 if (isset($info['archive_prefix'])) {
108 $archive_prefix = "{$info['archive_prefix']}";
110 elseif (isset($conf['archive_prefix'])) {
111 $archive_prefix = "{$conf['archive_prefix']}";
113 $archive_prefix = str_replace(array('{PROJECT}', '{DESCRIBE}'), array($project, $describe), $archive_prefix);
115 // Basename
116 $basename = "$project-tree-". substr($tree, 0, 7);
117 $basename = $archive_prefix;
118 if (isset($_REQUEST['n'])) {
119 $basename = "$basename-$_REQUEST[n]-". substr($tree, 0, 6);
122 $prefix_option = '';
123 if (isset($archive_prefix)) {
124 $prefix_option = "--prefix={$archive_prefix}/";
127 if ($type === 'targz') {
128 header("Content-Type: application/x-tar-gz");
129 header("Content-Transfer-Encoding: binary");
130 header("Content-Disposition: attachment; filename=\"$basename.tar.gz\";");
131 run_git_passthru($project, "archive --format=tar $prefix_option $tree |gzip");
133 elseif ($type === 'zip') {
134 header("Content-Type: application/x-zip");
135 header("Content-Transfer-Encoding: binary");
136 header("Content-Disposition: attachment; filename=\"$basename.zip\";");
137 run_git_passthru($project, "archive --format=zip $prefix_option $tree");
139 else {
140 die('Invalid archive type requested');
143 die();
147 * blob - send a blob to browser with filename suggestion
148 * @param p project
149 * @param h blob hash
150 * @param n filename
152 elseif ($action === 'blob') {
153 $project = validate_project($_REQUEST['p']);
154 $hash = validate_hash($_REQUEST['h']);
155 $name = $_REQUEST['n'];
157 header('Content-type: application/octet-stream');
158 header("Content-Disposition: attachment; filename=$name"); // FIXME needs quotation
160 run_git_passthru($project, "cat-file blob $hash");
161 die();
165 * co - git checkout. These requests come from mod_rewrite, see the .htaccess file.
166 * @param p project
167 * @param r path
169 elseif ($action === 'co') {
170 if (!$conf['allow_checkout']) { die('Checkout not allowed'); }
172 // For debugging
173 debug("Project: $_REQUEST[p] Request: $_REQUEST[r]");
175 // eg. info/refs, HEAD
176 $p = validate_project($_REQUEST['p']); // project
177 $r = $_REQUEST['r']; // path
179 $gitdir = $conf['projects'][$p]['repo'];
180 $filename = $gitdir .'/'. $r;
182 // make sure the request is legit (no reading of other files besides those under git projects)
183 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 || preg_match('!^objects/pack/pack-[0-9a-f]{40}\.(idx|pack)$!', $r)) {
184 if (file_exists($filename)) {
185 debug('OK, sending');
186 readfile($filename);
187 } else {
188 debug('Not found');
189 header('HTTP/1.0 404 Not Found');
191 } else {
192 debug("Denied");
195 die();
199 * commit - view commit information
200 * @param p project
201 * @param h commit hash
203 elseif ($action === 'commit') {
204 $template = 'commit';
205 $page['project'] = validate_project($_REQUEST['p']);
206 $page['title'] = "$page[project] - Commit - ViewGit";
207 $page['commit_id'] = validate_hash($_REQUEST['h']);
208 $page['subtitle'] = "Commit ". substr($page['commit_id'], 0, 6);
210 $info = git_get_commit_info($page['project'], $page['commit_id']);
212 $page['author_name'] = $info['author_name'];
213 $page['author_mail'] = $info['author_mail'];
214 $page['author_datetime'] = $info['author_datetime'];
215 $page['author_datetime_local'] = $info['author_datetime_local'];
216 $page['committer_name'] = $info['committer_name'];
217 $page['committer_mail'] = $info['committer_mail'];
218 $page['committer_datetime'] = $info['committer_datetime'];
219 $page['committer_datetime_local'] = $info['committer_datetime_local'];
220 $page['tree_id'] = $info['tree'];
221 $page['parents'] = $info['parents'];
222 $page['message'] = $info['message'];
223 $page['message_firstline'] = $info['message_firstline'];
224 $page['message_full'] = $info['message_full'];
225 $page['affected_files'] = git_get_changed_paths($page['project'], $page['commit_id']);
230 * commitdiff - view diff of a commit
231 * @param p project
232 * @param h commit hash
234 elseif ($action === 'commitdiff') {
235 $template = 'commitdiff';
236 $page['project'] = validate_project($_REQUEST['p']);
237 $page['title'] = "$page[project] - Commitdiff - ViewGit";
238 $hash = validate_hash($_REQUEST['h']);
239 $page['commit_id'] = $hash;
240 $page['subtitle'] = "Commitdiff ". substr($page['commit_id'], 0, 6);
242 $info = git_get_commit_info($page['project'], $hash);
244 $page['tree_id'] = $info['tree'];
246 $page['message'] = $info['message'];
247 $page['message_firstline'] = $info['message_firstline'];
248 $page['message_full'] = $info['message_full'];
249 $page['author_name'] = $info['author_name'];
250 $page['author_mail'] = $info['author_mail'];
251 $page['author_datetime'] = $info['author_datetime'];
253 $text = fix_encoding(git_diff($page['project'], "$hash^", $hash));
254 list($page['files'], $page['diffdata']) = format_diff($text);
255 //$page['diffdata'] = format_diff($text);
258 elseif ($action === 'patch') {
259 $project = validate_project($_REQUEST['p']);
260 $hash = validate_hash($_REQUEST['h']);
261 $filename = "$project-". substr($hash, 0, 7) .".patch";
263 //header("Content-Type: text/x-diff");
264 header("Content-Type: application/octet-stream");
265 header("Content-Transfer-Encoding: binary");
266 // TODO git-style filename
267 header("Content-Disposition: attachment; filename=\"$filename\";");
269 run_git_passthru($project, "format-patch --stdout $hash^..$hash");
270 die();
274 * rss-log - RSS feed of project changes
275 * @param p project
277 elseif ($action === 'rss-log') {
278 $page['project'] = validate_project($_REQUEST['p']);
280 $ext_url = 'http://'. $_SERVER['HTTP_HOST'] . dirname($_SERVER['SCRIPT_NAME']) .'/';
282 $page['rss_title'] = "Log for $page[project]";
283 $page['rss_link'] = $ext_url . makelink(array('a' => 'summary', 'p' => $page['project']));
284 $page['rss_description'] = "Git log for project $page[project], generated by ViewGit.";
285 $page['rss_pubDate'] = rss_pubdate(time());
286 $page['rss_ttl'] = $conf['rss_ttl'];
288 $page['rss_items'] = array();
290 $diffstat = strstr($conf['rss_item_description'], '{DIFFSTAT}');
292 $revs = git_get_rev_list($page['project'], 0, $conf['rss_max_items']);
293 foreach ($revs as $rev) {
294 $info = git_get_commit_info($page['project'], $rev);
295 $link = $ext_url . makelink(array('a' => 'commit', 'p' => $page['project'], 'h' => $rev));
296 if ($diffstat) {
297 $info['diffstat'] = git_diffstat($page['project'], $rev);
300 $page['rss_items'][] = array(
301 'title' => rss_item_format($conf['rss_item_title'], $info),
302 'guid' => $link,
303 'link' => $link,
304 'description' => rss_item_format($conf['rss_item_description'], $info),
305 'pubdate' => rss_pubdate($info['author_utcstamp']),
309 require('templates/rss.php');
310 die();
314 * search - search project history
315 * @param p project
316 * @param h branch
317 * @param st search type: commit,grep,author,committer,pickaxe
318 * @param s string to search for
320 elseif ($action === 'search') {
321 $template = 'shortlog';
323 $page['project'] = validate_project($_REQUEST['p']);
325 $info = git_get_commit_info($page['project']);
326 $page['commit_id'] = $info['h'];
327 $page['tree_id'] = $info['tree'];
329 $branch = validate_hash($_REQUEST['h']);
330 $type = $_REQUEST['st'];
331 $string = $_REQUEST['s'];
333 $page['search_t'] = $type;
334 $page['search_s'] = $string;
336 $commits = git_search_commits($page['project'], $branch, $type, $string);
337 $shortlog = array();
338 foreach ($commits as $c) {
339 $info = git_get_commit_info($page['project'], $c);
340 $shortlog[] = array(
341 'author' => $info['author_name'],
342 'date' => strftime($conf['datetime'], $info['author_utcstamp']),
343 'message' => $info['message'],
344 'commit_id' => $info['h'],
345 'tree' => $info['tree'],
346 'refs' => array(),
349 $page['shortlog_no_more'] = true;
350 $page['shortlog'] = $shortlog;
354 * shortlog - project shortlog entries
355 * @param p project
356 * @param h OPTIONAL commit id to start showing log from
358 elseif ($action === 'shortlog') {
359 $template = 'shortlog';
360 $page['project'] = validate_project($_REQUEST['p']);
361 $page['title'] = "$page[project] - Shortlog - ViewGit";
362 $page['subtitle'] = "Shortlog";
363 if (isset($_REQUEST['h'])) {
364 $page['ref'] = validate_hash($_REQUEST['h']);
365 } else {
366 $page['ref'] = 'HEAD';
368 if (isset($_REQUEST['pg'])) {
369 $page['pg'] = intval($_REQUEST['pg']);
370 } else {
371 $page['pg'] = 0;
374 $info = git_get_commit_info($page['project'], $page['ref']);
375 $page['commit_id'] = $info['h'];
376 $page['tree_id'] = $info['tree'];
378 $page['shortlog'] = handle_shortlog($page['project'], $page['ref'], $page['pg']);
380 elseif ($action === 'summary') {
381 $template = 'summary';
382 $page['project'] = validate_project($_REQUEST['p']);
383 $page['title'] = "$page[project] - Summary - ViewGit";
384 $page['subtitle'] = "Summary";
386 $info = git_get_commit_info($page['project']);
387 $page['commit_id'] = $info['h'];
388 $page['tree_id'] = $info['tree'];
390 $page['shortlog'] = handle_shortlog($page['project']);
392 $page['tags'] = handle_tags($page['project'], $conf['summary_tags']);
393 $page['ref'] = 'HEAD';
395 $heads = git_get_heads($page['project']);
396 $page['heads'] = array();
397 foreach ($heads as $h) {
398 $info = git_get_commit_info($page['project'], $h['h']);
399 $page['heads'][] = array(
400 'date' => strftime($conf['datetime'], $info['author_utcstamp']),
401 'h' => $h['h'],
402 'fullname' => $h['fullname'],
403 'name' => $h['name'],
407 elseif ($action === 'tags') {
408 $template = 'tags';
409 $page['project'] = validate_project($_REQUEST['p']);
410 $page['title'] = "$page[project] - Tags - ViewGit";
412 $info = git_get_commit_info($page['project']);
413 $page['commit_id'] = $info['h'];
414 $page['tree_id'] = $info['tree'];
416 $page['tags'] = handle_tags($page['project']);
419 * Shows a tree, with list of directories/files, links to them and download
420 * links to archives.
422 * @param p project
423 * @param h tree hash
424 * @param hb OPTIONAL base commit (trees can be part of multiple commits, this
425 * one denotes which commit the user navigated from)
426 * @param f OPTIONAL path the user has followed to view this tree
428 elseif ($action === 'tree') {
429 $template = 'tree';
430 $page['project'] = validate_project($_REQUEST['p']);
431 if (isset($_REQUEST['h'])) {
432 $page['tree_id'] = validate_hash($_REQUEST['h']);
435 else {
436 // TODO walk the tree
437 $page['tree_id'] = 'HEAD';
440 $page['title'] = "$page[project] - Tree - ViewGit";
442 // 'hb' optionally contains the commit_id this tree is related to
443 if (isset($_REQUEST['hb'])) {
444 $page['commit_id'] = validate_hash($_REQUEST['hb']);
446 else {
447 // for the header
448 $info = git_get_commit_info($page['project']);
449 $page['commit_id'] = $info['h'];
452 $page['path'] = '';
453 if (isset($_REQUEST['f'])) {
454 $page['path'] = $_REQUEST['f']; // TODO validate?
457 // get path info for the header
458 $page['pathinfo'] = git_get_path_info($page['project'], $page['commit_id'], $page['path']);
459 if (!isset($page['tree_id'])) {
460 // Take the last hash from the tree
461 if (count($page['pathinfo']) > 0) {
462 $page['tree_id'] = $page['pathinfo'][count($page['pathinfo']) - 1]['hash'];
463 } else {
464 $page['tree_id'] = 'HEAD';
468 $page['subtitle'] = "Tree ". substr($page['tree_id'], 0, 6);
469 $page['entries'] = git_ls_tree($page['project'], $page['tree_id']);
472 * View a blob as inline, embedded on the page.
473 * @param p project
474 * @param h blob hash
475 * @param hb OPTIONAL base commit
477 elseif ($action === 'viewblob') {
478 $template = 'blob';
479 $page['project'] = validate_project($_REQUEST['p']);
480 $page['hash'] = validate_hash($_REQUEST['h']);
481 $page['title'] = "$page[project] - Blob - ViewGit";
482 if (isset($_REQUEST['hb'])) {
483 $page['commit_id'] = validate_hash($_REQUEST['hb']);
485 else {
486 $page['commit_id'] = 'HEAD';
488 $page['subtitle'] = "Blob ". substr($page['hash'], 0, 6);
490 $page['path'] = '';
491 if (isset($_REQUEST['f'])) {
492 $page['path'] = $_REQUEST['f']; // TODO validate?
495 // For the header's pagenav
496 $info = git_get_commit_info($page['project'], $page['commit_id']);
497 $page['commit_id'] = $info['h'];
498 $page['tree_id'] = $info['tree'];
500 $page['pathinfo'] = git_get_path_info($page['project'], $page['commit_id'], $page['path']);
502 $page['data'] = fix_encoding(join("\n", run_git($page['project'], "cat-file blob $page[hash]")));
504 $page['lastlog'] = git_get_commit_info($page['project'], 'HEAD', $page['path']);
506 // GeSHi support
507 if ($conf['geshi'] && strpos($page['path'], '.')) {
508 $old_mask = error_reporting(E_ALL ^ E_NOTICE);
509 require_once($conf['geshi_path']);
510 $parts = explode('.', $page['path']);
511 $ext = array_pop($parts);
512 $geshi = new Geshi($page['data']);
513 $lang = $geshi->get_language_name_from_extension($ext);
514 if (strlen($lang) > 0) {
515 $geshi->set_language($lang);
516 if (is_int($conf['geshi_line_numbers'])) {
517 if ($conf['geshi_line_numbers'] == 0) {
518 $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
520 else {
521 $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, $conf['geshi_line_numbers']);
524 $page['html_data'] = $geshi->parse_code();
526 error_reporting($old_mask);
529 elseif (in_array($action, array_keys(VGPlugin::$plugin_actions))) {
530 VGPlugin::$plugin_actions[$action]->action($action);
531 die();
533 else {
534 die('Invalid action');
537 require 'templates/header.php';
538 require "templates/$template.php";
539 require 'templates/footer.php';