From: Dan Brook Date: Sun, 2 May 2010 19:06:51 +0000 (+0100) Subject: Merge remote branch 't0m/json' into json X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=68943a848e5fae4bd91569b930ddcdcecc6dfe9b;hp=68b525fcc3c5788eef526ebb643746317a10372b;p=catagits%2FGitalist.git Merge remote branch 't0m/json' into json Conflicts: Makefile.PL lib/Gitalist/Controller/Root.pm lib/Gitalist/Git/Repository.pm --- diff --git a/.gitignore b/.gitignore index b23a01a..ccafab4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +Thumbs.db Gitalist-* *.swp git-daemon-export-ok @@ -7,7 +8,6 @@ META.yml Makefile Makefile.old blib* -inc* pm_to_blib MANIFEST MANIFEST.bak diff --git a/Changes b/Changes index 90280a8..4488bb5 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,42 @@ This file documents the revision history for Perl extension Gitalist. +0.001003 2010-04-21 + - Added folder / file icons in tree view (Foxtons) + - Added favicon + - Fix for non uri_for hard coded links for running at not root locations. + (RT#56747) + +0.001002 2010-04-20 + - Fix link for the project_index action. + - Add missing images to MANIFEST. + +0.001001 2010-04-15 + - Bumping version number to a format I understand, thanks to mst for + pointing me in the right direction and t0m for the new number. + - Cleaned up the search results - removed search from the homepage as + wasn't working (Foxtons). + - Provide a link for an atom entry. switch mode=xml to type=xhtml (Brian + Cassidy). + +0.000006.1 2010-04-14 + - Documentation fixes for the --repo-dir flag (Dagfinn Ilmari Mannsåker). + +0.000006 2010-04-10 + - Major frontend redesign, thanks to ranguard and the web designers at + Foxtons for making this happen. + - Major URI overhaul, Gitalist has gone from old gitweb style CGI + parameters to proper URIs, however the old URIs are still supported + and will redirect appropriately (Tomas Doran). + - With the URI overhaul also came breaking up actions into fragments + for use with Catalyst::View::Component::SubInclude. + - BIG BREAKING CHANGE - Gitalist::Model::GitRepos has been renamed + Gitalist::Model::CollectionOfRepos. You need to fix your config and + if you have actually installed Gitalist - remove the old model file. + - Bump required version of Git::PurePerl for Encoding fixes. + - Fix Makefile.PL to not need release deps when checking out of Git. + 0.000005 2010-01-09 + - Require Git::PurePerl for Win32 compatibility. - Switch to IPC::Run::start for streamed mode, fixing RT#52658 and the tests with FreeBSD. - Require new FCGI release in the FCGI script for upstream bug fixes. @@ -23,6 +59,7 @@ This file documents the revision history for Perl extension Gitalist. - Decode getpwuid values correctly (Dagfinn Ilmari Mannsåker) - Generate correct provides information in META.yml so that search.cpan indexes the classes contained in Gitalist correctly. + - Dropped the dependency on File::Stat::ModeString 0.000003 2009-12-09 - Officially switch repository to Shadowcat @@ -30,7 +67,6 @@ This file documents the revision history for Perl extension Gitalist. - Start streamlining and generally rejiging the layout. - Hacked in syntax highlighting to the blame view. - Further tweaks to the blame view, making it more informative. - - Dropped the dependency on File::Stat::ModeString - Move all POD below the code, for ::Repo and ::Project. 0.000002 2009-12-06 diff --git a/ISSUES b/ISSUES index 057c37d..74d64a7 100644 --- a/ISSUES +++ b/ISSUES @@ -1,3 +1,7 @@ +* Root /search page doesn't do anything +* Atom feed sucks shit (see t/atom.t) +* OPML & RSS & Atom Feed dates incorrect format +* Gitweb compat URIs are borked - there were some I just wholesale ripped out. Put back.. * Sort out commitdiff so conflicted merges have an equivalent display to gitweb (d7f39bebabeb31ce9a7b4f1b6f3db9f391b78c3e as a reference) * Need to sanitise the input ... * The snapshot action does not properly support tgz - requests for this format will receive a tar. tbz2 is not supported at all (yet). diff --git a/Makefile.PL b/Makefile.PL index 9bac265..54e0b01 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -1,5 +1,8 @@ #!/usr/bin/env perl +use FindBin; +BEGIN { do "$FindBin::Bin/script/env" or die $@ } + use strict; use warnings; @@ -29,7 +32,7 @@ if ($ENV{GITALIST_RELEASE_TESTING}) { # Fill in provides info so that indexing works right (in the face of MX::Declare) # by just smashing filenames to package names and not trying to be smart.. File::Find::find(sub { - return unless $File::Find::name =~ /\.pm$/; + return unless /^\w.*?\.pm$/; my $fn = $File::Find::name; my $ver = ExtUtils::MM_Unix->parse_version($fn); @@ -54,9 +57,13 @@ requires 'Catalyst::Plugin::ConfigLoader'; requires 'Catalyst::Plugin::StackTrace'; requires 'Catalyst::Plugin::Static::Simple'; requires 'Catalyst::Plugin::Unicode::Encoding'; +requires 'Catalyst::Plugin::SubRequest' => '0.15'; requires 'Catalyst::Action::RenderView'; requires 'Catalyst::Component::InstancePerContext'; +requires 'Catalyst::Controller::ActionRole'; +requires 'Catalyst::View::Component::SubInclude' => '0.07'; requires 'Catalyst::View::TT'; +requires 'Try::Tiny'; requires 'Catalyst::Action::Serialize'; requires 'Template'; @@ -67,6 +74,7 @@ requires 'Config::General'; requires 'Moose'; requires 'Moose::Autobox'; +requires 'MooseX::MultiMethods' => '0.10'; requires 'MooseX::Declare' => '0.32'; requires 'MooseX::Types::DateTime'; requires 'MooseX::Types::ISO8601'; @@ -74,31 +82,31 @@ requires 'MooseX::Types::Common'; requires 'MooseX::Types::Path::Class'; requires 'MooseX::Types'; requires 'MooseX::Storage'; -<<<<<<< HEAD requires 'JSON::Any'; -======= requires 'JSON::XS'; ->>>>>>> origin/json requires 'namespace::autoclean'; -requires 'Git::PurePerl' => '0.43'; +requires 'Git::PurePerl' => '0.46'; requires 'aliased'; requires 'CGI'; requires 'DateTime'; requires 'DateTime::Format::Mail'; requires 'File::Copy::Recursive'; +requires 'File::Type'; +requires 'File::Type::WebImages'; requires 'File::Which'; requires 'HTML::Entities'; requires 'IPC::Run'; +requires 'JSON::XS'; requires 'List::MoreUtils'; requires 'Path::Class' => '0.17'; requires 'Sub::Exporter'; requires 'Syntax::Highlight::Engine::Kate'; requires 'Sys::Hostname'; -requires 'XML::Atom'; -requires 'XML::RSS'; requires 'XML::OPML::SimpleGen'; +requires 'XML::Atom::Feed'; +requires 'XML::RSS'; test_requires 'Test::More' => '0.88'; test_requires 'Test::utf8' => '0.02'; @@ -114,6 +122,16 @@ if ($Module::Install::AUTHOR) { and die $!; } +if ($ENV{GITALIST_RELEASE_TESTING}) { + author_tests('t/author'); + author_requires('Test::NoTabs'); + author_requires('Test::Pod' => '1.14'); + author_requires('Test::Pod::Coverage' => '1.04'); + author_requires('Test::WWW::Mechanize::Catalyst' => '0.51'); + author_requires('HTML::TreeBuilder::XPath'); + author_requires('WWW::Mechanize::TreeBuilder'); +} + install_script glob('script/*.pl'); auto_install; diff --git a/README b/README index 86853b2..2de1bfa 100644 --- a/README +++ b/README @@ -41,7 +41,7 @@ GETTING GITALIST The canonical repository for the master branch is: - it://git.shadowcat.co.uk/catagits/Gitalist.git + git://git.shadowcat.co.uk/catagits/Gitalist.git Gitalist is also mirrored to github, and a number of people have active forks with branches and/or new features in the master branch. @@ -73,7 +73,7 @@ INITIAL CONFIGURATION cp `perl -Ilib -MGitalist -e'print Gitalist->path_to("gitalist.conf")'` gitalist.conf - You can then edit this confg, adding a repos_dir path and customising + You can then edit this confg, adding a repo_dir path and customising other settings as desired. You can then start the Gitalist demo server by setting @@ -83,15 +83,15 @@ INITIAL CONFIGURATION Alternatively, if you only want to set a repository directory and are otherwise happy with the default configuration, then you can set the - "GITALIST_REPOS_DIR" environment variable, or pass the "--repos_dir" - flag to any of the scripts. + "GITALIST_REPO_DIR" environment variable, or pass the "--repo_dir" flag + to any of the scripts. - GITALIST_REPOS_DIR=/home/myuser/code/git gitalist_server.pl - gitalist_server.pl --repos_dir home/myuser/code/git + GITALIST_REPO_DIR=/home/myuser/code/git gitalist_server.pl + gitalist_server.pl --repo_dir home/myuser/code/git - The "GITALIST_REPOS_DIR" environment variable will override the + The "GITALIST_REPO_DIR" environment variable will override the repository directory set in configuration, and will itself be overridden - by he "--repos_dir" flag. + by he "--repo_dir" flag. RUNNING Once you have followed the instructions above to install and configure diff --git a/gitalist.conf b/gitalist.conf index 98b3160..d2e747c 100644 --- a/gitalist.conf +++ b/gitalist.conf @@ -1,10 +1,10 @@ name Gitalist - + #git /path/to/git # Configure this to where your repositories are. # repo_dir __path_to(../)__ - + sitename "A Gitalist" diff --git a/gitalist_local.conf b/gitalist_local.conf index bd77889..2729895 100644 --- a/gitalist_local.conf +++ b/gitalist_local.conf @@ -1,8 +1,8 @@ - + # This is set so that if you run Gitalist out of a git checkout then you # can browse the repositories the directory level above your checkout. # This file is suppressed from the built dist by MANIFEST.SKIP, so you # don't have this config if you install from CPAN. repo_dir __path_to(../)__ - + diff --git a/lib/Gitalist.pm b/lib/Gitalist.pm index c59a836..3d2703a 100644 --- a/lib/Gitalist.pm +++ b/lib/Gitalist.pm @@ -11,33 +11,49 @@ use Catalyst qw/ Unicode::Encoding Static::Simple StackTrace + SubRequest /; -our $VERSION = '0.000005'; +our $VERSION = '0.001003'; $VERSION = eval $VERSION; __PACKAGE__->config( name => 'Gitalist', default_view => 'Default', - default_model => 'GitRepos', + default_model => 'CollectionOfRepos', ); __PACKAGE__->setup(); +after prepare_path => sub { + my ($ctx) = @_; + if ($ctx->req->param('a')) { + $ctx->request->uri->path('/legacy' . $ctx->request->uri->path); + } +}; + around uri_for => sub { my ($orig, $c) = (shift, shift); - my $repository_name = $c->stash->{'Repository'} && $c->stash->{'Repository'}->name; - my $hash = ref($_[-1]) eq 'HASH' ? pop @_ : {}; - my $params = Catalyst::Utils::merge_hashes( - { p => $hash->{p} || $repository_name }, - $hash, - ); - delete $params->{p} unless defined $params->{p} && length $params->{p}; - (my $uri = $c->$orig(@_, $params)) - =~ tr[&][;]; + my $uri = $c->$orig(@_); + $$uri =~ tr[&][;] if defined $uri; return $uri; }; +around uri_for_action => sub { + my ($orig, $c) = (shift, shift); + my $uri = $c->$orig(@_); + $$uri =~ s[/fragment\b][] if defined $uri; + return $uri; +}; + +sub uri_with { + my ($self, @args) = @_; + my $uri = $self->request->uri_with(@args); + # Wow this awful. + $uri =~ s[/fragment\b][]; + return $uri; +} + 1; __END__ @@ -90,7 +106,7 @@ Alternatively, you can get Gitalist using git. The canonical repository for the master branch is: - it://git.shadowcat.co.uk/catagits/Gitalist.git + git://git.shadowcat.co.uk/catagits/Gitalist.git Gitalist is also mirrored to github, and a number of people have active forks with branches and/or new features in the master branch. @@ -123,21 +139,21 @@ by running: cp `perl -Ilib -MGitalist -e'print Gitalist->path_to("gitalist.conf")'` gitalist.conf -You can then edit this confg, adding a repos_dir path and customising other settings as desired. +You can then edit this confg, adding a repo_dir path and customising other settings as desired. You can then start the Gitalist demo server by setting C<< GITALIST_CONFIG >>. For example: GITALIST_CONFIG=/usr/local/etc/gitalist.conf gitalist_server.pl Alternatively, if you only want to set a repository directory and are otherwise happy with -the default configuration, then you can set the C<< GITALIST_REPOS_DIR >> environment -variable, or pass the C<< --repos_dir >> flag to any of the scripts. +the default configuration, then you can set the C<< GITALIST_REPO_DIR >> environment +variable, or pass the C<< --repo_dir >> flag to any of the scripts. - GITALIST_REPOS_DIR=/home/myuser/code/git gitalist_server.pl - gitalist_server.pl --repos_dir home/myuser/code/git + GITALIST_REPO_DIR=/home/myuser/code/git gitalist_server.pl + gitalist_server.pl --repo_dir home/myuser/code/git -The C<< GITALIST_REPOS_DIR >> environment variable will override the repository directory set -in configuration, and will itself be overridden by he C<< --repos_dir >> flag. +The C<< GITALIST_REPO_DIR >> environment variable will override the repository directory set +in configuration, and will itself be overridden by he C<< --repo_dir >> flag. =head1 RUNNING diff --git a/lib/Gitalist/ActionRole/FilenameArgs.pm b/lib/Gitalist/ActionRole/FilenameArgs.pm new file mode 100644 index 0000000..f0ed177 --- /dev/null +++ b/lib/Gitalist/ActionRole/FilenameArgs.pm @@ -0,0 +1,14 @@ +package Gitalist::ActionRole::FilenameArgs; +use Moose::Role; +use namespace::autoclean; + +requires 'execute'; + +before 'execute' => sub { + my ($self, $controller, $c, @args) = @_; + $c->stash->{filename} = join('/', @args) || '' + unless $c->stash->{filename}; +}; + +1; + diff --git a/lib/Gitalist/ContentMangler/Resolver.pm b/lib/Gitalist/ContentMangler/Resolver.pm new file mode 100644 index 0000000..f0ba985 --- /dev/null +++ b/lib/Gitalist/ContentMangler/Resolver.pm @@ -0,0 +1,6 @@ +package Gitalist::ContentMangler::Resolver; +use Moose::Role; + +requires 'resolve'; + +1; diff --git a/lib/Gitalist/ContentMangler/Resolver/Default.pm b/lib/Gitalist/ContentMangler/Resolver/Default.pm new file mode 100644 index 0000000..607f4d4 --- /dev/null +++ b/lib/Gitalist/ContentMangler/Resolver/Default.pm @@ -0,0 +1,9 @@ +use MooseX::Declare; + +class Gitalist::ContentMangler::Resolver::Default with Gitalist::ContentMangler::Resolver { + method resolve ($data) { + return unless $data->{filename}; + my $language = 'Perl' if $data->{filename} =~ /\.p[lm]$/i; + return (['SyntaxHighlight', {language => $language, css => $language}]); + } +} \ No newline at end of file diff --git a/lib/Gitalist/Controller.pm b/lib/Gitalist/Controller.pm new file mode 100644 index 0000000..9839ac3 --- /dev/null +++ b/lib/Gitalist/Controller.pm @@ -0,0 +1,7 @@ +package Gitalist::Controller; +use Moose; +use namespace::autoclean; + +BEGIN { extends 'Catalyst::Controller::ActionRole' } + +__PACKAGE__->meta->make_immutable; diff --git a/lib/Gitalist/Controller/Fragment.pm b/lib/Gitalist/Controller/Fragment.pm new file mode 100644 index 0000000..5a4bfd2 --- /dev/null +++ b/lib/Gitalist/Controller/Fragment.pm @@ -0,0 +1,32 @@ +package Gitalist::Controller::Fragment; + +use Moose; +use namespace::autoclean; + +BEGIN { extends 'Gitalist::Controller' } + +sub base : Chained('/base') PathPart('fragment') CaptureArgs(0) { + my ($self, $c) = @_; + $c->stash(no_wrapper => 1); +} + +sub collectionofrepositories : Chained('base') Args(0) { + my ($self, $c) = @_; + my @list = @{ $c->model()->repositories }; + die 'No repositories found in '. $c->model->repo_dir + unless @list; + + my $search = $c->req->param('s') || ''; + if($search) { + @list = grep { + index($_->name, $search) > -1 + or ( $_->description !~ /^Unnamed repository/ and index($_->description, $search) > -1 ) + } @list + } + + $c->stash( + repositories => \@list, + ); +} + +__PACKAGE__->meta->make_immutable; diff --git a/lib/Gitalist/Controller/Fragment/Ref.pm b/lib/Gitalist/Controller/Fragment/Ref.pm new file mode 100644 index 0000000..a2e02e3 --- /dev/null +++ b/lib/Gitalist/Controller/Fragment/Ref.pm @@ -0,0 +1,143 @@ +package Gitalist::Controller::Fragment::Ref; +use Moose; +use namespace::autoclean; + +BEGIN { extends 'Gitalist::Controller' } +with qw/ + Gitalist::URIStructure::Ref + Gitalist::URIStructure::Fragment::WithLog +/; + +use File::Type::WebImages (); +use JSON::XS qw(encode_json); + +sub base : Chained('/fragment/repository/find') PathPart('') CaptureArgs(0) {} + +sub _diff { + my ($self, $c) = @_; + my $commit = $c->stash->{Commit}; + my %filename = $c->stash->{filename} ? (filename => $c->stash->{filename}) : (); + my($tree, $patch) = $c->stash->{Repository}->diff( + commit => $commit, + parent => $c->stash->{parent}, + patch => 1, + %filename, + ); + $c->stash( + diff_tree => $tree, + diff => $patch, + # XXX Hack hack hack, see View::SyntaxHighlight + blobs => [map $_->{diff}, @$patch], + %filename, + ); +} + +after diff_fancy => sub { + my ($self, $c) = @_; + $self->_diff($c); + $c->forward('Model::ContentMangler'); +}; + +after diff_plain => sub { + my ($self, $c) = @_; + $self->_diff($c); +}; + +after tree => sub { + my ( $self, $c ) = @_; + my $repository = $c->stash->{Repository}; + my $commit = $c->stash->{Commit}; + my $tree = $c->stash->{filename} + ? $repository->get_object($repository->hash_by_path($commit->sha1, $c->stash->{filename})) + : $repository->get_object($commit->tree_sha1) + ; + $c->stash( + tree => $tree, + tree_list => [$repository->list_tree($tree->sha1)], + ); +}; + +after blame => sub { + my($self, $c) = @_; + + my $repository = $c->stash->{Repository}; + # WTF? + my $blame = $c->stash->{Commit}->blame($c->stash->{filename}, $c->stash->{Commit}->sha1); + $c->stash( + blame => $blame, + blob => join("\n", map $_->{line}, @$blame), + ); + + $c->forward('Model::ContentMangler'); +}; + +=head2 blob + +The blob action i.e the contents of a file. + +=cut + +after blob => sub { + my ( $self, $c ) = @_; + $c->stash( + is_image => File::Type::WebImages::mime_type($c->stash->{blob}), + is_binary => Gitalist::Utils::is_binary($c->stash->{blob}), + ); + $c->forward('Model::ContentMangler'); +}; + +after history => sub { + my ($self, $c) = @_; + my $repository = $c->stash->{Repository}; + my $filename = $c->stash->{filename}; + + my %logargs = ( + sha1 => $c->stash->{Commit}->sha1, + count => 25, #Gitalist->config->{paging}{log} || 25, + ($filename ? (file => $filename) : ()) + ); + + my $file = $repository->get_object( + $repository->hash_by_path( + $repository->head_hash, + $filename + ) + ); + + my $page = $c->req->param('pg') || 0; + $logargs{skip} = $c->req->param('pg') * $logargs{count} + if $c->req->param('pg'); + + $c->stash( + log_lines => [$repository->list_revs(%logargs)], + refs => $repository->references, + filename => $filename, + filetype => $file->type, + ); +}; + +after file_commit_info => sub { + my ($self, $c) = @_; + + my $repository = $c->stash->{Repository}; + + my($commit) = $repository->list_revs( + sha1 => $c->stash->{Commit}->sha1, + count => 1, + file => $c->stash->{filename}, + ); + + my $json_obj = !$commit + ? { } + : { + sha1 => $commit->sha1, + comment => $c->stash->{short_cmt}->($commit->comment), + age => $c->stash->{time_since}->($commit->authored_time), + }; + + $c->response->content_type('application/json'); + # XXX Make use of the json branch + $c->response->body( encode_json $json_obj ); +}; + +__PACKAGE__->meta->make_immutable; diff --git a/lib/Gitalist/Controller/Fragment/Repository.pm b/lib/Gitalist/Controller/Fragment/Repository.pm new file mode 100644 index 0000000..b73c1a5 --- /dev/null +++ b/lib/Gitalist/Controller/Fragment/Repository.pm @@ -0,0 +1,33 @@ +package Gitalist::Controller::Fragment::Repository; +use Moose; +use namespace::autoclean; + +BEGIN { extends 'Gitalist::Controller' } +with qw/ + Gitalist::URIStructure::Repository + Gitalist::URIStructure::Fragment::WithLog +/; + +sub base : Chained('/fragment/base') PathPart('') CaptureArgs(0) {} + +after heads => sub { + my ($self, $c) = @_; + $c->stash( + heads => $c->stash->{Repository}->heads, + ); +}; + +=head2 tags + +The current list of tags in the repo. + +=cut + +after tags => sub { + my ( $self, $c ) = @_; + $c->stash( + tags => $c->stash->{Repository}->tags, + ); +}; + +__PACKAGE__->meta->make_immutable; diff --git a/lib/Gitalist/Controller/LegacyURI.pm b/lib/Gitalist/Controller/LegacyURI.pm new file mode 100644 index 0000000..a4c4420 --- /dev/null +++ b/lib/Gitalist/Controller/LegacyURI.pm @@ -0,0 +1,146 @@ +package Gitalist::Controller::LegacyURI; +use Moose; +use Moose::Autobox; +use namespace::autoclean; + +BEGIN { extends 'Gitalist::Controller' } + +my %LEGACY_DISPATCH = ( + opml => sub { '/opml/opml' }, + project_index => sub { '/legacyuri/project_index' }, + '(?:summary|heads|tags)' => sub { + my($c, $action, $repos) = @_; + return "/repository/$action", [$repos]; + }, + blob => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h'); + return '/ref/blob', [$repos, $ref], $c->req->param('f'); + }, + blob_plain => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h'); + return '/ref/raw', [$repos, $ref], $c->req->param('f'); + }, + blobdiff => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h'); + my $compare = $c->req->param('hbp') || $c->req->param('hp'); + return '/ref/diff_fancy', [$repos, $ref], $compare, $c->req->param('f'); + }, + blobdiff_plain => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h'); + my $compare = $c->req->param('hbp') || $c->req->param('hp'); + return '/ref/diff_plain', [$repos, $ref], $compare, $c->req->param('f'); + }, + commit => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD'; + return '/ref/commit', [$repos, $ref]; + }, + # XXX These can be consolidated with the blob equivalents. + commitdiff => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD'; + my $compare = $c->req->param('hbp') || $c->req->param('hp'); + return '/ref/diff_fancy', [$repos, $ref], $compare, $c->req->param('f'); + }, + commitdiff_plain => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h'); + my $compare = $c->req->param('hbp') || $c->req->param('hp'); + return '/ref/diff_plain', [$repos, $ref || 'HEAD'], $compare, $c->req->param('f'); + }, + history => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD'; + return '/ref/history', [$repos, $ref], $c->req->param('f'); + }, + log => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD'; + return '/ref/longlog', [$repos, $ref]; + }, + patch => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD'; + return '/ref/patch', [$repos, $ref]; + }, + patches => sub { + my($c, $action, $repos) = @_; + # XXX Is the arg there wrong? It's just copying G::C::R::patch. + return '/ref/patches', [$repos, $c->req->param('h') || 'HEAD'], 1; + }, + search_help => sub { + return '/search_help'; + }, + shortlog => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD'; + return '/ref/shortlog', [$repos, $ref]; + }, + snapshot => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('h') || 'HEAD'; + return '/ref/snapshot', [$repos, $ref], $c->req->param('sf'); + }, + tree => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h') || 'HEAD'; + return '/ref/tree', [$repos, $ref], $c->req->param('f'); + }, + '(?:atom|rss)' => sub { + my($c, $action, $repos) = @_; + # XXX No support for arbitrary branches or merges/nomerges option :( + return "/repository/$action", [$repos], $c->req->param('f'); + }, + blame => sub { + my($c, $action, $repos) = @_; + my $ref = $c->req->param('hb') || $c->req->param('h'); + return '/ref/blame', [$repos, $ref], $c->req->param('f'); + }, +); + +sub _legacy_uri { + my($self, $c, $repos, $action) = @_; + + return + unless $action; + + my @result = grep { $action =~ /^$_$/ } keys %LEGACY_DISPATCH; + die "Matched too many actions for '$a' - @result" + if @result > 1; + + return + unless $result[0]; + + my($real_action, $captures, @args) = $LEGACY_DISPATCH{$result[0]}->($c, $action, $repos); + + return $real_action, $captures || [], grep defined, @args; +} + +sub handler : Chained('/base') PathPart('legacy') Args() { + my ( $self, $c, $repos ) = @_; + + $repos ||= $c->req->param('p'); + + my ($action, $captures, @args) = $self->_legacy_uri($c, $repos, $c->req->param('a')); + + die("Not supported") + unless $action; + + $c->res->redirect($c->uri_for_action($action, $captures, @args)); + $c->res->status(301); +} + +sub project_index : Chained('/base') Args(0) { + my ( $self, $c ) = @_; + + $c->response->content_type('text/plain'); + $c->response->body( + join "\n", map $_->name, $c->model()->repositories->flatten + ) or die 'No repositories found in '. $c->model->repo_dir; +} + +__PACKAGE__->meta->make_immutable; diff --git a/lib/Gitalist/Controller/OPML.pm b/lib/Gitalist/Controller/OPML.pm new file mode 100644 index 0000000..ece56f3 --- /dev/null +++ b/lib/Gitalist/Controller/OPML.pm @@ -0,0 +1,29 @@ +package Gitalist::Controller::OPML; + +use Moose; +use Moose::Autobox; +use DateTime; +use Sys::Hostname qw/hostname/; +use XML::OPML::SimpleGen; + +use namespace::autoclean; + +BEGIN { extends 'Gitalist::Controller' } + +sub opml : Chained('/base') Args(0) { + my ($self, $c) = @_; + + my $opml = XML::OPML::SimpleGen->new(); + + $c->stash( + title => lc(hostname()) . ' - ' . blessed($c)->config->{name}, + Repositories => $c->model()->repositories, + now => DateTime->now, + template => 'opml.tt2', + no_wrapper => 1, + ); + + $c->response->content_type('application/rss'); +} + +__PACKAGE__->meta->make_immutable; diff --git a/lib/Gitalist/Controller/Ref.pm b/lib/Gitalist/Controller/Ref.pm new file mode 100644 index 0000000..207d4ca --- /dev/null +++ b/lib/Gitalist/Controller/Ref.pm @@ -0,0 +1,86 @@ +package Gitalist::Controller::Ref; + +use Moose; +use namespace::autoclean; + +BEGIN { extends 'Gitalist::Controller' } +with 'Gitalist::URIStructure::Ref'; + +use File::Type; +use File::Type::WebImages (); + +sub base : Chained('/repository/find') PathPart('') CaptureArgs(0) {} + +after commit => sub { + my($self, $c) = @_; + + $c->stash->{diff_tree} = ($c->stash->{Repository}->diff( + commit => $c->stash->{Commit}, + ))[0]; +}; + +sub raw : Chained('find') Does('FilenameArgs') Args() { + my ($self, $c) = @_; + $c->forward('find_blob'); + + if(!Gitalist::Utils::is_binary($c->stash->{blob})) { + $c->response->content_type('text/plain; charset=utf-8'); + } else { + my $ft = File::Type->new(); + $c->response->content_type( + File::Type::WebImages::mime_type($c->stash->{blob}) + || File::Type->new->mime_type($c->stash->{blob}) + ); + } + + $c->response->body(delete $c->stash->{blob}); +} + +=head2 snapshot + +Provides a snapshot of a given commit. + +=cut + +sub snapshot : Chained('find') PathPart('snapshot') Args() { + my ($self, $c, $format) = @_; + $format ||= 'tgz'; + my @snap = $c->stash->{Repository}->snapshot( + sha1 => $c->stash->{Commit}->sha1, + format => $format + ); + $c->response->status(200); + $c->response->headers->header( 'Content-Disposition' => + "attachment; filename=$snap[0]"); + $c->response->body($snap[1]); +} + +=head2 patch + +A raw patch for a given commit. + +=cut + +sub patch : Chained('find') Args(0) { + my ($self, $c) = @_; + $c->detach('patches', [1]); +} + +=head2 patches + +The patcheset for a given commit ??? + +=cut + +sub patches : Chained('find') Args(1) { + my ($self, $c, $count) = @_; + $count ||= Gitalist->config->{patches}{max}; + my $commit = $c->stash->{Commit}; + my $parent = $c->req->param('hp') || undef; # FIXME + my $patch = $commit->get_patch( $parent, $count ); + $c->response->body($patch); + $c->response->content_type('text/plain'); + $c->response->status(200); +} + +__PACKAGE__->meta->make_immutable; diff --git a/lib/Gitalist/Controller/Repository.pm b/lib/Gitalist/Controller/Repository.pm new file mode 100644 index 0000000..0aba16c --- /dev/null +++ b/lib/Gitalist/Controller/Repository.pm @@ -0,0 +1,131 @@ +package Gitalist::Controller::Repository; +use Moose; +use XML::Atom::Feed; +use XML::Atom::Entry; +use XML::RSS; +use Sys::Hostname qw/hostname/; +use namespace::autoclean; + +BEGIN { extends 'Gitalist::Controller' } +with 'Gitalist::URIStructure::Repository'; + +sub base : Chained('/base') PathPart('') CaptureArgs(0) {} + +=head2 search + +The action for the search form. + +=cut + +sub search : Chained('find') Args(0) { + my($self, $c) = @_; + my $repository = $c->stash->{Repository}; + # Lifted from /shortlog. + my %logargs = ( + sha1 => $repository->head_hash, +# count => Gitalist->config->{paging}{log}, +# ($c->req->param('f') ? (file => $c->req->param('f')) : ()), + search => { + type => $c->req->param('type'), + text => $c->req->param('text'), + regexp => $c->req->param('regexp') || 0, + }, + ); + + $c->stash( +# commit => $commit, + results => [$repository->list_revs(%logargs)], + # This could be added - page => $page, + ); +} + +=head2 tree + +Provide a simple redirect to C. + +=cut + +sub tree : Chained('find') Args(0) { + my($self, $c) = @_; + $c->res->redirect($c->uri_for_action('/ref/tree', [$c->stash->{Repository}->name, 'HEAD'])); + $c->res->status(301); +} + +=head2 atom + +Provides an atom feed for a given repository. + +=cut + +sub atom : Chained('find') Does('FilenameArgs') Args() { + my ($self, $c) = @_; + + my $host = lc hostname(); + $c->stash( + title => $host . ' - ' . Gitalist->config->{name}, + updated => DateTime->now + ); + + my $repository = $c->stash->{Repository}; + my %logargs = ( + sha1 => $repository->head_hash, + count => Gitalist->config->{paging}{log} || 25, + ($c->stash->{filename} ? (file => $c->stash->{filename}) : ()), + ); + + my @revs; + my $mk_title = $c->stash->{short_cmt}; + for my $commit ($repository->list_revs(%logargs)) { + my $entry = {}; + $entry->{title} = $mk_title->($commit->comment); + $entry->{id} = $c->uri_for_action('/ref/commit', [$repository->name, $commit->sha1]); + # XXX FIXME Needs work ... + $entry->{content} = $commit->comment; + push(@revs, $entry); + } + $c->stash( + Commits => \@revs, + no_wrapper => 1, + ); + $c->response->content_type('application/atom+xml'); +} + +=head2 rss + +Provides an RSS feed for a given repository. + +=cut + +sub rss : Chained('find') Does('FilenameArgs') Args() { + my ($self, $c) = @_; + + my $repository = $c->stash->{Repository}; + + $c->stash( + title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name}, + language => 'en', + pubDate => DateTime->now, + lastBuildDate => DateTime->now, + no_wrapper => 1, + ); + + my %logargs = ( + sha1 => $repository->head_hash, + count => Gitalist->config->{paging}{log} || 25, + ($c->stash->{filename} ? (file => $c->stash->{filename}) : ()), + ); + my @revs; + my $mk_title = $c->stash->{short_cmt}; + for my $commit ($repository->list_revs(%logargs)) { + # XXX FIXME Needs work .... + push(@revs, { + title => $mk_title->($commit->comment), + permaLink => $c->uri_for_action('/ref/commit', [$repository->name, $commit->sha1]), + description => $commit->comment, + }); + } + $c->stash(Commits => \@revs); + $c->response->content_type('application/rss+xml'); +} + +__PACKAGE__->meta->make_immutable; diff --git a/lib/Gitalist/Controller/Root.pm b/lib/Gitalist/Controller/Root.pm index be29129..a46fff0 100644 --- a/lib/Gitalist/Controller/Root.pm +++ b/lib/Gitalist/Controller/Root.pm @@ -2,643 +2,37 @@ package Gitalist::Controller::Root; use Moose; use Moose::Autobox; -use Sys::Hostname (); -use XML::Atom::Feed; -use XML::Atom::Entry; -use XML::RSS; -use XML::OPML::SimpleGen; - +use Digest::MD5 qw(md5_hex); use Gitalist::Utils qw/ age_string /; use namespace::autoclean; -BEGIN { extends 'Catalyst::Controller' } +BEGIN { extends 'Gitalist::Controller' } -__PACKAGE__->config->{namespace} = ''; +__PACKAGE__->config(namespace => ''); sub root : Chained('/') PathPart('') CaptureArgs(0) {} -sub _get_object { - my($self, $c, $haveh) = @_; - - my $h = $haveh || $c->req->param('h') || ''; - my $f = $c->req->param('f'); - - my $m = $c->stash->{Repository}; - my $pd = $m->path; - - # Either use the provided h(ash) parameter, the f(ile) parameter or just use HEAD. - my $hash = ($h =~ /[^a-f0-9]/ ? $m->head_hash($h) : $h) - || ($f && $m->hash_by_path($f)) - || $m->head_hash - # XXX This could definitely use more context. - || Carp::croak("Couldn't find a hash for the commit object!"); - - my $obj = $m->get_object($hash) - or Carp::croak("Couldn't find a object for '$hash' in '$pd'!"); - - return $obj; -} - sub index : Chained('base') PathPart('') Args(0) { - my ( $self, $c ) = @_; - - $c->detach($c->req->param('a')) - if $c->req->param('a'); - - my @list = @{ $c->model()->repositories }; - die 'No repositories found in '. $c->model->repo_dir - unless @list; - - my $search = $c->req->param('s') || ''; - if($search) { - @list = grep { - index($_->name, $search) > -1 - or ( $_->description !~ /^Unnamed repository/ and index($_->description, $search) > -1 ) - } @list - } - - $c->stash( - search_text => $search, - repositories => \@list, - action => 'index', - ); -} - -# FIXME - WTF is this for? -sub repository_index : Chained('base') Args(0) { - my ( $self, $c ) = @_; - - my @list = @{ $c->model()->repositories }; - die 'No repositories found in '. $c->model->repo_dir - unless @list; - - $c->response->content_type('text/plain'); - $c->response->body( - join "\n", map $_->name, @list - ); - $c->response->status(200); -} -# FIXME - maintain compatibility with previous URI -sub project_index : Chained('base') Args(0) { - my ( $self, $c) = @_; - $c->detach('repository_index'); -} - -=head2 summary - -A summary of what's happening in the repo. - -=cut - -sub summary : Chained('base') Args(0) { - my ( $self, $c ) = @_; - my $repository = $c->stash->{data} = $c->stash->{Repository}; - $c->detach('error_404') unless $repository; - my $commit = $self->_get_object($c); - my @heads = @{$repository->heads}; - my $maxitems = Gitalist->config->{paging}{summary} || 10; - $c->stash( - commit => $commit, - log_lines => [$repository->list_revs( - sha1 => $commit->sha1, - count => $maxitems, - )], - refs => $repository->references, - heads => [ @heads[0 .. ($#heads < $maxitems ? $#heads : $maxitems)] ], - action => 'summary', - ); -} - -=head2 heads - -The current list of heads (aka branches) in the repo. - -=cut - -sub heads : Chained('base') Args(0) { - my ( $self, $c ) = @_; - my $repository = $c->stash->{Repository}; - $c->stash( - commit => $self->_get_object($c), - heads => $repository->heads, - action => 'heads', - ); -} - -=head2 tags - -The current list of tags in the repo. - -=cut - -sub tags : Chained('base') Args(0) { - my ( $self, $c ) = @_; - my $repository = $c->stash->{Repository}; - $c->stash( - commit => $self->_get_object($c), - tags => $repository->tags, - action => 'tags', - ); -} - -sub blame : Chained('base') Args(0) { - my($self, $c) = @_; - - my $repository = $c->stash->{Repository}; - my $h = $c->req->param('h') - || $repository->hash_by_path($c->req->param('hb'), $c->req->param('f')) - || die "No file or sha1 provided."; - my $hb = $c->req->param('hb') - || $repository->head_hash - || die "Couldn't discern the corresponding head."; - my $filename = $c->req->param('f') || ''; - - my $blame = $repository->get_object($hb)->blame($filename, $h); - $c->stash( - blame => $blame, - head => $repository->get_object($hb), - filename => $filename, - - # XXX Hack hack hack, see View::SyntaxHighlight - language => ($filename =~ /\.p[lm]$/i ? 'Perl' : ''), - blob => join("\n", map $_->{line}, @$blame), - ); - - $c->forward('View::SyntaxHighlight') - unless $c->stash->{no_wrapper}; -} - -sub _blob_objs { - my ( $self, $c ) = @_; - my $repository = $c->stash->{Repository}; - my $h = $c->req->param('h') - || $repository->hash_by_path($c->req->param('hb'), $c->req->param('f')) - || die "No file or sha1 provided."; - my $hb = $c->req->param('hb') - || $repository->head_hash - || die "Couldn't discern the corresponding head."; - - my $filename = $c->req->param('f') || ''; - - my $blob = $repository->get_object($h); - $blob = $repository->get_object( - $repository->hash_by_path($h || $hb, $filename) - ) if $blob->type ne 'blob'; - - return $blob, $repository->get_object($hb), $filename; -} - -=head2 blob - -The blob action i.e the contents of a file. - -=cut - -sub blob : Chained('base') Args(0) { - my ( $self, $c ) = @_; - - my($blob, $head, $filename) = $self->_blob_objs($c); - $c->stash( - blob => $blob->content, - head => $head, - filename => $filename, - # XXX Hack hack hack, see View::SyntaxHighlight - language => ($filename =~ /\.p[lm]$/i ? 'Perl' : ''), - action => 'blob', - ); - - $c->forward('View::SyntaxHighlight') - unless $c->stash->{no_wrapper}; -} - -=head2 blob_plain - -The plain text version of blob, where file is rendered as is. - -=cut - -sub blob_plain : Chained('base') Args(0) { - my($self, $c) = @_; - - my($blob) = $self->_blob_objs($c); - $c->response->content_type('text/plain; charset=utf-8'); - $c->response->body($blob->content); - $c->response->status(200); -} - -=head2 blobdiff_plain - -The plain text version of blobdiff. - -=cut - -sub blobdiff_plain : Chained('base') Args(0) { - my($self, $c) = @_; - - $c->stash(no_wrapper => 1); - $c->response->content_type('text/plain; charset=utf-8'); - - $c->forward('blobdiff'); -} - -=head2 blobdiff - -Exposes a given diff of a blob. - -=cut - -sub blobdiff : Chained('base') Args(0) { - my ( $self, $c ) = @_; - my $commit = $self->_get_object($c, $c->req->param('hb')); - my $filename = $c->req->param('f') - || croak("No file specified!"); - my($tree, $patch) = $c->stash->{Repository}->diff( - commit => $commit, - patch => 1, - parent => $c->req->param('hpb') || undef, - file => $filename, - ); - $c->stash( - commit => $commit, - diff => $patch, - filename => $filename, - # XXX Hack hack hack, see View::SyntaxHighlight - blobs => [$patch->[0]->{diff}], - language => 'Diff', - action => 'blobdiff', - ); - - $c->forward('View::SyntaxHighlight') - unless $c->stash->{no_wrapper}; -} - -=head2 commit - -Exposes a given commit. - -=cut - -sub commit : Chained('base') Args(0) { - my ( $self, $c ) = @_; - my $repository = $c->stash->{Repository}; - my $commit = $self->_get_object($c); - $c->stash( - commit => $commit, - diff_tree => ($repository->diff(commit => $commit))[0], - refs => $repository->references, - action => 'commit', - ); -} - -=head2 commitdiff - -Exposes a given diff of a commit. - -=cut - -sub commitdiff : Chained('base') Args(0) { - my ( $self, $c ) = @_; - my $commit = $self->_get_object($c); - my($tree, $patch) = $c->stash->{Repository}->diff( - commit => $commit, - parent => $c->req->param('hp') || undef, - patch => 1, - ); - $c->stash( - commit => $commit, - diff_tree => $tree, - diff => $patch, - # XXX Hack hack hack, see View::SyntaxHighlight - blobs => [map $_->{diff}, @$patch], - language => 'Diff', - action => 'commitdiff', - ); - - $c->forward('View::SyntaxHighlight') - unless $c->stash->{no_wrapper}; -} - -sub commitdiff_plain : Chained('base') Args(0) { - my($self, $c) = @_; - - $c->stash(no_wrapper => 1); - $c->response->content_type('text/plain; charset=utf-8'); - - $c->forward('commitdiff'); -} - -=head2 shortlog - -Expose an abbreviated log of a given sha1. - -=cut - -sub shortlog : Chained('base') Args(0) { - my ( $self, $c ) = @_; - - my $repository = $c->stash->{Repository}; - my $commit = $self->_get_object($c, $c->req->param('hb')); - my $filename = $c->req->param('f') || ''; - - my %logargs = ( - sha1 => $commit->sha1, - count => Gitalist->config->{paging}{log} || 25, - ($filename ? (file => $filename) : ()) - ); - - my $page = $c->req->param('pg') || 0; - $logargs{skip} = $c->req->param('pg') * $logargs{count} - if $c->req->param('pg'); - - $c->stash( - commit => $commit, - log_lines => [$repository->list_revs(%logargs)], - refs => $repository->references, - page => $page, - filename => $filename, - action => 'shortlog', - ); -} - -=head2 log - -Calls shortlog internally. Perhaps that should be reversed ... - -=cut - -sub log : Chained('base') Args(0) { - $_[0]->shortlog($_[1]); - $_[1]->stash->{action} = 'log'; -} - -# For legacy support. -sub history : Chained('base') Args(0) { my ( $self, $c ) = @_; - $self->shortlog($c); - my $repository = $c->stash->{Repository}; - my $file = $repository->get_object( - $repository->hash_by_path( - $repository->head_hash, - $c->stash->{filename} - ) - ); - $c->stash( action => 'history', - filetype => $file->type, - ); -} - -=head2 tree - -The tree of a given commit. - -=cut - -sub tree : Chained('base') Args(0) { - my ( $self, $c ) = @_; - my $repository = $c->stash->{Repository}; - my $commit = $self->_get_object($c, $c->req->param('hb')); - my $filename = $c->req->param('f') || ''; - my $tree = $filename - ? $repository->get_object($repository->hash_by_path($commit->sha1, $filename)) - : $repository->get_object($commit->tree_sha1) - ; - $c->stash( - commit => $commit, - tree => $tree, - tree_list => [$repository->list_tree($tree->sha1)], - path => $c->req->param('f') || '', - action => 'tree', - ); -} - -=head2 reflog - -Expose the local reflog. This may go away. - -=cut - -sub reflog : Chained('base') Args(0) { - my ( $self, $c ) = @_; - my @log = $c->stash->{Repository}->reflog( - '--since=yesterday' - ); - - $c->stash( - log => \@log, - action => 'reflog', - ); -} - -=head2 search - -The action for the search form. - -=cut - -sub search : Chained('base') Args(0) { - my($self, $c) = @_; - $c->stash(current_action => 'GitRepos'); - my $repository = $c->stash->{Repository}; - my $commit = $self->_get_object($c); - # Lifted from /shortlog. - my %logargs = ( - sha1 => $commit->sha1, - count => Gitalist->config->{paging}{log}, - ($c->req->param('f') ? (file => $c->req->param('f')) : ()), - search => { - type => $c->req->param('type'), - text => $c->req->param('text'), - regexp => $c->req->param('regexp') || 0, - }, - ); - - $c->stash( - commit => $commit, - results => [$repository->list_revs(%logargs)], - action => 'search', - # This could be added - page => $page, - ); -} - -=head2 search_help - -Provides some help for the search form. - -=cut - -sub search_help : Chained('base') Args(0) { - my ($self, $c) = @_; - $c->stash(template => 'search_help.tt2'); + $c->stash( search_text => $c->req->param('s') || '' ) # FIXME - XSS? } -=head2 atom - -Provides an atom feed for a given repository. - -=cut - -sub atom : Chained('base') Args(0) { - my($self, $c) = @_; - - my $feed = XML::Atom::Feed->new; - - my $host = lc Sys::Hostname::hostname(); - $feed->title($host . ' - ' . Gitalist->config->{name}); - $feed->updated(~~DateTime->now); - - my $repository = $c->stash->{Repository}; - my %logargs = ( - sha1 => $repository->head_hash, - count => Gitalist->config->{paging}{log} || 25, - ($c->req->param('f') ? (file => $c->req->param('f')) : ()) - ); - - my $mk_title = $c->stash->{short_cmt}; - for my $commit ($repository->list_revs(%logargs)) { - my $entry = XML::Atom::Entry->new; - $entry->title( $mk_title->($commit->comment) ); - $entry->id($c->uri_for('commit', {h=>$commit->sha1})); - # XXX Needs work ... - $entry->content($commit->comment); - $feed->add_entry($entry); - } - - $c->response->body($feed->as_xml); - $c->response->content_type('application/atom+xml'); - $c->response->status(200); -} - -=head2 rss - -Provides an RSS feed for a given repository. - -=cut - -sub rss : Chained('base') Args(0) { - my ($self, $c) = @_; - - my $repository = $c->stash->{Repository}; - - my $rss = XML::RSS->new(version => '2.0'); - $rss->channel( - title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name}, - link => $c->uri_for('summary', {p=>$repository->name}), - language => 'en', - description => $repository->description, - pubDate => DateTime->now, - lastBuildDate => DateTime->now, - ); - - my %logargs = ( - sha1 => $repository->head_hash, - count => Gitalist->config->{paging}{log} || 25, - ($c->req->param('f') ? (file => $c->req->param('f')) : ()) - ); - my $mk_title = $c->stash->{short_cmt}; - for my $commit ($repository->list_revs(%logargs)) { - # XXX Needs work .... - $rss->add_item( - title => $mk_title->($commit->comment), - permaLink => $c->uri_for(commit => {h=>$commit->sha1}), - description => $commit->comment, - ); - } - - $c->response->body($rss->as_string); - $c->response->content_type('application/rss+xml'); - $c->response->status(200); -} - -sub opml : Chained('base') Args(0) { - my($self, $c) = @_; - - my $opml = XML::OPML::SimpleGen->new(); - - $opml->head(title => lc(Sys::Hostname::hostname()) . ' - ' . Gitalist->config->{name}); - - my @list = @{ $c->model()->repositories }; - die 'No repositories found in '. $c->model->repo_dir - unless @list; - - for my $proj ( @list ) { - $opml->insert_outline( - text => $proj->name. ' - '. $proj->description, - xmlUrl => $c->uri_for(rss => {p => $proj->name}), - ); - } - - $c->response->body($opml->as_string); - $c->response->content_type('application/rss'); - $c->response->status(200); -} - -=head2 patch - -A raw patch for a given commit. - -=cut - -sub patch : Chained('base') Args(0) { - my ($self, $c) = @_; - $c->detach('patches', [1]); -} - -=head2 patches - -The patcheset for a given commit ??? - -=cut - -sub patches : Chained('base') Args(0) { - my ($self, $c, $count) = @_; - $count ||= Gitalist->config->{patches}{max}; - my $commit = $self->_get_object($c); - my $parent = $c->req->param('hp') || undef; - my $patch = $commit->get_patch( $parent, $count ); - $c->response->body($patch); - $c->response->content_type('text/plain'); - $c->response->status(200); -} - -=head2 snapshot - -Provides a snapshot of a given commit. - -=cut +# XXX Fragile much? +sub css : Chained('/root') PathPart('core.css') Args(0) { + my ( $self, $c ) = @_; -sub snapshot : Chained('base') Args(0) { - my ($self, $c) = @_; - my $format = $c->req->param('sf') || 'tgz'; - die unless $format; - my $sha1 = $c->req->param('h') || $self->_get_object($c)->sha1; - my @snap = $c->stash->{Repository}->snapshot( - sha1 => $sha1, - format => $format - ); - $c->response->status(200); - $c->response->headers->header( 'Content-Disposition' => - "attachment; filename=$snap[0]"); - $c->response->body($snap[1]); + $c->response->content_type('text/css'); + $c->stash(template => 'static/css/core.css'); } - sub base : Chained('/root') PathPart('') CaptureArgs(0) { my($self, $c) = @_; - my $repository = $c->req->param('p'); - if (defined $repository) { - eval { - $c->stash(Repository => $c->model('GitRepos')->get_repository($repository)); - }; - if ($@) { - $c->detach('/error_404'); - } - } - - my $a_repository = $c->stash->{Repository} || $c->model()->repositories->[0]; + my $git_version = `git --version`; + chomp($git_version); $c->stash( - git_version => $a_repository->run_cmd('--version'), + git_version => $git_version, version => $Gitalist::VERSION, # XXX Move these to a plugin! @@ -649,12 +43,19 @@ sub base : Chained('/root') PathPart('') CaptureArgs(0) { short_cmt => sub { my $cmt = shift; my($line) = split /\n/, $cmt; - $line =~ s/^(.{70,80}\b).*/$1 \x{2026}/; + $line =~ s/^(.{70,80}\b).*/$1 \x{2026}/ if defined $line; return $line; }, abridged_description => sub { join(' ', grep { defined } (split / /, shift)[0..10]); }, + uri_for_gravatar => sub { # FIXME - Cache these? + my $email = shift; + my $size = shift; + my $uri = 'http://www.gravatar.com/avatar/' . md5_hex($email); + $uri .= "?s=$size" if $size; + return $uri; + }, ); } @@ -713,18 +114,8 @@ Provides the repository listing. Attempt to render a view, if needed. -=head2 blame - -=head2 commitdiff_plain - =head2 error_404 -=head2 history - -=head2 opml - -=head2 repository_index - =head1 AUTHORS See L for authors. diff --git a/lib/Gitalist/Git/Object/Commit.pm b/lib/Gitalist/Git/Object/Commit.pm index ef36f05..0445999 100644 --- a/lib/Gitalist/Git/Object/Commit.pm +++ b/lib/Gitalist/Git/Object/Commit.pm @@ -49,7 +49,7 @@ class Gitalist::Git::Object::Commit method diff ( Maybe[Bool] :$patch?, Maybe[NonEmptySimpleStr] :$parent?, - Maybe[NonEmptySimpleStr] :$file? + Maybe[NonEmptySimpleStr] :$filename? ) { $parent = $parent ? $parent @@ -57,7 +57,7 @@ class Gitalist::Git::Object::Commit ? $self->parent_sha1 : '-c'; my @etc = ( - ( $file ? ('--', $file) : () ), + ( $filename ? ('--', $filename) : () ), ); my @out = $self->_raw_diff( @@ -105,7 +105,7 @@ class Gitalist::Git::Object::Commit $line{sha1} = $line{sha1dst}; $line{is_new} = $line{sha1src} =~ /^0+$/ if $line{sha1src}; - @line{qw/status sim/} = $line{status} =~ /(R)(\d+)/ + @line{qw/status sim/} = $line{status} =~ /(R)0*(\d+)/ if $line{status} =~ /^R/; push @ret, \%line; } @@ -128,7 +128,7 @@ class Gitalist::Git::Object::Commit next; } - if (/^index (\w+)\.\.(\w+) (\d+)$/) { + if (/^index (\w+)\.\.(\w+)(?: (\d+))?$/) { @{$ret[-1]}{qw(index src dst mode)} = ($_, $1, $2, $3); next } diff --git a/lib/Gitalist/Git/Repository.pm b/lib/Gitalist/Git/Repository.pm index a230129..e4eb595 100644 --- a/lib/Gitalist/Git/Repository.pm +++ b/lib/Gitalist/Git/Repository.pm @@ -4,6 +4,7 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { # FIXME, use Types::Path::Class and coerce use MooseX::Types::Common::String qw/NonEmptySimpleStr/; use MooseX::Types::Moose qw/Str Maybe Bool HashRef ArrayRef/; + use MooseX::MultiMethods; use Gitalist::Git::Types qw/SHA1 DateTime Dir/; use Moose::Autobox; use List::MoreUtils qw/any zip/; @@ -78,6 +79,15 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { } ## Public methods + + multi method get_object_or_head (SHA1 $sha1) { + $self->get_object($sha1); + } + multi method get_object_or_head (NonEmptySimpleStr $ref) { + my $sha1 = $self->head_hash($ref); + $self->get_object($sha1); + } + method head_hash (Str $head?) { my $output = $self->run_cmd(qw/rev-parse --verify/, $head || 'HEAD' ); confess("No such head: " . $head) unless defined $output; @@ -129,7 +139,7 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { if !$sha1 || $sha1 !~ $SHA1RE; my @search_opts; - if ($search) { + if ($search and exists $search->{text}) { $search->{type} = 'grep' if $search->{type} eq 'commit'; @search_opts = ( @@ -180,11 +190,11 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { method diff ( Gitalist::Git::Object :$commit!, Bool :$patch?, Maybe[NonEmptySimpleStr] :$parent?, - NonEmptySimpleStr :$file? + NonEmptySimpleStr :$filename? ) { return $commit->diff( patch => $patch, parent => $parent, - file => $file); + filename => $filename); } method reflog (@logargs) { @@ -241,6 +251,8 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { $description = $self->path->file('description')->slurp; chomp $description; }; + $description = "Unnamed repository, edit the .git/description file to set a description" + if $description eq "Unnamed repository; edit this file 'description' to name the repository."; return $description; } diff --git a/lib/Gitalist/Model/GitRepos.pm b/lib/Gitalist/Model/CollectionOfRepos.pm similarity index 98% rename from lib/Gitalist/Model/GitRepos.pm rename to lib/Gitalist/Model/CollectionOfRepos.pm index deff310..2820b53 100644 --- a/lib/Gitalist/Model/GitRepos.pm +++ b/lib/Gitalist/Model/CollectionOfRepos.pm @@ -1,4 +1,4 @@ -package Gitalist::Model::GitRepos; +package Gitalist::Model::CollectionOfRepos; use Moose; use Gitalist::Git::CollectionOfRepositories::FromDirectory; diff --git a/lib/Gitalist/Model/ContentMangler.pm b/lib/Gitalist/Model/ContentMangler.pm new file mode 100644 index 0000000..62a3356 --- /dev/null +++ b/lib/Gitalist/Model/ContentMangler.pm @@ -0,0 +1,61 @@ +package Gitalist::Model::ContentMangler; +use Moose; +use MooseX::Types::Moose qw/HashRef/; +use MooseX::Types::Common::String qw/NonEmptySimpleStr/; +use Gitalist::ContentMangler::Resolver; +use namespace::autoclean; + +extends 'Catalyst::Model'; + +has resolver_class => ( + isa => NonEmptySimpleStr, + is => 'ro', + required => 1, + default => 'Gitalist::ContentMangler::Resolver::Default', +); + +has resolver_config => ( + isa => HashRef, + is => 'ro', + default => sub { {} }, +); + +has _resolver => ( + does => 'Gitalist::ContentMangler::Resolver', + handles => ['resolve'], + is => 'bare', lazy => 1, + default => sub { + my $self = shift; + my $class = $self->resolver_class; + Class::MOP::load_class($class); + return $class->new($self->resolver_config); + }, +); + +# FIXME This method is a gross hack. +# +# We need to work out what types of content mangles we have for various things based on hit type +# file name and mime type, and perform the appropriate bits.. + +# We need to support multiple languages, and we also want to be able to do HTMLizing (for e.g. Pod) + +sub process { + my ($self, $c) = @_; + + my @steps = $self->resolve({ filename => $c->stash->{filename} }); + my @css = map { $_->[1]->{css} } grep { exists $_->[1] && exists $_->[1]->{css} && defined $_->[1]->{css} && length $_->[1]->{css} } @steps; + $c->stash( + syntax_css => [ map { $c->uri_for('/static/css/syntax/' . $_ . '.css') } @css ], + mangled => scalar @steps, + ); + + if ($c->stash->{blobs} || $c->stash->{blob}) { + foreach my $step (@steps) { + for ($c->stash->{blobs} ? @{$c->stash->{blobs}} : $c->stash->{blob}) { + $_ = $c->view($step->[0])->render($c, $_, $step->[1]); + } + } + } +} + +__PACKAGE__->meta->make_immutable; diff --git a/lib/Gitalist/URIStructure/Fragment/WithLog.pm b/lib/Gitalist/URIStructure/Fragment/WithLog.pm new file mode 100644 index 0000000..ea3ae2a --- /dev/null +++ b/lib/Gitalist/URIStructure/Fragment/WithLog.pm @@ -0,0 +1,27 @@ +package Gitalist::URIStructure::Fragment::WithLog; +use MooseX::MethodAttributes::Role; +use namespace::autoclean; + +requires 'log'; + +after log => sub { + my ($self, $c) = @_; + my $repository = $c->stash->{Repository}; + + my %logargs = ( + sha1 => $c->stash->{Commit}->sha1, # $commit->sha1 + count => 25, #Gitalist->config->{paging}{log} || 25, + ); + + my $page = $c->req->param('pg') || 0; + $logargs{skip} = abs $page * $logargs{count} + if $page; + + $c->stash( + page => $page, + log_lines => [$repository->list_revs(%logargs)], + refs => $repository->references, + ); +}; + +1; diff --git a/lib/Gitalist/URIStructure/Ref.pm b/lib/Gitalist/URIStructure/Ref.pm new file mode 100644 index 0000000..da71487 --- /dev/null +++ b/lib/Gitalist/URIStructure/Ref.pm @@ -0,0 +1,85 @@ +package Gitalist::URIStructure::Ref; +use MooseX::MethodAttributes::Role; +use Moose::Autobox; +use namespace::autoclean; + +use Gitalist::Git::Types qw/SHA1/; + +requires 'base'; + +with qw/ + Gitalist::URIStructure::WithLog +/; + +after 'base' => sub { + my ($self, $c) = @_; + confess("No repository in the stash") + unless $c->stash->{Repository}; +}; + +sub find : Chained('base') PathPart('') CaptureArgs(1) { + my ($self, $c, $sha1part) = @_; + # FIXME - Should not be here! + $c->stash->{Commit} = $c->stash->{Repository}->get_object_or_head($sha1part) + or $c->detach('/error404', "Couldn't find a object for '$sha1part' in XXXX!"); +} + +sub diff : Chained('find') CaptureArgs(0) {} + +sub _set_diff_args { + my($self, $c, @rest) = @_; + + # FIXME - This ain't pretty + $c->stash(parent => shift @rest) + if @rest == 2 + # Check that the single arg is unlikely to be a path. + or @rest && to_SHA1($rest[0]) && $c->stash->{Repository}->get_object_or_head($rest[0]); + $c->stash(filename => $rest[-1]) + if @rest; +} + +sub diff_fancy : Chained('diff') PathPart('') Args() { + my($self, $c, @rest) = @_; + + $self->_set_diff_args($c, @rest); + } + +sub diff_plain : Chained('diff') PathPart('plain') Args() { + my($self, $c, $comparison, @rest) = @_; + + $self->_set_diff_args($c, @rest); + + $c->stash(no_wrapper => 1); + $c->response->content_type('text/plain; charset=utf-8'); +} + +sub commit : Chained('find') PathPart('commit') Args(0) {} + +sub file_commit_info : Chained('find') Does('FilenameArgs') Args() {} + +sub tree : Chained('find') Does('FilenameArgs') Args() {} + +sub find_blob : Action { + my ($self, $c) = @_; + my($repo, $object) = @{$c->{stash}}{qw(Repository Commit)}; + # FIXME - Eugh! + my $h = $object->isa('Gitalist::Git::Object::Commit') + ? $repo->hash_by_path($object->sha1, $c->stash->{filename}) + : $object->isa('Gitalist::Git::Object::Blob') + ? $object->sha1 + : die "Unknown object type for '${\$object->sha1}'"; + die "No file or sha1 provided." + unless $h; + $c->stash(blob => $repo->get_object($h)->content); +} + +sub blob : Chained('find') Does('FilenameArgs') Args() { + my ($self, $c) = @_; + $c->forward('find_blob'); +} + +sub blame : Chained('find') Does('FilenameArgs') Args() {} + +sub history : Chained('find') Does('FilenameArgs') Args() {} + +1; diff --git a/lib/Gitalist/URIStructure/Repository.pm b/lib/Gitalist/URIStructure/Repository.pm new file mode 100644 index 0000000..976917e --- /dev/null +++ b/lib/Gitalist/URIStructure/Repository.pm @@ -0,0 +1,51 @@ +package Gitalist::URIStructure::Repository; +use MooseX::MethodAttributes::Role; +use Try::Tiny qw/try catch/; +use namespace::autoclean; + +requires 'base'; + +with qw/ + Gitalist::URIStructure::WithLog +/; + +sub find : Chained('base') PathPart('') CaptureArgs(1) { + my ($self, $c, $repos_name) = @_; + # XXX FIXME - This should be in the repository fragment controller, and the repository + # controller should just check has_repository + try { + my $repos = $c->model()->get_repository($repos_name); + $c->stash( + Repository => $repos, + HEAD => $repos->head_hash, + ); + } + catch { + $c->detach('/error_404'); + }; +} + +before 'log' => sub { + my ($self, $c) = @_; + $c->stash->{Commit} = $c->stash->{Repository}->get_object($c->stash->{Repository}->head_hash); +}; + +sub object : Chained('find') PathPart('') Args(1) { + my ($self, $c, $sha1) = @_; + + my $repo = $c->stash->{Repository}; + my $obj = $c->stash->{Commit} = $repo->get_object($sha1); + my($act) = (ref($obj) || '') =~ /::(\w+)$/; + + $c->res->redirect($c->uri_for_action("/ref/\L$act", [$repo->name, $obj->sha1])); + $c->res->status(301); + +} + +sub summary : Chained('find') PathPart('') Args(0) {} + +sub heads : Chained('find') Args(0) {} + +sub tags : Chained('find') Args(0) {} + +1; diff --git a/lib/Gitalist/URIStructure/WithLog.pm b/lib/Gitalist/URIStructure/WithLog.pm new file mode 100644 index 0000000..318522d --- /dev/null +++ b/lib/Gitalist/URIStructure/WithLog.pm @@ -0,0 +1,11 @@ +package Gitalist::URIStructure::WithLog; +use MooseX::MethodAttributes::Role; +use namespace::autoclean; + +sub log : Chained('find') PathPart('') CaptureArgs(0) {} + +sub shortlog : Chained('log') Args(0) {} + +sub longlog : Chained('log') PathPart('log') Args(0) {} + +1; \ No newline at end of file diff --git a/lib/Gitalist/Utils.pm b/lib/Gitalist/Utils.pm index 0df4973..f11dc34 100644 --- a/lib/Gitalist/Utils.pm +++ b/lib/Gitalist/Utils.pm @@ -45,6 +45,11 @@ sub age_string { return $age_str; } +sub is_binary { + # Crappy heuristic - does the first line or so look printable? + return $_[0] !~ /^[[:print:]]+$ (?: \s ^[[:print:]]+$ )?/mx; +} + 1; __END__ @@ -57,7 +62,11 @@ Gitalist::Utils - trivial utils for Gitalist =head2 age_string -Turns an integer number of seconds into a string.. +Turns an integer number of seconds into a string. + +=head2 is_binary + +Check whether a string is binary according to C<-B>. =head1 AUTHORS diff --git a/lib/Gitalist/View/Default.pm b/lib/Gitalist/View/Default.pm index 757f982..0c8d576 100644 --- a/lib/Gitalist/View/Default.pm +++ b/lib/Gitalist/View/Default.pm @@ -1,16 +1,28 @@ package Gitalist::View::Default; use Moose; +use Moose::Autobox; use namespace::autoclean; extends 'Catalyst::View::TT'; +with 'Catalyst::View::Component::SubInclude'; use Template::Plugin::Cycle; __PACKAGE__->config( TEMPLATE_EXTENSION => '.tt2', - WRAPPER => 'default.tt2', + WRAPPER => 'wrapper.tt2', + subinclude_plugin => 'SubRequest', ); +use Template::Stash; + +# define list method to flatten arrayrefs +$Template::Stash::LIST_OPS->{ to_path } = sub { + my $path = join('%2F', shift->flatten, @_); + $path =~ s{/}{%2F}g; + return $path; +}; + __PACKAGE__->meta->make_immutable(inline_constructor => 0); __END__ diff --git a/lib/Gitalist/View/SyntaxHighlight.pm b/lib/Gitalist/View/SyntaxHighlight.pm index 2df82ed..9c28341 100644 --- a/lib/Gitalist/View/SyntaxHighlight.pm +++ b/lib/Gitalist/View/SyntaxHighlight.pm @@ -9,37 +9,16 @@ use Syntax::Highlight::Engine::Kate::Perl (); use HTML::Entities qw(encode_entities); -# What should be done, but isn't currently: -# -# broquaint> Another Cat question - if I want to have arbitrary things highlighted is pushing things through a View at all costs terribly wrong? -# broquaint> e.g modifying this slightly to highlight anything (or arrays of anything) http://github.com/broquaint/Gitalist/blob/a7cc1ede5f9729465bb53da9c3a8b300a3aa8a0a/lib/Gitalist/View/SyntaxHighlight.pm -# t0m> no, that's totally fine.. I'd tend to push the rendering logic into a model, so you end up doing something like: $c->model('SyntaxDriver')->highlight_all($stuff, $c->view('SyntaxHighlight')); -# broquaint> I'm thinking it's a bad idea because the Controller needs to munge data such that the View knows what to do -# broquaint> You just blew my mind ;) -# t0m> ^^ That works _much_ better if you split up your view methods into process & render.. -# t0m> ala TT.. -# t0m> i.e. I'd have 'highlight this scalar' as the ->render method in the view.. -# t0m> And then the 'default' thing (i.e. process method) will do that and shove the output in the body.. -# t0m> but then you can write foreach my $thing (@things) { push(@highlighted_things, $c->view('SyntaxHighlight')->render($thing)); } -# t0m> and then I'd move that ^^ loop down into a model which actually knows about / abstracts walking the data structures concerned.. -# t0m> But splitting render and process is the most important bit.. :) Otherwise you need to jump through hoops to render things that don't fit 'nicely' into the bits of stash / body that the view uses by 'default' -# t0m> I wouldn't kill you for putting the structure walking code in the view given you're walking simple arrays / hashes.. It becomes more important if you have a more complex visitor.. -# t0m> (I use Visitor in the design patterns sense) -# t0m> As the visitor is responsible for walking the structure, delegating to the ->render call in the view which is responsible for actually mangling the content.. - sub process { my($self, $c) = @_; - for($c->stash->{blobs} ? @{$c->stash->{blobs}} : $c->stash->{blob}) { - $_ = $self->highlight($c->stash->{language} => $_); - } - - $c->forward('View::Default'); + $c->res->body($self->render($c, $c->res->body, $c->stash)); } -# XXX This takes for freakin' ever on big merges. A cache may be needed. -sub highlight { - my($self, $lang, $blob) = @_; +sub render { + my ($self, $c, $blob, $args) = @_; + + my $lang = $args->{language}; my $ret; if($lang) { diff --git a/root/_chroma_hash.tt2 b/root/_chroma_hash.tt2 deleted file mode 100644 index d7a0bb7..0000000 --- a/root/_chroma_hash.tt2 +++ /dev/null @@ -1,9 +0,0 @@ -[%- sha1 = sha1 || HEAD -%] -[%- - hptr = 0; - WHILE hptr < sha1.length - 6; - sha1part = sha1.substr(hptr, 6); - "" _ sha1part _ ""; - hptr = hptr + 6; - END; --%] diff --git a/root/_diff.tt2 b/root/_diff.tt2 old mode 100644 new mode 100755 index b3062eb..11b4199 --- a/root/_diff.tt2 +++ b/root/_diff.tt2 @@ -1,14 +1,33 @@ [% INCLUDE inc/syntax_highlight_css.tt2 %] + +

Differences

+ +
+ [% FOREACH item IN diff %] -
- diff --git - [% item.a %] - [% item.b %] + +

diff --git [%# FIXME %] + [% IF !item.src.match('^0+$') %] + [% item.a %] + [% ELSE %] + [% item.a %] + [% END %] + [% IF !item.dst.match('^0+$') %] + [% item.b %] + [% ELSE %] + [% item.b %] + [% END %] +

+ + +
+
[% blobs.${loop.index} %]
+
- [% item.index %] -
-
-
[% blobs.${loop.index} %]
+ [% item.index.replace("index","Index") %]
+ [% END %] + +
\ No newline at end of file diff --git a/root/_diff_tree.tt2 b/root/_diff_tree.tt2 old mode 100644 new mode 100755 index 2057772..7126a75 --- a/root/_diff_tree.tt2 +++ b/root/_diff_tree.tt2 @@ -1,23 +1,18 @@ +

[% diff_tree.size %] file[% "s" IF diff_tree.size > 1 %] in this diff ([%- Commit.sha1 || HEAD -%])

+ - - - + + + - - - - - - - [% FOREACH line IN diff_tree -%] [% END %] diff --git a/root/_header_feeds.tt2 b/root/_header_feeds.tt2 index 2183ab5..1fbcfaa 100644 --- a/root/_header_feeds.tt2 +++ b/root/_header_feeds.tt2 @@ -3,13 +3,13 @@ [% ELSE %] @@ -18,11 +18,11 @@ title="[% c.config.sitename %] Git repositories list" href="[% c.uri_for('repository_index') %]" type="text/plain; charset=utf-8" - > + > [% END %] diff --git a/root/_heads.tt2 b/root/_heads.tt2 deleted file mode 100644 index 0eb77e7..0000000 --- a/root/_heads.tt2 +++ /dev/null @@ -1,33 +0,0 @@ -
filestatusactionsFileStatusActions
filestatusactions
- [% line.file %] + [% line.file %] [% @@ -32,9 +27,9 @@ %] - [% IF !line.is_new %]diff[% END %] - blob - [% IF !line.is_new %]history[% END %] + [% IF !line.is_new %]diff[% END %] + [% IF !line.is_new %]history[% END %] + blob
- - - - - - - - - - - - - - - - - - - [% FOREACH head IN heads %] - - - - - - - [% END %] - -
HEADagebranchactions
HEADagebranchactions
[% INCLUDE '_chroma_hash.tt2' sha1 = head.sha1.substr(0,7) %][% time_since(head.last_change) %][% head.name %] - shortlog - log - tree -
diff --git a/root/_history.tt2 b/root/_history.tt2 deleted file mode 100644 index 83f09e1..0000000 --- a/root/_history.tt2 +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - [% FOREACH line IN log_lines %] - - - - - - - - [% END %] - -
sha1timeauthormessageactions
sha1timeauthormessageactions
[% INCLUDE '_chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %][% time_since(line.authored_time) %][% line.author.name | html %] - [% short_cmt(line.comment) | html %] - [% INCLUDE '_refs.tt2' object = line.0 %] - - [% IF filetype == 'tree' %] - tree - [% ELSIF filetype == 'blob' %] - blob - [% END %] - commitdiff - [% IF filetype == 'blob' %] - diff to current - [% END %] -
diff --git a/root/_log_pager.tt2 b/root/_log_pager.tt2 index c332d35..e98f0ee 100644 --- a/root/_log_pager.tt2 +++ b/root/_log_pager.tt2 @@ -1,9 +1,9 @@
- HEAD § + HEAD § [% IF log_lines.first.sha1 != commit.sha1 %] - « prev + « prev [% END %] [% IF log_lines.size == 50 %] - next » + next » [% END %]
diff --git a/root/_refs.tt2 b/root/_refs.tt2 index 71bb0ed..76625af 100644 --- a/root/_refs.tt2 +++ b/root/_refs.tt2 @@ -1,7 +1,7 @@ [% FOREACH ref IN refs.${object.sha1} %] - [% ref.replace('^(remote|head)s/', '') %] + [% ref.replace('^(remote|head)s/', '') %] [% END %] diff --git a/root/_shortlog.tt2 b/root/_shortlog.tt2 deleted file mode 100644 index 19caaf1..0000000 --- a/root/_shortlog.tt2 +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - [% FOREACH line IN log_lines %] - - - - - - - - [% END %] - -
sha1timeauthormessageactions
sha1timeauthormessageactions
[% INCLUDE '_chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %][% time_since(line.authored_time) %][% line.author.name | html %] - [% short_cmt(line.comment) | html %] - [% INCLUDE '_refs.tt2' object = line %] - - commit - commitdiff - tree -
diff --git a/root/_tree.tt2 b/root/_tree.tt2 deleted file mode 100644 index 6a96cda..0000000 --- a/root/_tree.tt2 +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - [% FOREACH item IN tree_list %] - - - [% theact = item.type == 'tree' ? 'tree' : 'blob' -%] - [% fullpath = path ? path _ '/' _ item.file : item.file %] - - - - [% END %] - -
modefileactions
modefileactions
[% item.modestr %] - [% item.file %] - - [% theact %] - [% IF item.type == 'blob' %] - blame - [% END %] - history - [% IF item.type == 'blob' %] - raw - [% END %] -
diff --git a/root/blame.tt2 b/root/blame.tt2 deleted file mode 100644 index 19d3e61..0000000 --- a/root/blame.tt2 +++ /dev/null @@ -1,59 +0,0 @@ -[% PROCESS 'nav/actions.tt2' object = head %] -[% INCLUDE inc/syntax_highlight_css.tt2 %] - -
- -[% IF object.type == 'commit' %] -
[% short_cmt(head.comment) %]
-[% END %] - -[% INCLUDE 'nav/path.tt2' %] - -
- - - - - - - - - - - - - - - - - - - - - - - [% blame_lines = blob.split("\n") %] - [% FOR info IN blame %] - - [%- - linecolour = info.commit.sha1.substr(0,6); - IF info.commit.sha1 != lastsha1; - styleinfo = " style='border-top: solid 3px #" _ linecolour _ ";'"; -%] - - - - [%- ELSE -%] - - - - [%- END -%] - - - [% lastsha1 = info.commit.sha1 %] - - [% END %] - -
authordatesha1data
authordatesha1data
[% info.commit.author %][% info.commit.author_dt.ymd %][% linecolour %][% info.meta.lineno %]
[% blame_lines.${loop.index} %]
-
- -
diff --git a/root/blob.tt2 b/root/blob.tt2 deleted file mode 100644 index 06077b2..0000000 --- a/root/blob.tt2 +++ /dev/null @@ -1,14 +0,0 @@ -[% INCLUDE inc/syntax_highlight_css.tt2 %] -[% PROCESS 'nav/actions.tt2' object = head %] - -
- [% IF object.type == 'commit' %] -
- [% short_cmt(head.comment) %] -
- [% END %] - [% INCLUDE 'nav/path.tt2' %] -
-
[% blob %]
-
-
diff --git a/root/blobdiff.tt2 b/root/blobdiff.tt2 deleted file mode 100644 index b771f74..0000000 --- a/root/blobdiff.tt2 +++ /dev/null @@ -1,9 +0,0 @@ -[% PROCESS 'nav/actions.tt2' object = commit %] - -
-
- [% short_cmt(commit.comment) | html %] ... -
- - [% INCLUDE '_diff.tt2' %] -
diff --git a/root/blobdiff_plain.tt2 b/root/blobdiff_plain.tt2 deleted file mode 100644 index 4b771f4..0000000 --- a/root/blobdiff_plain.tt2 +++ /dev/null @@ -1 +0,0 @@ -[%- INCLUDE '_diff_plain.tt2' -%] diff --git a/root/commit.tt2 b/root/commit.tt2 deleted file mode 100644 index 408eedc..0000000 --- a/root/commit.tt2 +++ /dev/null @@ -1,43 +0,0 @@ -[% INCLUDE 'nav/actions.tt2' object = commit %] - -
-
- [% - short_cmt(commit.comment) | html; - INCLUDE '_refs.tt2' object = commit; - %] -
- -
-
author
-
[% commit.author.name | html %] <[% commit.author.email %]>
- [% commit.authored_time %]
-
committer
-
[% commit.committer.name %] <[% commit.committer.email %]>
- [% commit.committed_time %]
-
commit
-
[% commit.sha1 %]
-
tree
-
[% commit.tree_sha1 %] - tree -
- [% FOREACH parent IN commit.parents %] -
parent
-
[% parent.sha1 %] - - commit - diff - -
- [% END %] -
- -
[% commit.comment | html%]
- - [% - # In the case of merge commits there will be no diff tree. - IF diff_tree.size > 0; - INCLUDE '_diff_tree.tt2'; - END; - %] -
diff --git a/root/commitdiff.tt2 b/root/commitdiff.tt2 deleted file mode 100644 index 9db50ec..0000000 --- a/root/commitdiff.tt2 +++ /dev/null @@ -1,33 +0,0 @@ -[% PROCESS 'nav/actions.tt2' object = commit %] - -
-
- [% # XXX Wah, stuff like this doesn't work because end() isn't called as we forward to View::SyntaxHighlight - short_cmt(commit.comment) | html %] -
- -
- [% commit.author.name | html %] [[% commit.authored_time %]] -
- - - [% - # In the case of merge commits there will be no diff tree. - IF diff_tree.size > 0; - INCLUDE '_diff_tree.tt2'; - END; - IF diff.size > 0; - INCLUDE '_diff.tt2'; - ELSE - %] -
- [% - IF commit.parents > 1; - 'Trivial merge'; - ELSE; - 'No differences found'; - END; - %] -
- [% END %] -
diff --git a/root/commitdiff_plain.tt2 b/root/commitdiff_plain.tt2 deleted file mode 100644 index 4b771f4..0000000 --- a/root/commitdiff_plain.tt2 +++ /dev/null @@ -1 +0,0 @@ -[%- INCLUDE '_diff_plain.tt2' -%] diff --git a/root/default.tt2 b/root/default.tt2 deleted file mode 100644 index d38cdcb..0000000 --- a/root/default.tt2 +++ /dev/null @@ -1,75 +0,0 @@ -[%- IF no_wrapper || template.name.match('\.(css|js|txt)'); content; ELSE; -%] - - - - - - - - [%- - title = BLOCK; - c.config.sitename; - IF Repository; ' - ' _ Repository.name | html; END; - IF action; ' / ' _ action; END; - IF filename; ' - ' _ filename | html; END; - IF action && action == 'tree'; '/'; END; - END; - title; - -%] (Gitalist) - [% INCLUDE '_header_feeds.tt2' %] - - - - - - - - - -
- -[% site_header %] - - - -
-[% - IF action; - SET actmpl = action _ ".tt2"; - INCLUDE $actmpl; - ELSE; - # The output of gitweb.cgi is injected at this point. - content; - END; -%] -
- - - -
- - - -[%- END -%] diff --git a/root/favicon.ico b/root/favicon.ico old mode 100644 new mode 100755 index 5ad723d..fea2fec Binary files a/root/favicon.ico and b/root/favicon.ico differ diff --git a/root/favicon.png b/root/favicon.png deleted file mode 100644 index de637c0..0000000 Binary files a/root/favicon.png and /dev/null differ diff --git a/root/fragment/collectionofrepositories.tt2 b/root/fragment/collectionofrepositories.tt2 new file mode 100755 index 0000000..2b153d3 --- /dev/null +++ b/root/fragment/collectionofrepositories.tt2 @@ -0,0 +1,17 @@ + +[% FOR p IN repositories %] + [%- repos_link = c.uri_for_action('/repository/summary', [p.name]) -%] + + [% loop.count %] + [% p.name %] +
[% abridged_description(p.description) IF p.description != "Unnamed repository; edit this file to name it for gitweb." %]
+ [% time_since(p.last_change) %] + [% p.owner %] + + short log + long log + tree + + +[% END %] + diff --git a/root/fragment/ref/blame.tt2 b/root/fragment/ref/blame.tt2 new file mode 100755 index 0000000..af465d4 --- /dev/null +++ b/root/fragment/ref/blame.tt2 @@ -0,0 +1,21 @@ +[% blame_lines = blob.split("\n") %] + [% FOR info IN blame %] + + [%- + linecolour = info.commit.sha1.substr(0,6); + IF info.commit.sha1 != lastsha1; + styleinfo = " style='border-top: solid 3px #" _ linecolour _ ";'"; -%] + [% info.commit.author %] + [% info.commit.author_dt.ymd %] + [% INCLUDE 'inc/chroma_hash.tt2' sha1 = info.commit.sha1.substr(0, 7) %] +[%# linecolour %] + [%- ELSE -%] + + + + [%- END -%] + [% info.meta.lineno %] +
[% blame_lines.${loop.index} | html %]
+ [% lastsha1 = info.commit.sha1 %] + + [% END %] diff --git a/root/fragment/ref/blob.tt2 b/root/fragment/ref/blob.tt2 new file mode 100644 index 0000000..3fe51e8 --- /dev/null +++ b/root/fragment/ref/blob.tt2 @@ -0,0 +1,8 @@ +[%- IF is_image -%] +
+[%- ELSIF is_binary -%] +
This is a binary file which won't render natively on the web, but you can get it here all the same: [% filename %]
+[%- ELSE -%] +[%- INCLUDE inc/syntax_highlight_css.tt2 -%] +
[% IF mangled; blob; ELSE; blob | html; END; %]
+[%- END -%] diff --git a/root/fragment/ref/commit.tt2 b/root/fragment/ref/commit.tt2 new file mode 100755 index 0000000..8560e1f --- /dev/null +++ b/root/fragment/ref/commit.tt2 @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID (sha1)Last changeMessageByRole
[% INCLUDE 'inc/chroma_hash.tt2' sha1 = Commit.sha1.substr(0, 7) %][% time_since(Commit.authored_time) %] + [% Commit.comment | html%] + [% + + INCLUDE '_refs.tt2' object = commit; + %][% Commit.author.name | html %]Author
[% time_since(Commit.committed_time) %][% Commit.committer.name %]Committer
+ + + + + + + + + + + + + + + + [% FOREACH parent IN Commit.parents %] + + + + + +[% END %] +
[% INCLUDE 'inc/chroma_hash.tt2' sha1 = Commit.sha1.substr(0, 7), hide_sha1_output = 1 %]
Commit
[% Commit.sha1 %]diff
[% INCLUDE 'inc/chroma_hash.tt2' sha1 = Commit.tree_sha1.substr(0, 7), hide_sha1_output = 1 %]
Tree
[% Commit.tree_sha1 %]tree
[% INCLUDE 'inc/chroma_hash.tt2' sha1 = parent.sha1.substr(0, 7), hide_sha1_output = 1 %]
Parent
[% parent.sha1 %] + commit + diff +
+ + + + [% + # In the case of merge commits there will be no diff tree. + IF diff_tree && diff_tree.size > 0; + INCLUDE '_diff_tree.tt2'; + END; + %] diff --git a/root/fragment/ref/diff_fancy.tt2 b/root/fragment/ref/diff_fancy.tt2 new file mode 100755 index 0000000..7cba200 --- /dev/null +++ b/root/fragment/ref/diff_fancy.tt2 @@ -0,0 +1,20 @@ +[% + # In the case of merge commits there will be no diff tree. + IF (diff_tree.size||0) > 0; + INCLUDE '_diff_tree.tt2'; + END; + IF (diff.size||0) > 0; + INCLUDE '_diff.tt2'; + ELSE + %] +

+ [% + IF commit && commit.parents > 1; + 'Trivial merge'; + ELSE; + 'No differences found'; + END; + %] +

+ [% END %] + diff --git a/root/fragment/ref/diff_plain.tt2 b/root/fragment/ref/diff_plain.tt2 new file mode 100644 index 0000000..0f58361 --- /dev/null +++ b/root/fragment/ref/diff_plain.tt2 @@ -0,0 +1,5 @@ +[%- FOREACH item IN diff -%] +diff --git [% item.a _ ' ' _ item.b %] +[% item.index %] +[% blobs.${loop.index} %] +[%- END -%] diff --git a/root/fragment/ref/history.tt2 b/root/fragment/ref/history.tt2 new file mode 100755 index 0000000..e20dda7 --- /dev/null +++ b/root/fragment/ref/history.tt2 @@ -0,0 +1,53 @@ +[% BLOCK history_table_headfoot %] +[% SET cell = type == 'head' ? 'th' : 'td' %] + +<[% cell %] colspan="2">Compare + <[% cell %]>sha1 + <[% cell %]>time + <[% cell %]>message + <[% cell %]>author + <[% cell %]>actions + +[% END %] +
+ + [% PROCESS history_table_headfoot type = 'head' %] + [% FOREACH line IN log_lines %] + + + + + + + + + + + [% END %] + + + + + +
[% INCLUDE 'inc/chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %][% time_since(line.authored_time) %] + [% short_cmt(line.comment) | html %] + [% INCLUDE '_refs.tt2' object = line.0 %] + [% line.author.name | html %] + [% IF filetype == 'tree' %] + blob + [% ELSIF filetype == 'blob' %] + blob + [% END %] + commitdiff + [% IF filetype == 'blob' %] + diff to current + [% END %] +
Compare
+
+ +[% filename %] +[% c.uri_for_action('/ref/diff_fancy', [Repository.name, 'HEAD']) %] + +[% INCLUDE 'inc/log_pager.tt2' %] diff --git a/root/fragment/ref/longlog.tt2 b/root/fragment/ref/longlog.tt2 new file mode 100644 index 0000000..5470847 --- /dev/null +++ b/root/fragment/ref/longlog.tt2 @@ -0,0 +1 @@ +[% PROCESS 'fragment/repository/longlog.tt2' %] diff --git a/root/fragment/ref/shortlog.tt2 b/root/fragment/ref/shortlog.tt2 new file mode 100644 index 0000000..0ba3aa1 --- /dev/null +++ b/root/fragment/ref/shortlog.tt2 @@ -0,0 +1 @@ +[% PROCESS 'fragment/repository/shortlog.tt2' %] diff --git a/root/fragment/ref/tree.tt2 b/root/fragment/ref/tree.tt2 new file mode 100755 index 0000000..0ec6c16 --- /dev/null +++ b/root/fragment/ref/tree.tt2 @@ -0,0 +1,60 @@ +[% BLOCK tree_table_headfoot %] +[% SET cell = type == 'head' ? 'th' : 'td' %] + + <[% cell %]>Mode + <[% cell %]>Folder / File + <[% cell %]>Actions + <[% cell %]>Message + +[% END %] +[%- + SET counter = 1; + + # sort files and folders + SET tree_files = []; + SET tree_folders = []; + FOREACH item IN tree_list; + IF item.type == "blob"; + tree_files.push(item); + ELSE; + tree_folders.push(item); + END; + END; +%] + +[% BLOCK output_tree %] + [% FOREACH item IN tree_type.sort('file') %] + + [% item.modestr %] + [%- + action_type = item.type == 'tree' ? 'tree' : 'blob'; + action_for_link = item.type == 'tree' ? '/ref/tree' : '/ref/blob'; + blob_or_tree_link = c.uri_for_action(action_for_link, c.req.captures, c.req.args.to_path(item.file)) + -%] + [% item.file %] + + [% theact %] + [% IF item.type == 'blob' %] + Blob + raw + blame + [% END %] + Short log + + [% c.req.args.to_path(item.file) %]Loading commit info ... + + [% counter = counter + 1 %] + [% END %] +[% END %] + + +[% PROCESS tree_table_headfoot type = 'head' %] + + [% INCLUDE output_tree tree_type => tree_folders %] + [% PROCESS output_tree tree_type => tree_files %] + +
+ +[%- # We use uri_for instead of uri_for_action as we *want* a /fragment URI in this case %] +[% c.uri_for('/fragment/' _ Repository.name _ '/' _ Commit.sha1 _ '/file_commit_info') %] +[% c.uri_for_action('/ref/commit', [Repository.name, 'HEAD']) %] \ No newline at end of file diff --git a/root/fragment/repository/heads.tt2 b/root/fragment/repository/heads.tt2 new file mode 100755 index 0000000..69c5766 --- /dev/null +++ b/root/fragment/repository/heads.tt2 @@ -0,0 +1,27 @@ +[% BLOCK repository_heads_headfoot %] +[% SET cell = type == 'head' ? 'th' : 'td' %] + + <[% cell %]>HEAD + <[% cell %]>Last change + <[% cell %]>Branch + <[% cell %]>Actions + +[% END %] + + + [% PROCESS repository_heads_headfoot type = 'head' %] + + [% FOREACH head IN heads %] + + + + + + + [% END %] + +
[% INCLUDE 'inc/chroma_hash.tt2' sha1 = head.sha1.substr(0,7) %][% time_since(head.last_change) %][% head.name %][%# FIXME %] + shortlog + log + tree +
diff --git a/root/fragment/repository/longlog.tt2 b/root/fragment/repository/longlog.tt2 new file mode 100755 index 0000000..3f11484 --- /dev/null +++ b/root/fragment/repository/longlog.tt2 @@ -0,0 +1,5 @@ +[% INCLUDE 'fragment/repository/shortlog.tt2' longlogformat = 1 %] + + +

Branches

+[% subinclude('/fragment/repository/heads', [Repository.name]) %] diff --git a/root/fragment/repository/shortlog.tt2 b/root/fragment/repository/shortlog.tt2 new file mode 100755 index 0000000..0cde010 --- /dev/null +++ b/root/fragment/repository/shortlog.tt2 @@ -0,0 +1,49 @@ +[% BLOCK shortlog_table_headfoot %] +[% SET cell = type == 'head' ? 'th' : 'td' %] + + <[% cell %] colspan="2">Compare + <[% cell %]>ID (sha1) + <[% cell %]>Last change + <[% cell %]>Message + <[% cell %]>By + <[% cell %]>Actions + +[% END %] + +
+ +[% PROCESS shortlog_table_headfoot type = 'head' %] + + [% FOREACH line IN log_lines %] + + + + + + + + + +[% END %] + + + + + +
[% INCLUDE 'inc/chroma_hash.tt2' sha1 = line.sha1.substr(0, 7) %][% time_since(line.authored_time) %] + [% IF longlogformat %] + [% message = line.comment | html; message.replace("\n", "
") %] + [% ELSE %] + [% short_cmt(line.comment) | html %] + [% INCLUDE '_refs.tt2' object = line %] + [% END %] +
[% line.author.name | html %] + commit + commitdiff + tree +
Compare
+
[% c.uri_for_action('/ref/diff_fancy', [Repository.name, 'HEAD']) %] + +[% INCLUDE 'inc/log_pager.tt2' %] diff --git a/root/fragment/repository/summary.tt2 b/root/fragment/repository/summary.tt2 new file mode 100755 index 0000000..a2a160c --- /dev/null +++ b/root/fragment/repository/summary.tt2 @@ -0,0 +1,5 @@ +
+
description
[% Repository.description %]
+
owner
[% Repository.owner %]
+
last change
[% time_since(Repository.last_change) %]
+
diff --git a/root/fragment/repository/tags.tt2 b/root/fragment/repository/tags.tt2 new file mode 100644 index 0000000..3f47409 --- /dev/null +++ b/root/fragment/repository/tags.tt2 @@ -0,0 +1 @@ +[% INCLUDE 'fragment/repository/heads.tt2' heads = tags %] diff --git a/root/git-logo.png b/root/git-logo.png deleted file mode 100644 index 16ae8d5..0000000 Binary files a/root/git-logo.png and /dev/null differ diff --git a/root/gitweb.css b/root/gitweb.css deleted file mode 100644 index e5473aa..0000000 --- a/root/gitweb.css +++ /dev/null @@ -1,501 +0,0 @@ -body { - font-family: sans-serif; - font-size: small; - border: solid #d9d8d1; - border-width: 1px; - margin: 10px; - background-color: #ffffff; - color: #000000; -} - -a { - color: #0000cc; -} - -a:hover, a:visited, a:active { - color: #880000; -} - -span.cntrl { - border: dashed #aaaaaa; - border-width: 1px; - padding: 0px 2px 0px 2px; - margin: 0px 2px 0px 2px; -} - -img.logo { - float: right; - border-width: 0px; -} - -div.page_header { - height: 25px; - padding: 8px; - font-size: 150%; - font-weight: bold; - background-color: #d9d8d1; -} - -div.page_header a:visited, a.header { - color: #0000cc; -} - -div.page_header a:hover { - color: #880000; -} - -div.page_nav { - padding: 8px; -} - -div.page_nav a:visited { - color: #0000cc; -} - -div.page_path { - padding: 8px; - font-weight: bold; - border: solid #d9d8d1; - border-width: 0px 0px 1px; -} - -div.page_footer { - height: 17px; - padding: 4px 8px; - background-color: #d9d8d1; -} - -div.page_footer_text { - float: left; - color: #555555; - font-style: italic; -} - -div.page_body { - padding: 8px; - font-family: monospace; -} - -div.title, a.title { - display: block; - padding: 6px 8px; - font-weight: bold; - background-color: #edece6; - text-decoration: none; - color: #000000; -} - -div.readme { - padding: 8px; -} - -a.title:hover { - background-color: #d9d8d1; -} - -div.title_text { - padding: 6px 0px; - border: solid #d9d8d1; - border-width: 0px 0px 1px; - font-family: monospace; -} - -div.log_body { - padding: 8px 8px 8px 150px; -} - -span.age { - position: relative; - float: left; - width: 142px; - font-style: italic; -} - -span.signoff { - color: #888888; -} - -div.log_link { - padding: 0px 8px; - font-size: 70%; - font-family: sans-serif; - font-style: normal; - position: relative; - float: left; - width: 136px; -} - -div.list_head { - padding: 6px 8px 4px; - border: solid #d9d8d1; - border-width: 1px 0px 0px; - font-style: italic; -} - -div.author_date { - padding: 8px; - border: solid #d9d8d1; - border-width: 0px 0px 1px 0px; - font-style: italic; -} - -a.list { - text-decoration: none; - color: #000000; -} - -a.subject, a.name { - font-weight: bold; -} - -table.tags a.subject { - font-weight: normal; -} - -a.list:hover { - text-decoration: underline; - color: #880000; -} - -a.text { - text-decoration: none; - color: #0000cc; -} - -a.text:visited { - text-decoration: none; - color: #880000; -} - -a.text:hover { - text-decoration: underline; - color: #880000; -} - -table { - padding: 8px 4px; - border-spacing: 0; -} - -table.diff_tree { - font-family: monospace; -} - -table.combined.diff_tree th { - text-align: center; -} - -table.combined.diff_tree td { - padding-right: 24px; -} - -table.combined.diff_tree th.link, -table.combined.diff_tree td.link { - padding: 0px 2px; -} - -table.combined.diff_tree td.nochange a { - color: #6666ff; -} - -table.combined.diff_tree td.nochange a:hover, -table.combined.diff_tree td.nochange a:visited { - color: #d06666; -} - -table.blame { - border-collapse: collapse; -} - -table.blame td { - padding: 0px 5px; - font-size: 100%; - vertical-align: top; -} - -th { - padding: 2px 5px; - font-size: 100%; - text-align: left; -} - -tr.light:hover { - background-color: #edece6; -} - -tr.dark { - background-color: #f6f6f0; -} - -tr.dark2 { - background-color: #f6f6f0; -} - -tr.dark:hover { - background-color: #edece6; -} - -td { - padding: 2px 5px; - font-size: 100%; - vertical-align: top; -} - -td.link, td.selflink { - padding: 2px 5px; - font-family: sans-serif; - font-size: 70%; -} - -td.selflink { - padding-right: 0px; -} - -td.sha1 { - font-family: monospace; -} - -td.error { - color: red; - background-color: yellow; -} - -td.current_head { - text-decoration: underline; -} - -table.diff_tree span.file_status.new { - color: #008000; -} - -table.diff_tree span.file_status.deleted { - color: #c00000; -} - -table.diff_tree span.file_status.moved, -table.diff_tree span.file_status.mode_chnge { - color: #777777; -} - -table.diff_tree span.file_status.copied { - color: #70a070; -} - -/* noage: "No commits" */ -table.repository_list td.noage { - color: #808080; - font-style: italic; -} - -/* age2: 60*60*24*2 <= age */ -table.repository_list td.age2, table.blame td.age2 { - font-style: italic; -} - -/* age1: 60*60*2 <= age < 60*60*24*2 */ -table.repository_list td.age1 { - color: #009900; - font-style: italic; -} - -table.blame td.age1 { - color: #009900; - background: transparent; -} - -/* age0: age < 60*60*2 */ -table.repository_list td.age0 { - color: #009900; - font-style: italic; - font-weight: bold; -} - -table.blame td.age0 { - color: #009900; - background: transparent; - font-weight: bold; -} - -td.pre, div.pre, div.diff { - font-family: monospace; - font-size: 12px; - white-space: pre; -} - -td.mode { - font-family: monospace; -} - -/* styling of diffs (patchsets): commitdiff and blobdiff views */ -div.diff.header, -div.diff.extended_header { - white-space: normal; -} - -div.diff.header { - font-weight: bold; - - background-color: #edece6; - - margin-top: 4px; - padding: 4px 0px 2px 0px; - border: solid #d9d8d1; - border-width: 1px 0px 1px 0px; -} - -div.diff.header a.path { - text-decoration: underline; -} - -div.diff.extended_header, -div.diff.extended_header a.path, -div.diff.extended_header a.hash { - color: #777777; -} - -div.diff.extended_header .info { - color: #b0b0b0; -} - -div.diff.extended_header { - background-color: #f6f5ee; - padding: 2px 0px 2px 0px; -} - -div.diff a.list, -div.diff a.path, -div.diff a.hash { - text-decoration: none; -} - -div.diff a.list:hover, -div.diff a.path:hover, -div.diff a.hash:hover { - text-decoration: underline; -} - -div.diff.to_file a.path, -div.diff.to_file { - color: #007000; -} - -div.diff.add { - color: #008800; -} - -div.diff.from_file a.path, -div.diff.from_file { - color: #aa0000; -} - -div.diff.rem { - color: #cc0000; -} - -div.diff.chunk_header a, -div.diff.chunk_header { - color: #990099; -} - -div.diff.chunk_header { - border: dotted #ffe0ff; - border-width: 1px 0px 0px 0px; - margin-top: 2px; -} - -div.diff.chunk_header span.chunk_info { - background-color: #ffeeff; -} - -div.diff.chunk_header span.section { - color: #aa22aa; -} - -div.diff.incomplete { - color: #cccccc; -} - -div.diff.nodifferences { - font-weight: bold; - color: #600000; -} - -div.index_include { - border: solid #d9d8d1; - border-width: 0px 0px 1px; - padding: 12px 8px; -} - -div.search { - font-size: 100%; - font-weight: normal; - margin: 4px 8px; - float: right; - top: 56px; - right: 12px -} - -td.linenr { - text-align: right; -} - -a.linenr { - color: #999999; - text-decoration: none -} - -a.rss_logo { - float: right; - padding: 3px 0px; - width: 35px; - line-height: 10px; - border: 1px solid; - border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e; - color: #ffffff; - background-color: #ff6600; - font-weight: bold; - font-family: sans-serif; - font-size: 70%; - text-align: center; - text-decoration: none; -} - -a.rss_logo:hover { - background-color: #ee5500; -} - -span.refs span { - padding: 0px 4px; - font-size: 70%; - font-weight: normal; - border: 1px solid; - background-color: #ffaaff; - border-color: #ffccff #ff00ee #ff00ee #ffccff; -} - -span.refs span.ref { - background-color: #aaaaff; - border-color: #ccccff #0033cc #0033cc #ccccff; -} - -span.refs span.tag { - background-color: #ffffaa; - border-color: #ffffcc #ffee00 #ffee00 #ffffcc; -} - -span.refs span.head { - background-color: #aaffaa; - border-color: #ccffcc #00cc33 #00cc33 #ccffcc; -} - -span.atnight { - color: #cc0000; -} - -span.match { - color: #e00000; -} - -div.binary { - font-style: italic; -} diff --git a/root/gitweb.tt2 b/root/gitweb.tt2 deleted file mode 100644 index dd1a5ed..0000000 --- a/root/gitweb.tt2 +++ /dev/null @@ -1 +0,0 @@ -[% gitweb_output %] diff --git a/root/heads.tt2 b/root/heads.tt2 deleted file mode 100644 index baa8e4b..0000000 --- a/root/heads.tt2 +++ /dev/null @@ -1,9 +0,0 @@ -[% INCLUDE 'nav/actions.tt2' object = commit %] - -
-
- [% Repository.name %] -
- - [% INCLUDE '_heads.tt2' %] -
diff --git a/root/history.tt2 b/root/history.tt2 deleted file mode 100644 index babb960..0000000 --- a/root/history.tt2 +++ /dev/null @@ -1,7 +0,0 @@ -[% INCLUDE 'nav/actions.tt2' object = commit %] - -[% - INCLUDE '_log_pager.tt2'; - INCLUDE '_history.tt2'; - INCLUDE '_log_pager.tt2'; -%] diff --git a/root/inc/chroma_hash.tt2 b/root/inc/chroma_hash.tt2 new file mode 100755 index 0000000..b07a1ea --- /dev/null +++ b/root/inc/chroma_hash.tt2 @@ -0,0 +1,13 @@ +[%- sha1 = sha1 || HEAD -%] +[%- + hptr = 0; + WHILE hptr < sha1.length - 6; + sha1part = sha1.substr(hptr, 6); + hptr = hptr + 6; + END; +-%] + +
+[% IF !hide_sha1_output %] +
[% sha1part %]
+[% END %] diff --git a/root/_footer_feeds.tt2 b/root/inc/footer_feeds.tt2 similarity index 74% rename from root/_footer_feeds.tt2 rename to root/inc/footer_feeds.tt2 index 3e99e51..2d598f9 100644 --- a/root/_footer_feeds.tt2 +++ b/root/inc/footer_feeds.tt2 @@ -17,16 +17,16 @@ + href="[% c.uri_for_action('/repository/rss', [Repository.name]) %]">RSS + href="[% c.uri_for_action('/repository/atom', [Repository.name]) %]">Atom [% ELSE %] + href="[% c.uri_for_action('/opml/opml') %]">OPML + href="[% c.uri_for('/project_index') %]">TXT [% END %] diff --git a/root/inc/log_pager.tt2 b/root/inc/log_pager.tt2 new file mode 100755 index 0000000..e58aaab --- /dev/null +++ b/root/inc/log_pager.tt2 @@ -0,0 +1,11 @@ +
    + [% IF log_lines.first.sha1 != Commit.sha1 || (log_lines.size != 25 && page) %] +
  • Newer commits
  • + [% END %] + + [% IF log_lines.first.sha1 != Commit.sha1 && log_lines.size == 25 %] [% END %] + + [% IF log_lines.size == 25 %] +
  • Older commits
  • + [% END %] +
diff --git a/root/inc/syntax_highlight_css.tt2 b/root/inc/syntax_highlight_css.tt2 index 5fc9dad..56700a7 100644 --- a/root/inc/syntax_highlight_css.tt2 +++ b/root/inc/syntax_highlight_css.tt2 @@ -1 +1 @@ -[%- IF language %][% END -%] +[%- FOREACH file = syntax_css %][% END -%] diff --git a/root/index.tt2 b/root/index.tt2 old mode 100644 new mode 100755 index 9d1544d..5df3c17 --- a/root/index.tt2 +++ b/root/index.tt2 @@ -1,45 +1,20 @@ -
-
-

Search: - -

-
+[% BLOCK repos_table_headfoot %] +[% SET cell = type == 'head' ? 'th' : 'td' %] + + <[% cell %]> + <[% cell %]>Repository + + <[% cell %]>Description + <[% cell %]>Last change + <[% cell %]>By + <[% cell %]>Actions + +[% END %] - - - - - - - - - - - - - - - - - - - - - +
RepositoryDescriptionOwnerLast Change
RepositoryDescriptionOwnerLast Change
+ + [% INCLUDE repos_table_headfoot type = 'head' %] + +[% subinclude('/fragment/collectionofrepositories') %] +
- - [% FOR p IN repositories %] - - [% p.name %] - [% abridged_description(p.description) %] - [% p.owner %] - [% time_since(p.last_change) %] - summary - | shortlog - | log - | tree - - [% END %] - - -
diff --git a/root/log.tt2 b/root/log.tt2 deleted file mode 100644 index 3e3908a..0000000 --- a/root/log.tt2 +++ /dev/null @@ -1,35 +0,0 @@ -[% INCLUDE 'nav/actions.tt2' object = commit %] - -
- [% INCLUDE '_log_pager.tt2' %] - -
- [% FOREACH line IN log_lines %] -
-
- [% message = line.comment | html; - message.replace("\n", "
") %] -
[% INCLUDE '_chroma_hash.tt2' sha1 = line.sha1 %]
-
-
- - - - [% IF line.author.name != line.committer.name %] - - - [% END %] -
author[% line.author.name | html %]
authored time[% line.authored_time %]
committer[% line.committer.name | html %]
committered time[% line.committed_time %]
- [% time_since(line.authored_time) %] -
- commit - | commitdiff - | tree -
-
-
- [% END %] -
- - [% INCLUDE '_log_pager.tt2' %] -
diff --git a/root/logo.svg b/root/logo.svg deleted file mode 100644 index f0367df..0000000 --- a/root/logo.svg +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - git - - - - - - - - - - - diff --git a/root/nav/actions.tt2 b/root/nav/actions.tt2 index 52ca63e..7845ca9 100644 --- a/root/nav/actions.tt2 +++ b/root/nav/actions.tt2 @@ -1,20 +1,26 @@ +[%- SET object = commit || head -%]
+
+
- summary • - shortlog • - log • - commit • - commitdiff - [% IF object.type == 'commit' %] • - tree + summary • + [% IF c.req.captures.size == 1; SET path = 'repository'; ELSE; SET path = 'ref'; END %] + shortlog • + log + [% IF Commit %] + § + commit • + commitdiff • + tree [% END %] - [% IF filename %] + [% IF filename && c.action != 'ref/tree' %] § - blob • - raw • - blame • - history • - HEAD + blob • + raw • + blame • + history • + HEAD [% END %] -
[% INCLUDE '_chroma_hash.tt2' sha1 = object.sha1 %]
+
+
[% INCLUDE 'inc/chroma_hash.tt2' sha1 = object.sha1 %]
diff --git a/root/nav/path.tt2 b/root/nav/path.tt2 old mode 100644 new mode 100755 index eef339c..3567c09 --- a/root/nav/path.tt2 +++ b/root/nav/path.tt2 @@ -1,7 +1,6 @@ -
- [% Repository.name %] + [% Repository.name %] (tree) [% FOREACH part IN filename.split('/') %] [% path = loop.first ? part : path _ '/' _ part %] - / [% part %] + / [% part %] [% END %] -
+ diff --git a/root/nav/search.tt2 b/root/nav/search.tt2 old mode 100644 new mode 100755 index 35c3d88..9721094 --- a/root/nav/search.tt2 +++ b/root/nav/search.tt2 @@ -1,9 +1,9 @@ +[% IF Repository %] +[% END %] diff --git a/root/opml.tt2 b/root/opml.tt2 new file mode 100644 index 0000000..35f22e3 --- /dev/null +++ b/root/opml.tt2 @@ -0,0 +1,19 @@ + + + [% count = 1 %] + [% FOR Repository = Repositories %] + + [% SET count = count + 1; END %] + + + [% now %] + [% now %] + [% title | html_entity %] + + diff --git a/root/ref/blame.tt2 b/root/ref/blame.tt2 new file mode 100755 index 0000000..f943636 --- /dev/null +++ b/root/ref/blame.tt2 @@ -0,0 +1,37 @@ +[%- BLOCK blame_table_headfoot %] + + Author + Date + ID (sha1) + + Data + +[% END -%] + +[%- INCLUDE inc/syntax_highlight_css.tt2 -%] + + + + + + + +
+ + + [% PROCESS blame_table_headfoot %] + + + + [% subinclude('/fragment/ref/blame', c.req.captures, c.req.arguments.to_path ) %] + +
+
+ +

[% INCLUDE 'nav/path.tt2' %]

+ + +[% IF object.type == 'commit' %] +
[% short_cmt(head.comment) %]
+[% END %] + diff --git a/root/ref/blob.tt2 b/root/ref/blob.tt2 new file mode 100755 index 0000000..92fbf03 --- /dev/null +++ b/root/ref/blob.tt2 @@ -0,0 +1,12 @@ +

[% INCLUDE 'nav/path.tt2' %]

+ + [% IF object.type == 'commit' %] +
+ [% short_cmt(head.comment) %] +
+ [% END %] + +[% subinclude('/fragment/ref/blob', c.req.captures, c.req.args.to_path) %] + + + \ No newline at end of file diff --git a/root/ref/commit.tt2 b/root/ref/commit.tt2 new file mode 100644 index 0000000..2d79fa7 --- /dev/null +++ b/root/ref/commit.tt2 @@ -0,0 +1,4 @@ + +
+ [% subinclude('/fragment/ref/commit', c.req.captures) %] +
diff --git a/root/ref/diff_fancy.tt2 b/root/ref/diff_fancy.tt2 new file mode 100755 index 0000000..312c097 --- /dev/null +++ b/root/ref/diff_fancy.tt2 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + +
ID (sha1)Last changeMessageBy
[% INCLUDE 'inc/chroma_hash.tt2' sha1 = Commit.sha1.substr(0, 7) %][% time_since(Commit.authored_time) %][% short_cmt(Commit.comment) | html %][% Commit.author.name | html %]
+ + + [% + # What I really want is @{ c.req.args } + subinclude('/fragment/' _ c.action, c.req.captures, c.req.args.0 c.req.args.1) + %] diff --git a/root/ref/diff_plain.tt2 b/root/ref/diff_plain.tt2 new file mode 100644 index 0000000..0f98a04 --- /dev/null +++ b/root/ref/diff_plain.tt2 @@ -0,0 +1,2 @@ +[% subinclude('/fragment/' _ c.action, c.req.captures, c.req.args.0 c.req.args.1) %] + diff --git a/root/ref/history.tt2 b/root/ref/history.tt2 new file mode 100644 index 0000000..5820d4d --- /dev/null +++ b/root/ref/history.tt2 @@ -0,0 +1,2 @@ +[%# FIXME - Pager links are broken because I can't figure out how to pass the parameters %] +[% subinclude('/fragment/ref/history', c.req.captures, c.req.args.to_path) %] diff --git a/root/ref/longlog.tt2 b/root/ref/longlog.tt2 new file mode 100644 index 0000000..e927a16 --- /dev/null +++ b/root/ref/longlog.tt2 @@ -0,0 +1 @@ +[% PROCESS 'repository/longlog.tt2' %] diff --git a/root/ref/shortlog.tt2 b/root/ref/shortlog.tt2 new file mode 100644 index 0000000..4175b41 --- /dev/null +++ b/root/ref/shortlog.tt2 @@ -0,0 +1 @@ +[% PROCESS 'repository/shortlog.tt2' %] diff --git a/root/ref/tree.tt2 b/root/ref/tree.tt2 new file mode 100755 index 0000000..6d76c32 --- /dev/null +++ b/root/ref/tree.tt2 @@ -0,0 +1 @@ +[% subinclude('/fragment/ref/tree', c.req.captures, c.req.args.to_path) %] diff --git a/root/repository/atom.tt2 b/root/repository/atom.tt2 new file mode 100644 index 0000000..9f721ab --- /dev/null +++ b/root/repository/atom.tt2 @@ -0,0 +1,15 @@ + + + [% title | html_entity %] + [% updated %] + [% FOREACH Commit = Commits %] + + [% Commit.title | html_entity %] + [% Commit.id %] + + +
[% Commit.content | html_entity %]
+
+
+ [% END %] +
diff --git a/root/repository/heads.tt2 b/root/repository/heads.tt2 new file mode 100644 index 0000000..1d8d910 --- /dev/null +++ b/root/repository/heads.tt2 @@ -0,0 +1 @@ +[% subinclude('/fragment/repository/heads', c.req.captures) %] diff --git a/root/repository/longlog.tt2 b/root/repository/longlog.tt2 new file mode 100644 index 0000000..a0c9f4b --- /dev/null +++ b/root/repository/longlog.tt2 @@ -0,0 +1 @@ +[% subinclude('/fragment/' _ c.action, c.req.captures, c.req.parameters) %] diff --git a/root/reflog.tt2 b/root/repository/reflog.tt2 similarity index 100% rename from root/reflog.tt2 rename to root/repository/reflog.tt2 diff --git a/root/repository/rss.tt2 b/root/repository/rss.tt2 new file mode 100644 index 0000000..bd2c766 --- /dev/null +++ b/root/repository/rss.tt2 @@ -0,0 +1,19 @@ + + + + +[% title | html_entity %] +[% c.uri_for_action('/repository/summary', [Repository.name]) %] +[% Repository.description | html_entity %] +[% lang %] +[% pubDate %] +[% lastBuildDate %] +[% FOREACH Commit = Commits %] + +[% Commit.title | html_entity %] +[% Commit.description | html_entity %] +[% Commit.permaLink %] + +[% END %] + + diff --git a/root/repository/search.tt2 b/root/repository/search.tt2 new file mode 100755 index 0000000..901d4d6 --- /dev/null +++ b/root/repository/search.tt2 @@ -0,0 +1,35 @@ +[% BLOCK shortlog_table_headfoot %] +[% SET cell = type == 'head' ? 'th' : 'td' %] + + <[% cell %]>ID (sha1) + <[% cell %]>Last change + <[% cell %]>Message + <[% cell %]>By + <[% cell %]>Actions + +[% END %] + + +[% PROCESS shortlog_table_headfoot type = 'head' %] + +[% FOREACH result IN results %] + + + + + + + +[% END %] + +
[% INCLUDE 'inc/chroma_hash.tt2' sha1 = result.sha1.substr(0, 7) %][% time_since(result.authored_time) %][% + # XXX This is fragile at best. + html_comment = result.comment | html; + html_comment.replace( + c.req.param('text'), '' _ c.req.param('text') _ '' + ); + %][% result.author.name | html %] + commit + commitdiff + tree +
\ No newline at end of file diff --git a/root/repository/shortlog.tt2 b/root/repository/shortlog.tt2 new file mode 100755 index 0000000..90f7ea4 --- /dev/null +++ b/root/repository/shortlog.tt2 @@ -0,0 +1,2 @@ +[% subinclude('/fragment/' _ c.action, c.req.captures, c.req.parameters) %] + diff --git a/root/repository/summary.tt2 b/root/repository/summary.tt2 new file mode 100755 index 0000000..a0646e8 --- /dev/null +++ b/root/repository/summary.tt2 @@ -0,0 +1,15 @@ + + + + [% subinclude('/fragment/repository/shortlog', c.req.captures) %] + +

Branches

+ [% subinclude('/fragment/repository/heads', c.req.captures) %] + + [% IF Repository.tags.size > 0 %] +

Tags

+ [% subinclude('/fragment/repository/tags', c.req.captures) %] + [% END %] + diff --git a/root/repository/tags.tt2 b/root/repository/tags.tt2 new file mode 100755 index 0000000..3518081 --- /dev/null +++ b/root/repository/tags.tt2 @@ -0,0 +1,7 @@ +
+
+ [% Repository.name %] +
+ + [% subinclude('/fragment/repository/tags', c.req.captures) %] +
diff --git a/root/search.tt2 b/root/search.tt2 old mode 100644 new mode 100755 index 2504b82..06d845c --- a/root/search.tt2 +++ b/root/search.tt2 @@ -1,35 +1 @@ -[% INCLUDE 'nav/actions.tt2' object = commit %] -
- [%# INCLUDE '_log_pager.tt2' %] - - [%# XXX Nabbed the HTML below from gitweb's log action. %] - [% FOREACH result IN results %] - - -
- - [% result.author.name | html %] [% line.authored_time %] -
- -
- [% - # XXX This is fragile at best. - html_comment = result.comment | html; - html_comment.replace( - c.req.param('text'), '' _ c.req.param('text') _ '' - ); - %] -
- [% END %] - - [%# INCLUDE '_log_pager.tt2' %] -
+search diff --git a/root/search_help.tt2 b/root/search_help.tt2 old mode 100644 new mode 100755 index c23a414..86e8154 --- a/root/search_help.tt2 +++ b/root/search_help.tt2 @@ -1,10 +1,12 @@ -[% PROCESS 'nav/actions.tt2' object = head %] +

Search help

+

Pattern is by default a normal string that is matched precisely (but without regard to case, except in the case of pickaxe). However, when you check the re checkbox, the pattern entered is recognized as the POSIX extended regular expression (also case insensitive).

+
commit
The commit messages and authorship information will be scanned for the given pattern.
@@ -25,4 +27,4 @@ added, removed or "modified" the string) will be listed. This search can take a takes a lot of strain on the server, so please use it wisely. Note that since you may be interested even in changes just changing the case as well, this search is case sensitive.
- +
diff --git a/root/shortlog.tt2 b/root/shortlog.tt2 deleted file mode 100644 index 6f9636e..0000000 --- a/root/shortlog.tt2 +++ /dev/null @@ -1,9 +0,0 @@ -[% INCLUDE 'nav/actions.tt2' object = commit %] - -
-[% - INCLUDE '_log_pager.tt2'; - INCLUDE '_shortlog.tt2'; - INCLUDE '_log_pager.tt2'; -%] -
diff --git a/root/static/css/blueprint/ie.css b/root/static/css/blueprint/ie.css deleted file mode 100644 index f336f0e..0000000 --- a/root/static/css/blueprint/ie.css +++ /dev/null @@ -1,35 +0,0 @@ -/* ----------------------------------------------------------------------- - - - Blueprint CSS Framework 0.9 - http://blueprintcss.org - - * Copyright (c) 2007-Present. See LICENSE for more info. - * See README for instructions on how to use Blueprint. - * For credits and origins, see AUTHORS. - * This is a compressed file. See the sources in the 'src' directory. - ------------------------------------------------------------------------ */ - -/* ie.css */ -body {text-align:center;} -.container {text-align:left;} -* html .column, * html div.span-1, * html div.span-2, * html div.span-3, * html div.span-4, * html div.span-5, * html div.span-6, * html div.span-7, * html div.span-8, * html div.span-9, * html div.span-10, * html div.span-11, * html div.span-12, * html div.span-13, * html div.span-14, * html div.span-15, * html div.span-16, * html div.span-17, * html div.span-18, * html div.span-19, * html div.span-20, * html div.span-21, * html div.span-22, * html div.span-23, * html div.span-24 {display:inline;overflow-x:hidden;} -* html legend {margin:0px -8px 16px 0;padding:0;} -sup {vertical-align:text-top;} -sub {vertical-align:text-bottom;} -html>body p code {*white-space:normal;} -hr {margin:-8px auto 11px;} -img {-ms-interpolation-mode:bicubic;} -.clearfix, .container {display:inline-block;} -* html .clearfix, * html .container {height:1%;} -fieldset {padding-top:0;} -textarea {overflow:auto;} -input.text, input.title, textarea {background-color:#fff;border:1px solid #bbb;} -input.text:focus, input.title:focus {border-color:#666;} -input.text, input.title, textarea, select {margin:0.5em 0;} -input.checkbox, input.radio {position:relative;top:.25em;} -form.inline div, form.inline p {vertical-align:middle;} -form.inline label {position:relative;top:-0.25em;} -form.inline input.checkbox, form.inline input.radio, form.inline input.button, form.inline button {margin:0.5em 0;} -button, input.button {position:relative;top:0.25em;} \ No newline at end of file diff --git a/root/static/css/blueprint/print.css b/root/static/css/blueprint/print.css deleted file mode 100644 index fdb8220..0000000 --- a/root/static/css/blueprint/print.css +++ /dev/null @@ -1,29 +0,0 @@ -/* ----------------------------------------------------------------------- - - - Blueprint CSS Framework 0.9 - http://blueprintcss.org - - * Copyright (c) 2007-Present. See LICENSE for more info. - * See README for instructions on how to use Blueprint. - * For credits and origins, see AUTHORS. - * This is a compressed file. See the sources in the 'src' directory. - ------------------------------------------------------------------------ */ - -/* print.css */ -body {line-height:1.5;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;color:#000;background:none;font-size:10pt;} -.container {background:none;} -hr {background:#ccc;color:#ccc;width:100%;height:2px;margin:2em 0;padding:0;border:none;} -hr.space {background:#fff;color:#fff;visibility:hidden;} -h1, h2, h3, h4, h5, h6 {font-family:"Helvetica Neue", Arial, "Lucida Grande", sans-serif;} -code {font:.9em "Courier New", Monaco, Courier, monospace;} -a img {border:none;} -p img.top {margin-top:0;} -blockquote {margin:1.5em;padding:1em;font-style:italic;font-size:.9em;} -.small {font-size:.9em;} -.large {font-size:1.1em;} -.quiet {color:#999;} -.hide {display:none;} -a:link, a:visited {background:transparent;font-weight:700;text-decoration:underline;} -a:link:after, a:visited:after {content:" (" attr(href) ")";font-size:90%;} \ No newline at end of file diff --git a/root/static/css/blueprint/screen.css b/root/static/css/blueprint/screen.css deleted file mode 100644 index 8d05e3c..0000000 --- a/root/static/css/blueprint/screen.css +++ /dev/null @@ -1,254 +0,0 @@ -/* ----------------------------------------------------------------------- - - - Blueprint CSS Framework 0.9 - http://blueprintcss.org - - * Copyright (c) 2007-Present. See LICENSE for more info. - * See README for instructions on how to use Blueprint. - * For credits and origins, see AUTHORS. - * This is a compressed file. See the sources in the 'src' directory. - ------------------------------------------------------------------------ */ - -/* reset.css */ -html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;} -body {line-height:1.5;} -table {border-collapse:separate;border-spacing:0;} -caption, th, td {text-align:left;} -table, td, th {vertical-align:middle;} -blockquote:before, blockquote:after, q:before, q:after {content:"";} -blockquote, q {quotes:"" "";} -a img {border:none;} - -/* typography.css */ -html {font-size:100.01%;} -body {font-size:85%;color:#222;background:#fff;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;} -h1, h2, h3, h4, h5, h6 {font-weight:normal;color:#111;} -h1 {font-size:3em;line-height:1;margin-bottom:0.5em;} -h2 {font-size:2em;margin-bottom:0.75em;} -h3 {font-size:1.5em;line-height:1;margin-bottom:1em;} -h4 {font-size:1.2em;line-height:1.25;margin-bottom:1.25em;} -h5 {font-size:1em;font-weight:bold;margin-bottom:1.5em;} -h6 {font-size:1em;font-weight:bold;} -h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin:0;} -p {margin:0 0 1.5em;} -p img.left {float:left;margin:1.5em 1.5em 1.5em 0;padding:0;} -p img.right {float:right;margin:1.5em 0 1.5em 1.5em;} -a:focus, a:hover {color:#000;} -a {color:#009;text-decoration:underline;} -blockquote {margin:1.5em;color:#666;font-style:italic;} -strong {font-weight:bold;} -em, dfn {font-style:italic;} -dfn {font-weight:bold;} -sup, sub {line-height:0;} -abbr, acronym {border-bottom:1px dotted #666;} -address {margin:0 0 1.5em;font-style:italic;} -del {color:#666;} -pre {margin:1.5em 0;white-space:pre;} -pre, code, tt {font:1.1em 'andale mono', 'lucida console', monospace;line-height:1.5;} -li ul, li ol {margin:0;} -ul, ol {margin:0 1.5em 1.5em 0;padding-left:3.333em;} -ul {list-style-type:disc;} -ol {list-style-type:decimal;} -dl {margin:0 0 1.5em 0;} -dl dt {font-weight:bold;} -dd {margin-left:1.5em;} -table {width:100%;} -th {font-weight:bold;} - -caption {background:#eee;} -.small {font-size:.8em;margin-bottom:1.875em;line-height:1.875em;} -.large {font-size:1.2em;line-height:2.5em;margin-bottom:1.25em;} -.hide {display:none;} -.quiet {color:#666;} -.loud {color:#000;} -.highlight {background:#ff0;} -.added {background:#060;color:#fff;} -.removed {background:#900;color:#fff;} -.first {margin-left:0;padding-left:0;} -.last {margin-right:0;padding-right:0;} -.top {margin-top:0;padding-top:0;} -.bottom {margin-bottom:0;padding-bottom:0;} - -/* forms.css */ -label {font-weight:bold;} -fieldset {padding:1.4em;margin:0 0 1.5em 0;border:1px solid #ccc;} -legend {font-weight:bold;font-size:1.2em;} -input[type=text], input[type=password], input.text, input.title, textarea, select {background-color:#fff;border:1px solid #bbb;} -input[type=text]:focus, input[type=password]:focus, input.text:focus, input.title:focus, textarea:focus, select:focus {border-color:#666;} -input[type=text], input[type=password], input.text, input.title, textarea, select {margin:0.5em 0;} -input.text, input.title {width:300px;padding:5px;} -input.title {font-size:1.5em;} -textarea {width:390px;height:250px;padding:5px;} -input[type=checkbox], input[type=radio], input.checkbox, input.radio {position:relative;top:.25em;} -form.inline {line-height:3;} -form.inline p {margin-bottom:0;} -.error, .notice, .success {padding:.8em;margin-bottom:1em;border:2px solid #ddd;} -.error {background:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;} -.notice {background:#FFF6BF;color:#514721;border-color:#FFD324;} -.success {background:#E6EFC2;color:#264409;border-color:#C6D880;} -.error a {color:#8a1f11;} -.notice a {color:#514721;} -.success a {color:#264409;} - -/* grid.css */ -.container {width:950px;margin:0 auto;} -.showgrid {background:url(src/grid.png);} -.column, div.span-1, div.span-2, div.span-3, div.span-4, div.span-5, div.span-6, div.span-7, div.span-8, div.span-9, div.span-10, div.span-11, div.span-12, div.span-13, div.span-14, div.span-15, div.span-16, div.span-17, div.span-18, div.span-19, div.span-20, div.span-21, div.span-22, div.span-23, div.span-24 {float:left;margin-right:10px;} -.last, div.last {margin-right:0;} -.span-1 {width:30px;} -.span-2 {width:70px;} -.span-3 {width:110px;} -.span-4 {width:150px;} -.span-5 {width:190px;} -.span-6 {width:230px;} -.span-7 {width:270px;} -.span-8 {width:310px;} -.span-9 {width:350px;} -.span-10 {width:390px;} -.span-11 {width:430px;} -.span-12 {width:470px;} -.span-13 {width:510px;} -.span-14 {width:550px;} -.span-15 {width:590px;} -.span-16 {width:630px;} -.span-17 {width:670px;} -.span-18 {width:710px;} -.span-19 {width:750px;} -.span-20 {width:790px;} -.span-21 {width:830px;} -.span-22 {width:870px;} -.span-23 {width:910px;} -.span-24, div.span-24 {width:950px;margin-right:0;} -input.span-1, textarea.span-1, input.span-2, textarea.span-2, input.span-3, textarea.span-3, input.span-4, textarea.span-4, input.span-5, textarea.span-5, input.span-6, textarea.span-6, input.span-7, textarea.span-7, input.span-8, textarea.span-8, input.span-9, textarea.span-9, input.span-10, textarea.span-10, input.span-11, textarea.span-11, input.span-12, textarea.span-12, input.span-13, textarea.span-13, input.span-14, textarea.span-14, input.span-15, textarea.span-15, input.span-16, textarea.span-16, input.span-17, textarea.span-17, input.span-18, textarea.span-18, input.span-19, textarea.span-19, input.span-20, textarea.span-20, input.span-21, textarea.span-21, input.span-22, textarea.span-22, input.span-23, textarea.span-23, input.span-24, textarea.span-24 {border-left-width:1px!important;border-right-width:1px!important;padding-left:5px!important;padding-right:5px!important;} -input.span-1, textarea.span-1 {width:18px!important;} -input.span-2, textarea.span-2 {width:58px!important;} -input.span-3, textarea.span-3 {width:98px!important;} -input.span-4, textarea.span-4 {width:138px!important;} -input.span-5, textarea.span-5 {width:178px!important;} -input.span-6, textarea.span-6 {width:218px!important;} -input.span-7, textarea.span-7 {width:258px!important;} -input.span-8, textarea.span-8 {width:298px!important;} -input.span-9, textarea.span-9 {width:338px!important;} -input.span-10, textarea.span-10 {width:378px!important;} -input.span-11, textarea.span-11 {width:418px!important;} -input.span-12, textarea.span-12 {width:458px!important;} -input.span-13, textarea.span-13 {width:498px!important;} -input.span-14, textarea.span-14 {width:538px!important;} -input.span-15, textarea.span-15 {width:578px!important;} -input.span-16, textarea.span-16 {width:618px!important;} -input.span-17, textarea.span-17 {width:658px!important;} -input.span-18, textarea.span-18 {width:698px!important;} -input.span-19, textarea.span-19 {width:738px!important;} -input.span-20, textarea.span-20 {width:778px!important;} -input.span-21, textarea.span-21 {width:818px!important;} -input.span-22, textarea.span-22 {width:858px!important;} -input.span-23, textarea.span-23 {width:898px!important;} -input.span-24, textarea.span-24 {width:938px!important;} -.append-1 {padding-right:40px;} -.append-2 {padding-right:80px;} -.append-3 {padding-right:120px;} -.append-4 {padding-right:160px;} -.append-5 {padding-right:200px;} -.append-6 {padding-right:240px;} -.append-7 {padding-right:280px;} -.append-8 {padding-right:320px;} -.append-9 {padding-right:360px;} -.append-10 {padding-right:400px;} -.append-11 {padding-right:440px;} -.append-12 {padding-right:480px;} -.append-13 {padding-right:520px;} -.append-14 {padding-right:560px;} -.append-15 {padding-right:600px;} -.append-16 {padding-right:640px;} -.append-17 {padding-right:680px;} -.append-18 {padding-right:720px;} -.append-19 {padding-right:760px;} -.append-20 {padding-right:800px;} -.append-21 {padding-right:840px;} -.append-22 {padding-right:880px;} -.append-23 {padding-right:920px;} -.prepend-1 {padding-left:40px;} -.prepend-2 {padding-left:80px;} -.prepend-3 {padding-left:120px;} -.prepend-4 {padding-left:160px;} -.prepend-5 {padding-left:200px;} -.prepend-6 {padding-left:240px;} -.prepend-7 {padding-left:280px;} -.prepend-8 {padding-left:320px;} -.prepend-9 {padding-left:360px;} -.prepend-10 {padding-left:400px;} -.prepend-11 {padding-left:440px;} -.prepend-12 {padding-left:480px;} -.prepend-13 {padding-left:520px;} -.prepend-14 {padding-left:560px;} -.prepend-15 {padding-left:600px;} -.prepend-16 {padding-left:640px;} -.prepend-17 {padding-left:680px;} -.prepend-18 {padding-left:720px;} -.prepend-19 {padding-left:760px;} -.prepend-20 {padding-left:800px;} -.prepend-21 {padding-left:840px;} -.prepend-22 {padding-left:880px;} -.prepend-23 {padding-left:920px;} -div.border {padding-right:4px;margin-right:5px;border-right:1px solid #eee;} -div.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #eee;} -.pull-1 {margin-left:-40px;} -.pull-2 {margin-left:-80px;} -.pull-3 {margin-left:-120px;} -.pull-4 {margin-left:-160px;} -.pull-5 {margin-left:-200px;} -.pull-6 {margin-left:-240px;} -.pull-7 {margin-left:-280px;} -.pull-8 {margin-left:-320px;} -.pull-9 {margin-left:-360px;} -.pull-10 {margin-left:-400px;} -.pull-11 {margin-left:-440px;} -.pull-12 {margin-left:-480px;} -.pull-13 {margin-left:-520px;} -.pull-14 {margin-left:-560px;} -.pull-15 {margin-left:-600px;} -.pull-16 {margin-left:-640px;} -.pull-17 {margin-left:-680px;} -.pull-18 {margin-left:-720px;} -.pull-19 {margin-left:-760px;} -.pull-20 {margin-left:-800px;} -.pull-21 {margin-left:-840px;} -.pull-22 {margin-left:-880px;} -.pull-23 {margin-left:-920px;} -.pull-24 {margin-left:-960px;} -.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float:left;position:relative;} -.push-1 {margin:0 -40px 1.5em 40px;} -.push-2 {margin:0 -80px 1.5em 80px;} -.push-3 {margin:0 -120px 1.5em 120px;} -.push-4 {margin:0 -160px 1.5em 160px;} -.push-5 {margin:0 -200px 1.5em 200px;} -.push-6 {margin:0 -240px 1.5em 240px;} -.push-7 {margin:0 -280px 1.5em 280px;} -.push-8 {margin:0 -320px 1.5em 320px;} -.push-9 {margin:0 -360px 1.5em 360px;} -.push-10 {margin:0 -400px 1.5em 400px;} -.push-11 {margin:0 -440px 1.5em 440px;} -.push-12 {margin:0 -480px 1.5em 480px;} -.push-13 {margin:0 -520px 1.5em 520px;} -.push-14 {margin:0 -560px 1.5em 560px;} -.push-15 {margin:0 -600px 1.5em 600px;} -.push-16 {margin:0 -640px 1.5em 640px;} -.push-17 {margin:0 -680px 1.5em 680px;} -.push-18 {margin:0 -720px 1.5em 720px;} -.push-19 {margin:0 -760px 1.5em 760px;} -.push-20 {margin:0 -800px 1.5em 800px;} -.push-21 {margin:0 -840px 1.5em 840px;} -.push-22 {margin:0 -880px 1.5em 880px;} -.push-23 {margin:0 -920px 1.5em 920px;} -.push-24 {margin:0 -960px 1.5em 960px;} -.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float:right;position:relative;} -.prepend-top {margin-top:1.5em;} -.append-bottom {margin-bottom:1.5em;} -.box {padding:1.5em;margin-bottom:1.5em;background:#E5ECF9;} -hr {background:#ddd;color:#ddd;clear:both;float:none;width:100%;height:.1em;margin:0 0 1.45em;border:none;} -hr.space {background:#fff;color:#fff;visibility:hidden;} -.clearfix:after, .container:after {content:"\0020";display:block;height:0;clear:both;visibility:hidden;overflow:hidden;} -.clearfix, .container {display:block;} -.clear {clear:both;} diff --git a/root/static/css/core.css b/root/static/css/core.css new file mode 100755 index 0000000..09c3c7a --- /dev/null +++ b/root/static/css/core.css @@ -0,0 +1,540 @@ +#debug_holder{ + + display:none; + + clear:both; + padding-top:30px; + margin:30px 0; +} + +#debug_holder pre{ + margin:0; + padding:10px; + border:1px solid #ddd; + background-color:#f0f0f0; +} + +body{ + background:#FAFAFA url([% c.uri_for('/static/i/bg.png') %]) repeat-x left top; + padding:0; + margin:0; + font-family:Arial, Verdana, sans-serif; + font-size:80%; +} +a img{ + border:0; +} + + +/* structure */ +.sub_holder{ + width:970px; + margin:0 auto; + text-align:left; +} + +#header_holder{ + margin-top:20px; +} +#header{ + height:60px; +} + +#content_holder{ + background:transparent url([% c.uri_for('/static/i/bg_content.png') %]) repeat-y center center; +} +#content{ + min-height:200px; + padding:0 12px 40px 10px; + background:transparent url([% c.uri_for('/static/i/bg_bottom.png') %]) no-repeat center bottom; +} +#content_inner{ + padding-bottom:30px; + background:transparent url([% c.uri_for('/static/i/bg_top.png') %]) no-repeat; +} +.copy{ + padding:10px; +} +#logo{ + margin-left:-10px; + float:left; +} +#header .search{ + margin-right:10px; + float:right; +} +#git_logo{ + float:left; + margin-left:15px; +} +#feeds{ + float:right; + margin-right:15px; +} +#footer_holder{ + margin-bottom:100px; +} +#footer p{ + margin-left:15px; +} + + +/* nav tabs */ +#nav_logs{ + width:100%; + clear:both; + float:right; + margin:-5px 10px 0 0; +} +#nav_logs ul{ + margin:0; + padding:0; +} +#nav_logs li{ + display:block; + float:right; + list-style:none; + margin:0; + padding:0; +} +#nav_logs li a{ + display:block; + margin-left:20px; + padding:10px 15px 10px 48px; + color:#ffffff; + font-size:1.4em; + text-transform:uppercase; + text-decoration:none; +} +a#log_short{ + background:#666 url([% c.uri_for('/static/i/icons/shortlog.gif') %]) no-repeat 15px center; +} +a#log_full{ + background:#666 url([% c.uri_for('/static/i/icons/fulllog.gif') %]) no-repeat 15px center; +} +a#tree{ + background:#666 url([% c.uri_for('/static/i/icons/tree.gif') %]) no-repeat 15px center; +} +#nav_logs li a:hover{ + text-decoration:underline; + background-color:#DC143C; +} +#nav_logs li.selected a{ + background-color:#333; +} +#nav_logs #branch_selector{ + padding:10px 15px 10px 48px; + font-size:1.3em; + font-weight:bold; + color:#666; +} + + + + + +/* formating */ +h1{ + margin:0; + padding:20px 0; + clear:both; + font-weight:normal; + font-size:1.85em; + color:#fff; +} +h1 a{ + margin-right:10px; + color:#fff; +} +h1 a:hover{ + color:#EAF2F5; +} +h2{ + font-size:1.85em; + font-weight:normal; + color:#666; + margin:30px 15px 20px; +} +h2 span{ + color:#ccc; +} +h3{ + margin:0; + color:#fff; + padding:9px 5px 9px 10px; + font-size:1em; +} +h3 a{ + color:#ffffff; +} +h4 a{ + color:#ffffff; +} +h4 a:hover{ + color:#EAF2F5; +} +p, +td, +a{ + color:#666; +} +a:hover{ + color:#DC143C; +} + +/* sub actions dropdown changer in h1 */ +#actions_nav_link{ + border:1px solid #666; + padding:1px 4px; + text-decoration:none; + outline:none; +} +#actions_nav_link span{ + margin-right:5px; + padding-right:18px; + + background:transparent url([% c.uri_for('/static/i/arrow_down_white.gif') %]) no-repeat right center; +} +#actions_nav_list{ + display:none; + position:absolute; + padding:0 0 3px 0; + margin:0; + background-color:#333; + color:#fff; + font-size:1em; + border:1px solid #666; + border-top:none; +} +#actions_nav_list li{ + list-style:none; + margin:0; + padding:2px 10px 2px 5px; +} +#actions_nav_list a{ + color:#fff; + font-size:1.8em; +} +#actions_nav_list a:hover{ + color:#EAF2F5; +} +.actions_nav_list_over{ + display:block !important; +} +.button{ + display:block; + float:left; + vertical-align:middle; + margin-right:8px; + text-indent:-999999px; + width:21px; + height:21px; + outline:none; +} +a.commit{ + background:transparent url([% c.uri_for('/static/i/icons/commit.png') %]) no-repeat; +} +a.diff{ + background:transparent url([% c.uri_for('/static/i/icons/diff.png') %]) no-repeat; +} +a.diffcurrent{ + background:transparent url([% c.uri_for('/static/i/icons/diffcurrent.png') %]) no-repeat; +} +a.tree{ + background:transparent url([% c.uri_for('/static/i/icons/tree.png') %]) no-repeat; +} +a.shortlog{ + background:transparent url([% c.uri_for('/static/i/icons/shortlog.png') %]) no-repeat; +} +a.longlog{ + background:transparent url([% c.uri_for('/static/i/icons/longlog.png') %]) no-repeat; +} +a.blob{ + background:transparent url([% c.uri_for('/static/i/icons/blob.png') %]) no-repeat; +} +a.blame{ + background:transparent url([% c.uri_for('/static/i/icons/blame.png') %]) no-repeat; +} +a.history{ + background:transparent url([% c.uri_for('/static/i/icons/history.png') %]) no-repeat; +} +a.raw{ + background:transparent url([% c.uri_for('/static/i/icons/raw.png') %]) no-repeat; +} +.sha1_holder{ + background:transparent url([% c.uri_for('/static/i/icons/button_sha1.png') %]) no-repeat; +} +.sha1_holder_invert{ + background:transparent url([% c.uri_for('/static/i/icons/button_sha1_invert.png') %]) no-repeat; +} +.sha1_label{ + padding-top:2px; + float:left; +} +a.file{ + padding-left:25px; + background:transparent url([% c.uri_for('/static/i/icons/file.png') %]) no-repeat; +} +a.folder{ + padding-left:25px; + background:transparent url([% c.uri_for('/static/i/icons/folder.png') %]) no-repeat; +} +.msg{ + padding:5px 10px 5px 35px; + background:#f0f0f0 url([% c.uri_for('/static/i/icons/attention.png') %]) no-repeat 10px center; + border:1px solid #ddd; + margin:30px 15px; +} +.match{ + background-color:#ffff00; +} +a.rss_logo { + float: right; + padding: 3px 0px; + width: 35px; + line-height: 10px; + border: 1px solid; + border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e; + color: #ffffff; + background-color: #ff6600; + font-weight: bold; + font-family: sans-serif; + font-size: 70%; + font-style: normal; + text-align: center; + text-decoration: none; + margin: 3px; +} + +a.rss_logo:hover { + background-color: #ee5500; +} + + +.button_submit{ + text-indent:-999999px; + overflow:hidden; + width:95px; + height:26px; + border:0; + background:transparent url([% c.uri_for('/static/i/buttons/search.png') %]) no-repeat; + cursor: pointer; +} + + +/* paging */ +.pager{ + width:100%; + float:left; + margin:10px 0; + padding:0; +} +.pager li{ + margin:0; + padding:0; + display:block; + list-style:none; +} +.pager li a{ + display:block; + padding:4px 6px; + color:#fff; + border:1px solid #ddd; + background-color:#333; +} +.pager .pager_prev a{ + margin-left:20px; + float:left; +} +.pager .pager_next a{ + margin-right:20px; + float:right; +} + + + + +/* table listings */ +th{ + padding:9px 5px 9px 10px; + text-align:left; + color:#fff; +} +th a{ + color:#fff; +} +th a:hover{ + color:#f0f0f0; +} +.summary tr{ + background-color:#FAFAFA; + border-bottom:1px solid #fff; +} +.summary td{ + vertical-align:middle !important; +} +tr{ + background-color:#fff; +} +thead tr{ + background-color:transparent !important; +} +tr.invert{ + background-color:#f0f0f0; +} +tr.header{ + background-color:#666; +} +.sha1{ + width:80px; +} +.file-mode{ + width:80px; +} +.time-since{ + width:95px; + font-weight:bold; +} +.author{ + width:180px; +} +.author img{ + margin-right:5px; + vertical-align:middle; +} +table.listing{ + width:970px; + border-collapse:collapse; +} +.listing td{ + vertical-align:top; + padding:9px 5px 9px 10px; +} + +/* + +puts the repo description on one line which gets truncated if the repo name is too long +BUT the final width needs to be set with javascript based on the parent element (td) width + +.description{ + white-space:nowrap; + overflow:hidden; +} +.description div{ + position:absolute; + white-space:nowrap; + overflow:hidden; + width:200px; +} +*/ + +.action-list{ + width:120px; +} + +.diff-tree{ + background-color:#f0f0f0; +} +.diff-tree th{ + background-color:#666; +} +.diff-tree tr{ + border-bottom:1px solid #fff; +} +.diff-tree td{ + padding:6px 5px 4px 10px; +} +.differences{ + margin-top:0; + padding:0px 15px; +} +.differences pre{ + line-height:140%; + font-size:12px; + overflow:auto; + margin:0; + padding:10px; + border:1px solid #ddd; + background-color:#f0f0f0; + min-height:40px; +} +.diff-head{ + background-color:#666; + color:#fff; + margin:10px 0 0 0 !important; + border:1px solid #ddd; + border-bottom:none; + font-family: monospace; + padding:10px; + font-size:13px; +} +.diff-index { + margin:0 0 30px 0; + border:1px solid #ddd; + border-top:none; + background-color:#ccc; + padding:5px 10px; +} + + +/* /blame */ +#blame pre, #blame tt { + margin: 0; + font-size: 12px; +} +#blame .commit-info { + +} +#blame .lineno { + text-align: right; + padding: 0 8px; +} +#blame a { + atext-decoration: none; +} +#blame { + overflow-x: scroll; +} +#blame tr.alt { + background-color: #f7f7f7; +} +#blame tbody tr:hover { + background-color: #fefeaa; +} +#blame td { + vertical-align:middle; + padding: 3px; +} +#blame td.lineno { + background-color: #eee; +} +#blame td.date, #blame td.author, #blame td.commit-info { +} +#blame tbody td.data { + padding-left: 5px; + background-color: #333; + color: #ddd; +} + +/* /blob */ +pre.blob { + background-color: #333; + color: #ddd; + border-left: solid 3px #c33; + padding: 5px; + padding-left: 15px; + margin: 20px 15px 20px; + overflow:auto; + font-size:12px; +} +div.blob { + text-align: center; + margin: 30px; +} + +/* /blobdiff etc */ + +/* Hidden spans that contain bits of data to be used by JS */ +.js-data { + display: none; +} diff --git a/root/static/css/site.css b/root/static/css/site.css deleted file mode 100644 index a968bc5..0000000 --- a/root/static/css/site.css +++ /dev/null @@ -1,306 +0,0 @@ -#the-container { - margin: 1em 100px; - text-align: center; - background-color: white; -} -#body { - text-align: justify; -} -div.content { - padding: 0 7px; -} - -/* -14d2f2ca3732551d1585e7590e60b82492f3 -^^ A rather nice chroma hash -*/ -body { - background-color: #555; -} - -thead, tfoot { - /* FTR all table columns should be sortable hence the colour to indicate clickableness. */ - color: navy; - font-size: small; - font-weight: bold !important; -} -thead th { - border-bottom: solid 1px #777; -} -tfoot td { - border-top: solid 1px #777; -} - -.listing tbody tr:nth-child(even) { - background-color: #f7f7f7; -} -.listing tbody tr:hover { - background-color: #fefeaa; -} - -.chroma-hash { - font-family: monospace; - font-size: 1em; - font-style: normal; -} -div.chroma-hash { - float: right; -} - -.time-since { - font-style: italic; -} -.author, .head { - font-weight: bold; - font-family: "Trebuchet MS", "Lucida Grande", serif; -} - -.file-name, .file-mode { - font-family: monospace; -} -.action-list { - font-size: smaller; -} - -.path { - border-bottom: solid 1px #ddd; - padding: 3px 0; - font-weight: bold; -} - - -/* header */ -#page-header { - height: 25px; - padding: 8px; - font-size: 1.5em; - font-weight: bold; - border-bottom: solid 1px #777; - text-align: justify; -} - -img.logo { - float: right; - border-width: 0px; - margin-top: 4px; -} - -/* footer */ -#page-footer { - height: 20px; - padding: 8px; - margin-top: 10px; - font-style: italic; - background-color: #d9d8d1; - border-top: solid 1px #777; -} - -/* actions include */ -.actions { - padding-bottom: 4px 0; - font-style: italic; - border-bottom: solid 1px #777; - margin-bottom: 10px; - padding: 5px 7px; -} - -/* pager include */ -.pager { - text-align: center; -} - -/* search include */ -#page-search { - text-align: right; - float: right; - font-size: 0.7em; - padding-right: 20px; -} - -/* shortlog include */ -table.shortlog tbody tr { - padding: 1px 0px; -} -table.shortlog tbody td { - padding: 0 1px; -} - -/* /commit page */ -.commit-message { - font-family: monospace; - font-size: 1.2em; -} -div.commit-message { - margin-bottom: 10px; -} -pre.commit-message { - border-top: solid 2px red; - border-bottom: solid 2px green; - padding: 5px; -} -.commit-info dt { - font-weight: bold; -} -.commit-info dd { - font-family: monospace; -} - -/* /heads */ -.heads .head { - font-weight: bold; -} -.heads .current { - text-decoration: underline; -} -table.heads { - width: 75%; -} - -/* /blame */ -#blame pre, #blame tt { - margin: 0; - font-size: 0.9em; -} -#blame .commit-info { -} -#blame .lineno { - text-align: right; - padding: 0 8px; -} -#blame a { - text-decoration: none; -} -#blame { - overflow-x: scroll; -} -#blame tr.alt { - background-color: #f7f7f7; -} -#blame tbody tr:hover { - background-color: #fefeaa; -} -#blame td { - padding: 0 0.2em; -} -#blame td.lineno { - background-color: #eee; -} -#blame td.date, #blame td.author, #blame td.commit-info { -} -#blame .data { - padding-left: 5px; -} - -/* /blob */ -pre.blob { - background-color: #333; - color: #ddd; - border-left: solid 3px #c33; - padding: 5px; - padding-left: 15px; - margin: 10px 15px; -} - -/* /blobdiff etc */ - -.diff-head, .diff-index { - font-family: monospace; -} -.diff-head { - border-top: solid 1px red; -} -.diff-index { - border-bottom: solid 1px green; -} - -/* /commitdiff */ -.diff-patch { - font-size: 0.8em; -} - -/* /log */ -#log .entry { - border: solid 1px grey; - margin: 5px 0; - padding: 5px; -} -#log .meta { - border-top: dotted 1px #ddd; - color: #311; -} -#log table.summary { - width: 33%; - font-size: 0.9em; -} -#log .message { - font-family: monospace; - font-size: 1.15em; - /* XXX Need to enforce find the CSS switch to force the chroma-hash below the message box */ -} -#log .age { - float: right; - font-size: 0.85em; - font-style: italic; -} - -/* /summary */ -#stats { - float: right; -} - -/* /tree */ -table.tree { - width: 65%; -} - -/* Formerly of gitweb.css */ - -span.refs span { - padding: 0px 4px; - font-size: 80%; - /* XXX needs more families */ - font-family: Verdana; - letter-spacing: -1px; - border: 1px solid; - background-color: #ffaaff; - border-color: #ffccff #ff00ee #ff00ee #ffccff; -} - -span.refs span.ref { - background-color: #aaaaff; - border-color: #ccccff #0033cc #0033cc #ccccff; -} - -span.refs span.tag { - background-color: #ffffaa; - border-color: #ffffcc #ffee00 #ffee00 #ffffcc; -} - -span.refs span.head { - background-color: #aaffaa; - border-color: #ccffcc #00cc33 #00cc33 #ccffcc; -} -span.refs a { - text-decoration: none; -} - -a.rss_logo { - float: right; - padding: 3px 0px; - width: 35px; - line-height: 10px; - border: 1px solid; - border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e; - color: #ffffff; - background-color: #ff6600; - font-weight: bold; - font-family: sans-serif; - font-size: 70%; - font-style: normal; - text-align: center; - text-decoration: none; -} - -a.rss_logo:hover { - background-color: #ee5500; -} diff --git a/root/static/css/syntax/Diff.css b/root/static/css/syntax/Diff.css old mode 100644 new mode 100755 index a76b6fb..0303541 --- a/root/static/css/syntax/Diff.css +++ b/root/static/css/syntax/Diff.css @@ -1,16 +1,21 @@ span.Keyword { - color: #777; + color: #aaa; font-weight: bold; } span.DataType { + background-color:#F3E6FF; color: purple; } span.Normal { - color: gray; + } span.String { - color: green; + background-color:#CCF5CC; + padding:2px 0; + color: #006F00; } span.Others { - color: red; + background-color:#F3CCCC; + padding:2px 0; + color: #750000; } diff --git a/root/static/favicon.ico b/root/static/favicon.ico new file mode 100755 index 0000000..fea2fec Binary files /dev/null and b/root/static/favicon.ico differ diff --git a/root/git-favicon.png b/root/static/git-favicon.png similarity index 100% rename from root/git-favicon.png rename to root/static/git-favicon.png diff --git a/root/static/i/arrow_down_white.gif b/root/static/i/arrow_down_white.gif new file mode 100755 index 0000000..a8ef359 Binary files /dev/null and b/root/static/i/arrow_down_white.gif differ diff --git a/root/static/i/arrow_grey.gif b/root/static/i/arrow_grey.gif new file mode 100755 index 0000000..3a4429e Binary files /dev/null and b/root/static/i/arrow_grey.gif differ diff --git a/root/static/i/arrow_grey_left.gif b/root/static/i/arrow_grey_left.gif new file mode 100755 index 0000000..42e070a Binary files /dev/null and b/root/static/i/arrow_grey_left.gif differ diff --git a/root/static/i/bg.png b/root/static/i/bg.png new file mode 100755 index 0000000..1c48ca4 Binary files /dev/null and b/root/static/i/bg.png differ diff --git a/root/static/i/bg_bottom.png b/root/static/i/bg_bottom.png new file mode 100755 index 0000000..cda2f73 Binary files /dev/null and b/root/static/i/bg_bottom.png differ diff --git a/root/static/i/bg_content.png b/root/static/i/bg_content.png new file mode 100755 index 0000000..c6dc6b5 Binary files /dev/null and b/root/static/i/bg_content.png differ diff --git a/root/static/i/bg_top.png b/root/static/i/bg_top.png new file mode 100755 index 0000000..65601c4 Binary files /dev/null and b/root/static/i/bg_top.png differ diff --git a/root/static/i/buttons/search.png b/root/static/i/buttons/search.png new file mode 100755 index 0000000..4768621 Binary files /dev/null and b/root/static/i/buttons/search.png differ diff --git a/root/static/i/favicon.gif b/root/static/i/favicon.gif new file mode 100755 index 0000000..29319a5 Binary files /dev/null and b/root/static/i/favicon.gif differ diff --git a/root/static/i/favicon.png b/root/static/i/favicon.png new file mode 100755 index 0000000..9c14c24 Binary files /dev/null and b/root/static/i/favicon.png differ diff --git a/root/static/i/icons/Thumbs.db b/root/static/i/icons/Thumbs.db new file mode 100644 index 0000000..c804544 Binary files /dev/null and b/root/static/i/icons/Thumbs.db differ diff --git a/root/static/i/icons/attention.png b/root/static/i/icons/attention.png new file mode 100755 index 0000000..db00074 Binary files /dev/null and b/root/static/i/icons/attention.png differ diff --git a/root/static/i/icons/blame.png b/root/static/i/icons/blame.png new file mode 100755 index 0000000..7a6730b Binary files /dev/null and b/root/static/i/icons/blame.png differ diff --git a/root/static/i/icons/blob.png b/root/static/i/icons/blob.png new file mode 100755 index 0000000..c5261b8 Binary files /dev/null and b/root/static/i/icons/blob.png differ diff --git a/root/static/i/icons/button_sha1.png b/root/static/i/icons/button_sha1.png new file mode 100755 index 0000000..77915e0 Binary files /dev/null and b/root/static/i/icons/button_sha1.png differ diff --git a/root/static/i/icons/button_sha1_invert.png b/root/static/i/icons/button_sha1_invert.png new file mode 100755 index 0000000..2f1724b Binary files /dev/null and b/root/static/i/icons/button_sha1_invert.png differ diff --git a/root/static/i/icons/commit.png b/root/static/i/icons/commit.png new file mode 100755 index 0000000..692f11e Binary files /dev/null and b/root/static/i/icons/commit.png differ diff --git a/root/static/i/icons/diff.png b/root/static/i/icons/diff.png new file mode 100755 index 0000000..9883936 Binary files /dev/null and b/root/static/i/icons/diff.png differ diff --git a/root/static/i/icons/diffcurrent.png b/root/static/i/icons/diffcurrent.png new file mode 100755 index 0000000..8f4827f Binary files /dev/null and b/root/static/i/icons/diffcurrent.png differ diff --git a/root/static/i/icons/file.png b/root/static/i/icons/file.png new file mode 100755 index 0000000..1d0e6e0 Binary files /dev/null and b/root/static/i/icons/file.png differ diff --git a/root/static/i/icons/folder.png b/root/static/i/icons/folder.png new file mode 100755 index 0000000..2016319 Binary files /dev/null and b/root/static/i/icons/folder.png differ diff --git a/root/static/i/icons/fulllog.gif b/root/static/i/icons/fulllog.gif new file mode 100755 index 0000000..536da14 Binary files /dev/null and b/root/static/i/icons/fulllog.gif differ diff --git a/root/static/i/icons/history.png b/root/static/i/icons/history.png new file mode 100755 index 0000000..690a489 Binary files /dev/null and b/root/static/i/icons/history.png differ diff --git a/root/static/i/icons/longlog.png b/root/static/i/icons/longlog.png new file mode 100755 index 0000000..4859ac9 Binary files /dev/null and b/root/static/i/icons/longlog.png differ diff --git a/root/static/i/icons/raw.png b/root/static/i/icons/raw.png new file mode 100755 index 0000000..f2854f6 Binary files /dev/null and b/root/static/i/icons/raw.png differ diff --git a/root/static/i/icons/shortlog.gif b/root/static/i/icons/shortlog.gif new file mode 100755 index 0000000..0f1e9b0 Binary files /dev/null and b/root/static/i/icons/shortlog.gif differ diff --git a/root/static/i/icons/shortlog.png b/root/static/i/icons/shortlog.png new file mode 100755 index 0000000..9c48a2a Binary files /dev/null and b/root/static/i/icons/shortlog.png differ diff --git a/root/static/i/icons/tree.gif b/root/static/i/icons/tree.gif new file mode 100755 index 0000000..49d75b8 Binary files /dev/null and b/root/static/i/icons/tree.gif differ diff --git a/root/static/i/icons/tree.png b/root/static/i/icons/tree.png new file mode 100755 index 0000000..139a962 Binary files /dev/null and b/root/static/i/icons/tree.png differ diff --git a/root/static/i/logo.png b/root/static/i/logo.png new file mode 100755 index 0000000..cb0e1f8 Binary files /dev/null and b/root/static/i/logo.png differ diff --git a/root/static/js/site.js b/root/static/js/site.js new file mode 100755 index 0000000..3c748fe --- /dev/null +++ b/root/static/js/site.js @@ -0,0 +1,86 @@ +function findPos(obj) { + var curleft = curtop = 0; + if (obj.offsetParent) { + do { + curleft += obj.offsetLeft; + curtop += obj.offsetTop; + } + while (obj = obj.offsetParent); + return [curleft,curtop]; + } +} + +function setNavClass(el){ + var link_el = document.getElementById("actions_nav_link"); + var offsetAry = findPos(link_el); + // set position of list + el.style.left = offsetAry[0]+"px"; + el.style.top = offsetAry[1]+30 +"px"; + el.className+=" actions_nav_list_over"; +} + +// handles hover sub menus in IE +function startList() { + if(!document.getElementById("actions_nav_link")) + return; + var navList = document.getElementById("actions_nav_list"); + var navLink = document.getElementById("actions_nav_link"); + // assign event handlers to each element + navLink.onmouseover=function() { + setNavClass(navList); + }; + navList.onmouseover=function() { + setNavClass(navList); + }; + navList.onmouseout=function() { + navList.className=navList.className.replace(" actions_nav_list_over", ""); + }; + navLink.onmouseout=function() { + navList.className=navList.className.replace(" actions_nav_list_over", ""); + }; +} + +function uriFor(action, sha1) { + return jQuery('#' + action + '-uri').text().replace(/\bHEAD\b/, sha1); +} + +function switchBranch() { + var branch = jQuery('#branch-list').val(); + document.location.href = uriFor('current', branch); +} + +function compareDiffs(){ + var path = jQuery('#compare-path').text(), + baseSha1 = jQuery('#compare-form input[name=sha1_a]:checked').val(), + compSha1 = jQuery('#compare-form input[name=sha1_b]:checked').val(), + diffUri = uriFor('diff', baseSha1); + document.location.href = diffUri + '/' + compSha1 + (path ? '/' + encodeURIComponent(path) : ''); + return false; +} + +function _loadCommitInfo(cells) { + var cell = jQuery(cells.shift()); + var filename = cell.find('.js-data').text(); + jQuery.getJSON(uriFor('file_commit_info') + '/' + filename, {}, function(commitInfo) { + cell.empty(); + cell.html(''+commitInfo.comment+' '+commitInfo.age); + if(cells.length > 0) + _loadCommitInfo(cells); + }); +} + +function loadCommitInfo() { + _loadCommitInfo( jQuery('#commit-tree .message').get() ); +} + +jQuery(function() { + // Provide sub-nav dropdowns (I think). + startList(); + + // JS up any Compare links + jQuery('a.compare-link').click(compareDiffs); + // Change the URL when a branch is selected + jQuery('#branch-list').change(switchBranch); + // Wait for image requests to come back first + jQuery(window).load(loadCommitInfo); +}); diff --git a/root/summary.tt2 b/root/summary.tt2 deleted file mode 100644 index be4b3e1..0000000 --- a/root/summary.tt2 +++ /dev/null @@ -1,22 +0,0 @@ -[% PROCESS 'nav/actions.tt2' object = commit %] - -
- - -
-
description
[% Repository.description %]
-
owner
[% Repository.owner %]
-
last change
[% Repository.last_change %]
-
- -

shortlog

- [% INCLUDE '_shortlog.tt2' %] - -

branches

- [% INCLUDE '_heads.tt2' %] -
diff --git a/root/tags.tt2 b/root/tags.tt2 deleted file mode 100644 index 6f47285..0000000 --- a/root/tags.tt2 +++ /dev/null @@ -1,8 +0,0 @@ -[% INCLUDE 'nav/actions.tt2' object = commit %] -
-
- [% Repository.name %] -
- - [% INCLUDE '_heads.tt2' heads = tags %] -
diff --git a/root/tree.tt2 b/root/tree.tt2 deleted file mode 100644 index bc06ea5..0000000 --- a/root/tree.tt2 +++ /dev/null @@ -1,15 +0,0 @@ -[% INCLUDE 'nav/actions.tt2' object = commit %] - -
-
- [% short_cmt(commit.comment) | html %] ... -
- - [% - IF path; - INCLUDE 'nav/path.tt2' filename = path, head = commit; - END; - - INCLUDE '_tree.tt2'; - %] -
diff --git a/root/wrapper.tt2 b/root/wrapper.tt2 new file mode 100755 index 0000000..ad3bb5c --- /dev/null +++ b/root/wrapper.tt2 @@ -0,0 +1,198 @@ +[%- IF no_wrapper || template.name.match('\.(css|js|txt)'); content; ELSE; -%] + + + + + + + + [%# FIXME - MING %][%- + title = BLOCK; + c.config.sitename; + IF Repository; ' - ' _ Repository.name | html; END; + IF c.action; ' / ' _ c.action; END; + IF filename; ' - ' _ filename | html; END; + IF c.action && c.action == 'tree'; '/'; END; + END; + title; + -%] (Gitalist) + [% INCLUDE '_header_feeds.tt2' %] + + + + + + + + + +
+
+ + + + + +

+ Home + + [%- IF Repository %] + / [% Repository.name %] + [%- END %] + / + [%- IF Repository %] + [%# FIXME: output branch name in a nicer way!!! #%] + [% FOREACH branch_head IN Repository.heads %] + [% IF c.req.path.search(branch_head.name) %] + [% branch_head.name %] / + [% END %] + [% END %] + [%- END %] + + + [%- + + #FIXME on a history view of a folder, don't show extra actions in dropdown + SET on_file_page = 1; + + SET actions_list = { + "blob" => 1, + "raw" => 1, + "blame" => 1, + "history" => 1, + }; + + SET action_name = c.action.name + .replace("_"," ") + .replace("log", " log") + .replace("fancy","") + .replace("index","Repositories"); + action_name_ucfirst = action_name FILTER ucfirst; + IF actions_list.$action_name && on_file_page; + '' _ action_name_ucfirst _ ''; + ELSE; + action_name_ucfirst; + END; + + -%] + +

+ + [%- + IF actions_list.$action_name; + '
    '; + FOREACH action IN actions_list; + action_output = action.key FILTER ucfirst; + NEXT IF action_output == action_name_ucfirst; + action = action.key; + '
  • ' _ action_output _ '
  • '; + END; + '
'; + END; + -%] + +
+
+ + +
+
+
+ + + + [% content %] + + +
+
+
+ + + + + +
+
+ +

Debug:

+ + [% USE Dumper %] +
+	[% Repository.path %]
+	[% Dumper.dump(c.req.args) %]
+	
+ +
+
+ +[% + # A bit of smoke and mirrors to get the /repository/shortlog URIs working + cur_act = '' _ c.action; + cur_uri = cur_act.match("log") || cur_act.match("^ref") + ? '/ref/' _ cur_act.replace("^(repository|ref)/", '') + : cur_act; + # XXX I just want lists! + IF c.req.args.size > 0; + c.uri_for_action(cur_uri, [Repository.name, 'HEAD'], c.req.args.0); + ELSE; + c.uri_for_action(cur_uri, [Repository.name, 'HEAD']); + END; +%] + + + +[%- END -%] diff --git a/script/bootstrap.pl b/script/bootstrap.pl new file mode 100644 index 0000000..269f899 --- /dev/null +++ b/script/bootstrap.pl @@ -0,0 +1,81 @@ +#!/usr/bin/env perl + +# This script installs an initial local::lib into your application directory +# named local-lib5, which automatically turns on local::lib support by default. + +# Needs to be run as script/bootstrap.pl + +# Will then install Module::Install, and all of your dependencies into +# the local lib directory created. + +use strict; +use warnings; + +use lib; +use FindBin; +use CPAN; + +# Do not take no for an answer. + +$ENV{CATALYST_LOCAL_LIB}=1; + +# Get the base paths and setup your %ENV + +my $basedir; +if (-r "$FindBin::Bin/Makefile.PL") { + $basedir = $FindBin::Bin; +} +elsif (-r "$FindBin::Bin/../Makefile.PL") { + $basedir = "$FindBin::Bin/.."; +} + +$basedir ||= ''; +my $target = "$basedir/local-lib5"; +my $lib = "$target/lib/perl5"; + +# Start installing stuff in the target dir +$ENV{PERL_MM_OPT} = "INSTALL_BASE=$target"; +$ENV{PERL_MM_USE_DEFAULT} = "1"; +# And allow dependency checks to find it +lib->import("$target/lib/perl5"); + +# Deal with the weird case that cpan has never been run before and +# cpan wants to create a .cpan directory in /root or somewhere you +# can't access + +local %CPAN::Config; +require CPAN::HandleConfig; +CPAN::HandleConfig->load(); +$CPAN::Config->{prefs_dir} = "$ENV{HOME}/.cpan/prefs"; + +force(qw/install local::lib/); + +require lib::core::only; # Turn lib::core:only on +require local::lib; # Turn local::lib on +lib::core::only->import(); +local::lib->import( $target ); + +# Become fully self contained +$ENV{PERL5LIB} = ""; # If we used a local::lib to bootstrap, this kills it. + +# Sorry kane ;) +$ENV{PERL_AUTOINSTALL_PREFER_CPAN}=1; +$ENV{PERL_MM_OPT} .= " INSTALLMAN1DIR=none INSTALLMAN3DIR=none"; + +lib::core::only->import(); +local::lib->import( $target ); + +# Force a re-install of local::lib here to get the dependencies for local::lib +# It requires things which ensure we have an unfucked toolchain :) +force(qw/install local::lib/); + +# Install the base modules +install('Module::Install'); +install('YAML'); +install('CPAN'); +# For some reason this isn't installed along with M::I::Catalyst. +install('File::Copy::Recursive'); +install('Module::Install::Catalyst'); + +print "local::lib setup, type perl Makefile.PL && make installdeps to install dependencies"; + diff --git a/script/env b/script/env new file mode 100755 index 0000000..7f96991 --- /dev/null +++ b/script/env @@ -0,0 +1,79 @@ +#!/usr/bin/env perl +# vim: set filetype=perl: + +# env is a perl script similar in concept to /usr/bin/env + +# If you have a local-lib5 directory then this script will set it up for +# you as it executes. + +# If used like /usr/bin/env then it will run other commands based on +# your current path settings (with a local::lib environment if present) +# +# e.g. script/env bash +# +# NOTE: This environment _IS NOT_ self contained + +# If included inside another perl script, then it will be a no-op if +# a local::lib environment is not present, but if one is, it will be +# used as a --self-contained environment, expected to contain all non-core +# dependencies for your perl +# +# e.g. +# use FindBin; +# BEGIN { do "$FindBin::Bin/env" or die $@ } + +# The local::lib behavior can be explicitly enabled or disabled by setting +# the CATALYST_LOCAL_LIB enviromnent variable to true or false. + +use strict; +use warnings; +use Carp; +use lib; +use FindBin; + +my $basedir; +if (-r "$FindBin::Bin/Makefile.PL") { + $basedir = $FindBin::Bin; +} +elsif (-r "$FindBin::Bin/../Makefile.PL") { + $basedir = "$FindBin::Bin/.."; +} + +$basedir ||= ''; +my $target = "$basedir/local-lib5"; + +my $on = -d $target; +$on = ! ! $ENV{CATALYST_LOCAL_LIB} + if (exists $ENV{CATALYST_LOCAL_LIB} and defined $ENV{CATALYST_LOCAL_LIB}); + +Carp::confess("Could not find local-lib5 from '$FindBin::Bin'") + if ($on && ! length $basedir); + +if ( $on ) { + # So we can find local::lib when fully self contained + lib->import("$target/lib/perl5"); + + # . for CPAN + app dir + my @include = ('.', "$basedir/lib"); + + $ENV{PERL5LIB} = join ':', @include; + + # Sorry kane ;) + $ENV{PERL_AUTOINSTALL_PREFER_CPAN}=1; + + $ENV{PERL_MM_OPT} .= " INSTALLMAN1DIR=none INSTALLMAN3DIR=none"; + + require lib::core::only; + require local::lib; + lib::core::only->import(); + local::lib->import( $target ); +} + +unless ( caller ) { + if ( @ARGV ) { + exec @ARGV; + } +} + +1; + diff --git a/script/gitalist_cgi.pl b/script/gitalist_cgi.pl index 9ef29e6..4afe54e 100755 --- a/script/gitalist_cgi.pl +++ b/script/gitalist_cgi.pl @@ -1,5 +1,8 @@ #!/usr/bin/env perl +use FindBin; +BEGIN { do "$FindBin::Bin/env" or die $@ } + use Catalyst::ScriptRunner; Catalyst::ScriptRunner->run('Gitalist', 'CGI'); diff --git a/script/gitalist_create.pl b/script/gitalist_create.pl index 41cf45e..b8e3fea 100755 --- a/script/gitalist_create.pl +++ b/script/gitalist_create.pl @@ -1,4 +1,6 @@ #!/usr/bin/env perl +use FindBin; +BEGIN { do "$FindBin::Bin/env" or die $@ } use strict; use warnings; diff --git a/script/gitalist_fastcgi.pl b/script/gitalist_fastcgi.pl index 37f989b..6948d49 100755 --- a/script/gitalist_fastcgi.pl +++ b/script/gitalist_fastcgi.pl @@ -1,4 +1,6 @@ #!/usr/bin/env perl +use FindBin; +BEGIN { do "$FindBin::Bin/env" or die $@ } use Catalyst::ScriptRunner; Catalyst::ScriptRunner->run('Gitalist','FastCGI'); diff --git a/script/gitalist_server.pl b/script/gitalist_server.pl index 2e60bce..37d6b1f 100755 --- a/script/gitalist_server.pl +++ b/script/gitalist_server.pl @@ -1,4 +1,6 @@ #!/usr/bin/env perl +use FindBin; +BEGIN { do "$FindBin::Bin/env" or die $@ } BEGIN { $ENV{CATALYST_SCRIPT_GEN} = 40; diff --git a/script/gitalist_test.pl b/script/gitalist_test.pl index 67621d5..ab28c5c 100755 --- a/script/gitalist_test.pl +++ b/script/gitalist_test.pl @@ -1,4 +1,6 @@ #!/usr/bin/env perl +use FindBin; +BEGIN { do "$FindBin::Bin/env" or die $@ } use Catalyst::ScriptRunner; Catalyst::ScriptRunner->run('Gitalist','Test'); diff --git a/t/01app.t b/t/01app.t index 2ed2537..f52f36e 100644 --- a/t/01app.t +++ b/t/01app.t @@ -1,63 +1,58 @@ #!/usr/bin/env perl -use strict; -use warnings; -use Test::More; use FindBin qw/$Bin/; - -BEGIN { - $ENV{GITALIST_CONFIG} = $Bin; - $ENV{GITALIST_REPO_DIR} = ''; - use_ok 'Catalyst::Test', 'Gitalist'; -} - -ok( request('/')->is_success, 'Request should succeed' ); - -for my $p (qw/ repo1 nodescription /) { - my $path = '/summary?p=' . $p; +use lib "$Bin/lib"; +use TestGitalist; + +for my $p ('', qw{ + repo1 nodescription bare.git opml search + fragment/collectionofrepositories +}) { + my $path = '/' . $p; ok( request($path)->is_success, "$path should succeed"); } -my $response = request('/summary?p=DoesNotExist'); +my $response = request('/DoesNotExist'); is $response->code, 404, 'invalid repository 404s'; like $response->content, qr/Page not found/, 'invalid repository handled correctly'; -is request('/summary?p=../../../')->code, 404, 'directory traversal failed'; +# URI tests for repo1 +foreach my $test (map {curry_test_uri($_)} ('fragment/repo1', 'repo1') ) { + $test->(''); + $test->('shortlog'); + $test->('log'); + $test->('heads'); + $test->('tags'); + $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818'); + $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/tree'); + $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/tree/dir1'); + $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/diff'); + $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/diff/plain'); + $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/history/dir1'); + $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/history/dir1'); + $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/blame/file1'); + $test->('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/blob/file1'); +} { # URI tests for repo1 local *test = curry_test_uri('repo1'); - test('/summary'); - test('/shortlog'); - test('/log'); - test('/reflog'); - test('/commit'); - test('/commitdiff', 'h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818'); - test('/tree', 'h=145dc3ef5d307be84cb9b325d70bd08aeed0eceb;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818'); - test('/search', 'h=36c6c6708b8360d7023e8a1649c45bcf9b3bd818&f=&type=commit&text=added'); - test('/blobdiff', 'f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=refs/heads/master;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac'); - test('/blob', 'f=dir1/file2;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818'); - test('/patch'); - test('/patch', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e'); - test('/patch', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac'); - test('/patches'); - test('/patches', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e'); - test('/patches', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac'); + test('search'); + + test('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/patch'); + test('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/patches/1'); + test('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/patches/2'); + test('36c6c6708b8360d7023e8a1649c45bcf9b3bd818/raw/file1'); + + TODO: { + local $TODO = "FIXME"; + test('search', 'type=commit&text=added'); + + # FIXME - What's the difference here? + #test('patch', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e'); + #test('patch', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac'); + #test('patches', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e'); + #test('patches', 'h=3f7567c7bdf7e7ebf410926493b92d398333116e;hp=3bc0634310b9c62222bb0e724c11ffdfb297b4ac'); + } } done_testing; - -sub test_uri { - my ($p, $uri, $qs) = @_; - $qs ||= ''; - my $request = "$uri?p=repo1;$qs"; - my $response = request($request); - ok($response->is_success, "ok $p - $uri - $qs"); -} - -sub curry_test_uri { - my $p = shift; - sub { - my ($uri, $qs) = @_; - test_uri($p, $uri, $qs); - }; -}; diff --git a/t/02git_object.t b/t/02git_object.t index 3cdc2cc..bb49cf7 100644 --- a/t/02git_object.t +++ b/t/02git_object.t @@ -59,7 +59,7 @@ my $commit_obj = Gitalist::Git::Object::Commit->new( isa_ok($commit_obj, 'Gitalist::Git::Object::Commit', "commit object"); my ($tree, $patch) = $commit_obj->diff( parent => undef, - file => undef, + filename => undef, patch => 1, ); $patch = $patch->[0]; diff --git a/t/03legacy_uri.t b/t/03legacy_uri.t index 3017ef7..839d794 100644 --- a/t/03legacy_uri.t +++ b/t/03legacy_uri.t @@ -1,22 +1,34 @@ #!/usr/bin/env perl -use strict; -use warnings; -use Test::More; use FindBin qw/$Bin/; - -BEGIN { - $ENV{GITALIST_CONFIG} = $Bin; - no warnings; - $ENV{GITALIST_REPO_DIR} = undef; - use warnings; - use_ok 'Catalyst::Test', 'Gitalist'; -} +use lib "$Bin/lib"; +use TestGitalist qw/request curry_test_uri done_testing ok is $TODO/; ok( request('/')->is_success, 'Request should succeed' ); +sub test { + my ($uri, $qs) = @_; + my $request = "/$uri"; + $request =~ s{/+}{/}g; + $request .= "?$qs" if defined $qs; + my $response = request($request); + $uri = $response->header('Location') || ''; + is($response->code, 301, "ok $request 301 to " . $uri) + or return $response; + $response = request($uri); + ok($response->is_success, "ok $uri"); + return $response; +} +# FIXME # URI tests for repo1 -local *test = curry_test_uri('repo1'); +#local *test = curry_test_uri('repo1'); + +test('/', 'a=project_index'); +test('/', 'a=opml'); +no warnings 'redefine'; +local *test = curry_test_uri('repo1', \&test); +test('/', 'a=project_index'); +test('/', 'a=opml'); test('/', 'a=summary'); test('/', 'a=heads'); test('/', 'a=tags'); @@ -50,6 +62,7 @@ test('/', 'a=blob_plain;f=file1;hb=HEAD'); test('/', 'a=blob_plain;f=file1;hb=master'); test('/', 'a=blob_plain;f=file1;hb=refs/heads/master'); + test('/', 'a=blobdiff;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac'); test('/', 'a=blobdiff;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=3f7567c7bdf7e7ebf410926493b92d398333116e;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac'); test('/', 'a=blobdiff;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hp=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=HEAD;hpb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac'); @@ -98,7 +111,6 @@ test('/', 'a=commitdiff_plain;h=master;hp=3f7567c7bdf7e7ebf410926493b92d39833311 test('/', 'a=commitdiff_plain;h=refs/heads/master'); test('/', 'a=commitdiff_plain;h=refs/heads/master;hp=3f7567c7bdf7e7ebf410926493b92d398333116e'); - test('/', 'a=history;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818'); test('/', 'a=history;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=HEAD'); test('/', 'a=history;f=dir1/file2;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=master'); @@ -202,7 +214,6 @@ test('/', 'a=tree;hb=HEAD'); test('/', 'a=tree;hb=master'); test('/', 'a=tree;hb=refs/heads/master'); - test('/', 'a=atom'); test('/', 'a=atom;f=dir1'); test('/', 'a=atom;f=dir1/file2'); @@ -233,10 +244,6 @@ test('/', 'a=rss;h=refs/heads/master'); test('/', 'a=rss;h=refs/heads/master;opt=--no-merges'); test('/', 'a=rss;opt=--no-merges'); -test('/', 'a=project_index'); - -test('/', 'a=opml'); - test('/', 'a=blame;f=dir1/file2;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818'); test('/', 'a=blame;f=file1;h=257cc5642cb1a054f08cc83f2d943e56fd3ebe99;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac'); test('/', 'a=blame;f=file1;h=5716ca5987cbf97d6bb54920bea6adde242d87e6;hb=36c6c6708b8360d7023e8a1649c45bcf9b3bd818'); @@ -248,19 +255,3 @@ test('/', 'a=blame;f=file1;hb=3bc0634310b9c62222bb0e724c11ffdfb297b4ac'); test('/', 'a=blame;f=file1;hb=3f7567c7bdf7e7ebf410926493b92d398333116e'); done_testing; - -sub test_uri { - my ($p, $uri, $qs) = @_; - $qs ||= ''; - my $request = "$uri?p=repo1;$qs"; - my $response = request($request); - ok($response->is_success, "ok $request"); -} - -sub curry_test_uri { - my $p = shift; - sub { - my ($uri, $qs) = @_; - test_uri($p, $uri, $qs); - }; -}; diff --git a/t/app-mech-rootpage.t b/t/app-mech-rootpage.t new file mode 100644 index 0000000..cc6f71f --- /dev/null +++ b/t/app-mech-rootpage.t @@ -0,0 +1,33 @@ +#!/usr/bin/env perl +use FindBin qw/$Bin/; +use lib "$Bin/lib"; +use TestGitalist; +plan 'skip_all' => "One or more of the following modules aren't present: Test::WWW::Mechanize::Catalyst WWW::Mechanize::TreeBuilder HTML::TreeBuilder::XPath" unless MECH(); + +MECH->get_ok('/'); +{ + my $nodeset = MECH->findnodes('/html/body//tr[@class="reposrow"]'); + foreach my $row ($nodeset->get_nodelist) { + my $uri = $row->findnodes('.//a')->[0]->attr('href'); + my ($repos_name) = $uri =~ m{^http://localhost/([\w\.]+)$}; + ok $repos_name, "Repos name $repos_name"; + like $row->findnodes('.//a')->[1]->as_text, qr{^[\w\s/;',\.]+$}, 'Have description' + unless $repos_name eq 'nodescription'; + like $row->findnodes('.//td[@class="time-since"')->[0]->as_text, qr/^(never|\d\s+(years|months)\s+ago)$/, + 'Last change looks ok'; + my ($summary, $shortlog, $log, $tree) = $row->findnodes('.//td[@class="link"]/a')->get_nodelist; + like $summary->as_text, qr/summary/i, 'summary text ok'; + is $summary->attr('href'), $uri, 'summary href correct'; + like $shortlog->as_text, qr/shortlog/i, 'shortlog text ok'; + is $shortlog->attr('href'), "$uri/shortlog", 'shortlog href ok'; + like $log->as_text, qr/log/, 'log text ok'; + is $log->attr('href'), "$uri/log", 'log href ok'; + like $tree->as_text, qr/tree/, 'tree text ok'; + TODO: { + local $TODO = 'Bork'; + is $tree->attr('href'), "$uri/tree", 'tree href ok'; + } + } +} + +done_testing; diff --git a/t/atom.t b/t/atom.t new file mode 100644 index 0000000..13c835f --- /dev/null +++ b/t/atom.t @@ -0,0 +1,18 @@ +#!/usr/bin/env perl +use FindBin qw/$Bin/; +use lib "$Bin/lib"; +use TestGitalist; + +my $res = request('/repo1/atom'); +ok $res->is_success; +is $res->content_type, 'application/atom+xml'; +TODO: { + local $TODO = "Does not work yet. Need similar info to RSS feed"; + like $res->content, qr{link>http://localhost/repo1content, qr{description>some test repositorycontent, qr{add dir1/file2content, qr{http://localhost/repo1/36c6c6708b8360d7023e8a1649c45bcf9b3bd818/commitcontent, qr{title>add dir1/file2 + repo_dir __path_to(t/lib/repositories)__ - + name Gitalist diff --git a/t/lib/TestGitalist.pm b/t/lib/TestGitalist.pm new file mode 100644 index 0000000..dcdc539 --- /dev/null +++ b/t/lib/TestGitalist.pm @@ -0,0 +1,95 @@ +package TestGitalist; +use strict; +use warnings; +use Exporter (); +use FindBin qw/$Bin/; +BEGIN { + $ENV{GITALIST_CONFIG} = $Bin; + $ENV{GITALIST_REPO_DIR} = ''; +} +use Catalyst::Test qw/Gitalist/; +use Test::More; +use Test::Exception; + +our @EXPORT = (@Test::More::EXPORT, @Test::Exception::EXPORT, qw/ + test_uri + curry_test_uri + MECH + request + get + ctx_request + content_like + action_ok + action_redirect + action_notfound + contenttype_is +/); + +sub import { + my $into = caller(); + strict->import; + warnings->import; + goto \&Exporter::import; +} + +use constant (); +BEGIN { + my $mech = eval { + require Test::WWW::Mechanize::Catalyst; + require WWW::Mechanize::TreeBuilder; + my $mech = Test::WWW::Mechanize::Catalyst->new(catalyst_app => 'Gitalist'); + WWW::Mechanize::TreeBuilder->meta->apply($mech, + tree_class => 'HTML::TreeBuilder::XPath', + ); + return $mech; + }; + constant->import('MECH', $mech ); +} + +# Rechecking the same link multiple times is slow and lame! +# Nicked this from WWW::Mechanize and memoized it... +my %seen_links; +sub Test::WWW::Mechanize::Catalyst::page_links_ok { + my $self = shift; + my $desc = shift; + + $desc = 'All links ok' unless defined $desc; + + my @links = $self->followable_links(); + my @urls = Test::WWW::Mechanize::_format_links(\@links); + + my @failures = $self->_check_links_status( [ grep { ! $seen_links{$_}++ } @urls ] ); + my $ok = (@failures==0); + + ok( $ok, $desc ); + diag( $_ ) for @failures; + + return $ok; +} + + +sub test_uri { + my ($uri, $qs) = @_; + my $request = "/$uri"; + $request .= "?$qs" if defined $qs; + my $response = request($request); + ok($response->is_success || $response->is_redirect, "ok $request"); + if (MECH) { + my $res = MECH()->get($request); + ok $res->is_success, "ok mech $request (" . $res->code . ')'; + MECH()->page_links_ok("All links ok from $request") + if $res->content_type =~ m|text/html|; + } + return $response; +} + +sub curry_test_uri { + my $prefix = shift; + my $to_curry = shift || \&test_uri; + sub { + my $uri = shift; + $to_curry->("$prefix/$uri", @_); + }; +} + +1; diff --git a/t/lib/gitalist_testing.conf b/t/lib/gitalist_testing.conf deleted file mode 100644 index 8e0aecf..0000000 --- a/t/lib/gitalist_testing.conf +++ /dev/null @@ -1,43 +0,0 @@ -name Gitalist - -#git /path/to/git -projectroot __path_to(t/lib/repositories)__ -repo_dir __path_to(t/lib/repositories)__ - -sitename "Gitalist presently" - -# URI and label (title) of GIT logo link -logo_url git-scm.org -logo_label "git homepage" -logo_img /git-logo.png - -home_link / -home_link_str "A Gitalist" - -# XXX let's not hardcode these yeah? -version 1.6.3.3 - -# show repository only if this file exists -# (only effective if this variable evaluates to true) -# export_ok - -# XXX Code in config FAIL -# show repository only if this subroutine returns true -# when given the path to the project, for example: -# sub { return -e "$_[0]/git-daemon-export-ok"; } -# export_auth_hook - -# stylesheet path/to/your/stylesheet.css -logo /git-logo.png -favicon /git-favicon.png - -# $feature{'blame'}{'default'} = [1]; - - - default = 1 - - - -# fs traversing limit for getting project list -# the number is relative to the projectroot -project_maxdepth 2007 diff --git a/t/lib/repositories/repo1/refs/tags/0.01 b/t/lib/repositories/repo1/refs/tags/0.01 new file mode 100644 index 0000000..7c4a265 --- /dev/null +++ b/t/lib/repositories/repo1/refs/tags/0.01 @@ -0,0 +1 @@ +36c6c6708b8360d7023e8a1649c45bcf9b3bd818 diff --git a/t/model_gitrepos.t b/t/model_collectionofrepos.t similarity index 77% rename from t/model_gitrepos.t rename to t/model_collectionofrepos.t index ee78cb6..e847c33 100644 --- a/t/model_gitrepos.t +++ b/t/model_collectionofrepos.t @@ -12,9 +12,9 @@ use Class::MOP::Class; use Catalyst::Request; use Catalyst::Response; use Catalyst::Utils; -use Gitalist::Model::GitRepos; +use Gitalist::Model::CollectionOfRepos; use File::Temp qw/tempdir/; - + my $mock_ctx_meta = Class::MOP::Class->create_anon_class( superclasses => ['Moose::Object'] ); $mock_ctx_meta->add_attribute($_, accessor => $_, required => 1) for qw/request response/; $mock_ctx_meta->add_attribute('stash', accessor => 'stash', required => 1, default => sub { {} }); @@ -48,10 +48,10 @@ our $ctx_gen = sub { local %ENV = %ENV; delete $ENV{GITALIST_REPO_DIR}; -throws_ok { Gitalist::Model::GitRepos->COMPONENT($ctx_gen->(), {}) } +throws_ok { Gitalist::Model::CollectionOfRepos->COMPONENT($ctx_gen->(), {}) } qr/Cannot find repository dir/, 'Blows up nicely with no repos dir'; -throws_ok { Gitalist::Model::GitRepos->COMPONENT($ctx_gen->(), { repo_dir => '/does/not/exist' }) } +throws_ok { Gitalist::Model::CollectionOfRepos->COMPONENT($ctx_gen->(), { repo_dir => '/does/not/exist' }) } qr|Cannot find repository dir: "/does/not/exist"|, 'Blows up nicely with repos dir does not exist'; { @@ -64,25 +64,25 @@ throws_ok { Gitalist::Model::GitRepos->COMPONENT($ctx_gen->(), { repo_dir => '/d } # Note - we treat an empty list of repos as if it doesn't exist at all. -throws_ok { Gitalist::Model::GitRepos->COMPONENT($ctx_gen->(), { repos => [] } ) } +throws_ok { Gitalist::Model::CollectionOfRepos->COMPONENT($ctx_gen->(), { repos => [] } ) } qr/Cannot find repository dir/, 'Blows up nicely with no repos list'; -throws_ok { Gitalist::Model::GitRepos->COMPONENT($ctx_gen->(), { repos => [ '/does/not/exist' ] } ) } +throws_ok { Gitalist::Model::CollectionOfRepos->COMPONENT($ctx_gen->(), { repos => [ '/does/not/exist' ] } ) } qr/Cannot find repository directories/, 'Blows up nicely with repos list - 1 unknown item (array)'; -throws_ok { Gitalist::Model::GitRepos->COMPONENT($ctx_gen->(), { repos => '/does/not/exist' } ) } +throws_ok { Gitalist::Model::CollectionOfRepos->COMPONENT($ctx_gen->(), { repos => '/does/not/exist' } ) } qr/Cannot find repository directories/, 'Blows up nicely with repos list - 1 unknown item (scalar))'; -throws_ok { Gitalist::Model::GitRepos->COMPONENT($ctx_gen->(), { repos => [ '/does/not/exist', '/also/does/not/exist' ] } ) } +throws_ok { Gitalist::Model::CollectionOfRepos->COMPONENT($ctx_gen->(), { repos => [ '/does/not/exist', '/also/does/not/exist' ] } ) } qr/Cannot find repository directories/, 'Blows up nicely with repos list - 2 unknown items'; -throws_ok { Gitalist::Model::GitRepos->COMPONENT($ctx_gen->(), { repos => [ tempdir( CLEANUP => 1), '/also/does/not/exist' ] } ) } +throws_ok { Gitalist::Model::CollectionOfRepos->COMPONENT($ctx_gen->(), { repos => [ tempdir( CLEANUP => 1), '/also/does/not/exist' ] } ) } qr|Cannot find repository directories.*/also/does/not/exist|, 'Blows up nicely with repos list - 1 known, 1 unknown items'; { my $td = tempdir( CLEANUP => 1 ); local %ENV = %ENV; $ENV{GITALIST_REPO_DIR} = $td; - lives_ok { Gitalist::Model::GitRepos->COMPONENT($ctx_gen->(), {}) } 'GITALIST_REPO_DIR env variable works'; + lives_ok { Gitalist::Model::CollectionOfRepos->COMPONENT($ctx_gen->(), {}) } 'GITALIST_REPO_DIR env variable works'; } { @@ -104,7 +104,7 @@ sub test_with_config { my $ctx = $ctx_gen->(); my $m; - lives_ok { $m = Gitalist::Model::GitRepos->COMPONENT($ctx, $config) } $msg; + lives_ok { $m = Gitalist::Model::CollectionOfRepos->COMPONENT($ctx, $config) } $msg; ok $m, 'Has model'; my $i = $m->ACCEPT_CONTEXT($ctx); ok $i, 'Has model instance from ACCEPT_CONTEXT'; diff --git a/t/opml.t b/t/opml.t new file mode 100644 index 0000000..d468b44 --- /dev/null +++ b/t/opml.t @@ -0,0 +1,15 @@ +#!/usr/bin/env perl +use FindBin qw/$Bin/; +use lib "$Bin/lib"; +use TestGitalist; + +my $res = request('/opml'); +ok $res->is_success; + +is $res->content_type, 'application/rss'; +like $res->content, qr{Gitalist}; +like $res->content, qr{xmlUrl="http://localhost/bare.git/rss"}; +like $res->content, qr{text="repo1 - some test repository"}; +like $res->content, qr{xmlUrl="http://localhost/repo1/rss"}; + +done_testing; diff --git a/t/rss.t b/t/rss.t new file mode 100644 index 0000000..2d6a143 --- /dev/null +++ b/t/rss.t @@ -0,0 +1,15 @@ +#!/usr/bin/env perl +use FindBin qw/$Bin/; +use lib "$Bin/lib"; +use TestGitalist; + +my $res = request('/repo1/rss'); +ok $res->is_success; +is $res->content_type, 'application/rss+xml'; +like $res->content, qr{link>http://localhost/repo1content, qr{description>some test repositorycontent, qr{title>add dir1/file2content, qr{description>add dir1/file2content, qr{guid isPermaLink="true">http://localhost/repo1/36c6c6708b8360d7023e8a1649c45bcf9b3bd818/commitrun('Gitalist', 'Server'); + +1; +