project, $this->getScmType($request)); $scm = IDF_Scm::get($request); $branches = $scm->getBranches(); $commit = $match[2]; if ('commit' != $scm->testHash($commit)) { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::changeLog', array($request->project->shortname, $branches[0])); return new Pluf_HTTP_Response_Redirect($url); } $res = new Pluf_Template_ContextVars($scm->getChangeLog($commit, 25)); $scmConf = $request->conf->getVal('scm', 'git'); return Pluf_Shortcuts_RenderToResponse('source/changelog.html', array( 'page_title' => $title, 'title' => $title, 'changes' => $res, 'commit' => $commit, 'branches' => $branches, 'scm' => $scmConf, ), $request); } public $treeBase_precond = array('IDF_Precondition::accessSource'); public function treeBase($request, $match) { $title = sprintf(__('%1$s %2$s Source Tree'), (string) $request->project, $this->getScmType($request)); $scm = IDF_Scm::get($request); $commit = $match[2]; $branches = $scm->getBranches(); if ('commit' != $scm->testHash($commit)) { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', array($request->project->shortname, $branches[0])); return new Pluf_HTTP_Response_Redirect($url); } $res = new Pluf_Template_ContextVars($scm->filesAtCommit($commit)); $cobject = $scm->getCommit($commit); $tree_in = in_array($commit, $branches); $scmConf = $request->conf->getVal('scm', 'git'); $props = null; if ($scmConf === 'svn') { $props = $scm->getProperties($commit); } return Pluf_Shortcuts_RenderToResponse('source/'.$scmConf.'/tree.html', array( 'page_title' => $title, 'title' => $title, 'files' => $res, 'cobject' => $cobject, 'commit' => $commit, 'tree_in' => $tree_in, 'branches' => $branches, 'props' => $props, ), $request); } public $tree_precond = array('IDF_Precondition::accessSource'); public function tree($request, $match) { $title = sprintf(__('%1$s %2$s Source Tree'), (string) $request->project, $this->getScmType($request)); $scm = IDF_Scm::get($request); $branches = $scm->getBranches(); $commit = $match[2]; $request_file = $match[3]; if ('commit' != $scm->testHash($commit, $request_file)) { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', array($request->project->shortname, $branches[0])); return new Pluf_HTTP_Response_Redirect($url); } $request_file_info = $scm->getFileInfo($request_file, $commit); if (!$request_file_info) { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', array($request->project->shortname, $branches[0])); return new Pluf_HTTP_Response_Redirect($url); } if ($request_file_info->type != 'tree') { $info = self::getMimeType($request_file_info->file); if (!self::isText($info)) { $rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info, $commit), $info[0]); $rep->headers['Content-Disposition'] = 'attachment; filename="'.$info[1].'"'; return $rep; } else { // We want to display the content of the file as text $extra = array('branches' => $branches, 'commit' => $commit, 'request_file' => $request_file, 'request_file_info' => $request_file_info, 'mime' => $info, ); return $this->viewFile($request, $match, $extra); } } $bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->file); $page_title = $bc.' - '.$title; $cobject = $scm->getCommit($commit); $tree_in = in_array($commit, $branches); $res = new Pluf_Template_ContextVars($scm->filesAtCommit($commit, $request_file)); // try to find the previous level if it exists. $prev = split('/', $request_file); $l = array_pop($prev); $previous = substr($request_file, 0, -strlen($l.' ')); $scmConf = $request->conf->getVal('scm', 'git'); $props = null; if ($scmConf === 'svn') { $props = $scm->getProperties($commit, $request_file); } return Pluf_Shortcuts_RenderToResponse('source/'.$scmConf.'/tree.html', array( 'page_title' => $page_title, 'title' => $title, 'breadcrumb' => $bc, 'files' => $res, 'commit' => $commit, 'cobject' => $cobject, 'base' => $request_file_info->file, 'prev' => $previous, 'tree_in' => $tree_in, 'branches' => $branches, 'props' => $props, ), $request); } public static function makeBreadCrumb($project, $commit, $file, $sep='/') { $elts = split('/', $file); $out = array(); $stack = ''; $i = 0; foreach ($elts as $elt) { $stack .= ($i==0) ? $elt : '/'.$elt; $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::tree', array($project->shortname, $commit, $stack)); $out[] = ''.Pluf_esc($elt).''; $i++; } return ''.implode(''.$sep.'', $out).''; } public $commit_precond = array('IDF_Precondition::accessSource'); public function commit($request, $match) { $scm = IDF_Scm::get($request); $commit = $match[2]; $branches = $scm->getBranches(); if ('commit' != $scm->testHash($commit)) { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', array($request->project->shortname, $branches[0])); return new Pluf_HTTP_Response_Redirect($url); } $title = sprintf(__('%s Commit Details'), (string) $request->project); $page_title = sprintf(__('%s Commit Details - %s'), (string) $request->project, $commit); $cobject = $scm->getCommit($commit); $diff = new IDF_Diff($cobject->changes); $diff->parse(); $scmConf = $request->conf->getVal('scm', 'git'); return Pluf_Shortcuts_RenderToResponse('source/commit.html', array( 'page_title' => $page_title, 'title' => $title, 'diff' => $diff, 'cobject' => $cobject, 'commit' => $commit, 'branches' => $branches, 'scm' => $scmConf, ), $request); } /** * Should only be called through self::tree */ public function viewFile($request, $match, $extra) { $title = sprintf(__('%1$s %2$s Source Tree'), (string) $request->project, $this->getScmType($request)); $scm = IDF_Scm::get($request); $branches = $extra['branches']; $commit = $extra['commit']; $request_file = $extra['request_file']; $request_file_info = $extra['request_file_info']; $bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->file); $page_title = $bc.' - '.$title; $cobject = $scm->getCommit($commit); $tree_in = in_array($commit, $branches); // try to find the previous level if it exists. $prev = split('/', $request_file); $l = array_pop($prev); $previous = substr($request_file, 0, -strlen($l.' ')); $scmConf = $request->conf->getVal('scm', 'git'); $props = null; if ($scmConf === 'svn') { $props = $scm->getProperties($commit, $request_file); } $content = self::highLight($extra['mime'], $scm->getBlob($request_file_info, $commit)); return Pluf_Shortcuts_RenderToResponse('source/'.$scmConf.'/file.html', array( 'page_title' => $page_title, 'title' => $title, 'breadcrumb' => $bc, 'file' => $content, 'commit' => $commit, 'cobject' => $cobject, 'fullpath' => $request_file, 'base' => $request_file_info->file, 'prev' => $previous, 'tree_in' => $tree_in, 'branches' => $branches, 'props' => $props, ), $request); } /** * Get a given file at a given commit. * */ public $getFile_precond = array('IDF_Precondition::accessSource'); public function getFile($request, $match) { $scm = IDF_Scm::get($request); $branches = $scm->getBranches(); $commit = $match[2]; $request_file = $match[3]; if ('commit' != $scm->testHash($commit, $request_file)) { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', array($request->project->shortname, $branches[0])); return new Pluf_HTTP_Response_Redirect($url); } $request_file_info = $scm->getFileInfo($request_file, $commit); if (!$request_file_info or $request_file_info->type == 'tree') { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', array($request->project->shortname, $branches[0])); return new Pluf_HTTP_Response_Redirect($url); } $info = self::getMimeType($request_file_info->file); $rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info, $commit), $info[0]); $rep->headers['Content-Disposition'] = 'attachment; filename="'.$info[1].'"'; return $rep; } /** * Get a zip archive of the current commit. * */ public $download_precond = array('IDF_Precondition::accessSource'); public function download($request, $match) { $commit = trim($match[2]); $scm = IDF_Scm::get($request); $branches = $scm->getBranches(); if ('commit' != $scm->testHash($commit)) { // Redirect to the first branch $url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase', array($request->project->shortname, $branches[0])); return new Pluf_HTTP_Response_Redirect($url); } $base = $request->project->shortname.'-'.$commit; $cmd = $scm->getArchiveCommand($commit, $base.'/'); $rep = new Pluf_HTTP_Response_CommandPassThru($cmd, 'application/x-zip'); $rep->headers['Content-Transfer-Encoding'] = 'binary'; $rep->headers['Content-Disposition'] = 'attachment; filename="'.$base.'.zip"'; return $rep; } /** * Find the mime type of a file. * * Use /etc/mime.types to find the type. * * @param string Filename/Filepath * @param string Path to the mime types database ('/etc/mime.types') * @param array Mime type found or 'application/octet-stream', basename, extension */ public static function getMimeType($file, $src='/etc/mime.types') { $mimes = preg_split("/\015\012|\015|\012/", file_get_contents($src)); $info = pathinfo($file); if (isset($info['extension'])) { foreach ($mimes as $mime) { if ('#' != substr($mime, 0, 1)) { $elts = preg_split('/ |\t/', $mime, -1, PREG_SPLIT_NO_EMPTY); if (in_array($info['extension'], $elts)) { return array($elts[0], $info['basename'], $info['extension']); } } } } else { // we consider that if no extension and base name is all // uppercase, then we have a text file. if ($info['basename'] == strtoupper($info['basename'])) { return array('text/plain', $info['basename'], 'txt'); } $info['extension'] = 'bin'; } return array('application/octet-stream', $info['basename'], $info['extension']); } /** * Find if a given mime type is a text file. * This uses the output of the self::getMimeType function. * * @param array (Mime type, file name, extension) * @return bool Is text */ public static function isText($fileinfo) { if (0 === strpos($fileinfo[0], 'text/')) { return true; } $ext = 'mdtext php js'; return (in_array($fileinfo[2], explode(' ', $ext))); } public static function highLight($fileinfo, $content) { $table = array(); $i = 1; foreach (preg_split("/\015\012|\015|\012/", $content) as $line) { $table[] = ''.$i.'' .''.IDF_Diff::padLine(Pluf_esc($line)).''; $i++; } return Pluf_Template::markSafe(implode("\n", $table)); } /** * Get the scm type for page title * * @return String */ private function getScmType($request) { return mb_convert_case($request->conf->getVal('scm', 'git'), MB_CASE_TITLE, 'UTF-8'); } } function IDF_Views_Source_PrettySize($size) { return Pluf_Template::markSafe(str_replace(' ', ' ', Pluf_Utils::prettySize($size))); }