# Copyright (C) 2011 Alex Schroeder # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation, either version 3 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program. If not, see . =head1 NAME git - An Oddmuse module to save all changes made into a git repository. =head1 INSTALLATION This module is easily installable; move this file into the B subdirectory for your data directory (C<$DataDir>). =cut package OddMuse; =head1 CONFIGURATION Set these variables in the B file within your data directory. =head2 $GitBinary Default: C The fully qualified name for the binary to run. Your PATH will not be searched. =head2 $GitRepo Default: C<$DataDir/git> The directory in which the repository resides. If it doesn't exist, Oddmuse will create it for you. =head2 $GitMail Default: C The email address used to identify users in git. =cut use Cwd; use vars qw($GitBinary $GitRepo $GitMail $GitPageFile); $GitBinary = 'git'; $GitMail = 'unknown@oddmuse.org'; $GitPageFile = 0; push(@MyInitVariables, \&GitInitVariables); sub GitRun { my $oldDir = cwd; chdir($GitRepo); my $result = ''; local *STDOUT; open(STDOUT, '>', \$result) or die "Can't open memory file: $!"; my $exitStatus = system($GitBinary, @_); chdir($oldDir); if (not defined wantarray) { # void context return if $exitStatus == 0; ReportError("git failed: $!", '500 INTERNAL SERVER ERROR', undef, $q->p($q->tt(join(' ', $GitBinary, map { if (index($_, ' ') == -1) { $_; } else { "'$_'"; } } @_))), $q->pre($result)); } else { return $exitStatus; } } sub GitInitVariables { $GitRepo = $DataDir . '/git'; } sub GitInitRepository { return if -d "$GitRepo/.git"; CreateDir($GitRepo); GitRun(qw(init --quiet)); foreach my $id (AllPagesList()) { OpenPage($id); WriteStringToFile("$GitRepo/$id", $GitPageFile ? EncodePage(%Page) : $Page{text}); GitRun(qw(add --), $id); } GitRun(qw(commit --quiet -m), 'initial import', "--author=Oddmuse <$GitMail>"); } *GitOldSave = *Save; *Save = *GitNewSave; sub GitNewSave { GitOldSave(@_); GitInitRepository(); my ($id) = @_; WriteStringToFile("$GitRepo/$id", $GitPageFile ? EncodePage(%Page) : $Page{text}); GitRun(qw(add --), $id) if ($Page{revision} == 1); my $message = $Page{summary}; $message =~ s/^\s+$//; $message ||= T('no summary available'); my $author = $Page{username} || T('Anonymous'); GitRun(qw(commit --quiet -m), $message, "--author=$author <$GitMail>", '--', $id); } *GitOldDeletePage = *DeletePage; *DeletePage = *GitNewDeletePage; sub GitNewDeletePage { my $error = GitOldDeletePage(@_); return $error if $error; GitInitRepository(); my ($id) = @_; GitRun(qw(rm --quiet --ignore-unmatch --), $id); my $message = T('page was marked for deletion'); my $author = T('Oddmuse'); GitRun(qw(commit --quiet -m), $message, "--author=$author <$GitMail>", '--', $id); return ''; # no error } push(@MyMaintenance, \&GitCleanup); $Action{git} = \&DoGitCleanup; sub DoGitCleanup { UserIsAdminOrError(); print GetHeader('', 'Git', ''); print $q->start_div({-class=>'content git'}); RequestLockOrError(); print $q->p(T('Main lock obtained.')), '

', T('Cleaning up git repository'); GitCleanup(); ReleaseLock(); print $q->p(T('Main lock released.')), $q->end_div(); PrintFooter(); } sub GitCleanup { if (-d $GitRepo) { print $q->p('Git cleanup starting'); AllPagesList(); # delete all the files including all the files starting with a dot opendir(DIR, $GitRepo) or ReportError("cannot open directory $GitRepo: $!"); foreach my $file (readdir(DIR)) { my $name = $file; utf8::decode($name); # filenames are bytes next if $file eq '.git' or $file eq '.' or $file eq '..' or $IndexHash{$name}; print $q->p("Deleting left over file $name"); unlink "$GitRepo/$file" or ReportError("cannot delete $GitRepo/$name: $!"); } closedir DIR; # write all the files again, just to be sure print $q->p('Rewriting all the files, just to be sure'); foreach my $id (@IndexList) { OpenPage($id); WriteStringToFile("$GitRepo/$id", $GitPageFile ? EncodePage(%Page) : $Page{text}); } # run git! # add any new files print $q->p('Adding new files, if any'); GitRun(qw(add -A)); # commit the new state print $q->p('Committing changes, if any'); my $exitStatus = GitRun(qw(commit --quiet -m), 'maintenance job', "--author=Oddmuse <$GitMail>"); print $q->p('git commit finished with ' . $exitStatus . ' exit status.'); print $q->p('Git done'); } }