#!/usr/bin/perl use strict ; use warnings ; use locale ; use POSIX ; use KikiGrammar ; use CGI qw(:all keywords) ; use CGI::Carp qw(fatalsToBrowser) ; use HTTP::Date ; use File::Path ; use Fcntl ':flock' ; # The name of the start-page use constant STARTPAGE => 'kiki' ; use constant COLLISION_WARNING => <Danger, Will Robinson!

Someone saved the page while you were editing it!

Now you can either save your changes, which will clobber the changes the other save made, or you can use the "Unedit"-link to view the changes of the other save. Open in a new window if you don't want to lose your own changes. You can manually diff the changes in this way. EOF use constant HISTORY_SIZE => 5 ; if ($ENV{REDIRECT_URL} and $ENV{REDIRECT_URL} =~ m!^/cgi-bin/!) { print redirect('/kiki/?'.join '+',keywords()) ; exit ; } # This directory must exist and be writable by the httpd-user. my $kikidir = '/srv/www/data/kiki' ; chdir $kikidir ; chroot $kikidir ; # The homepage outside kiki my $homepage = '/' ; my $title = get_title() ; my $kikipage = find_file($title) ; my $mtime = (stat $kikipage)[9] ; my $collision = 0 ; if (request_method() eq 'POST') { print header(-type=>'text/plain', -status=>405) ; print "POST not allowed."; exit; } #if (param('body') and param('title')) { # if (param('timestamp') <= $mtime) { # # Page was saved by someone else during editing # $collision = 1 ; # } else { # write_file($kikipage, param('body')) ; # print redirect(url().'?'.param('title')) ; # exit ; # } #} my $body = get_body() ; if (get_edit() or !$body or $collision) { $body = get_editform($body) ; $body = COLLISION_WARNING . $body if $collision ; } else { $body = kikify(htmlify($body)) ; } my @history = handle_history($title) ; my $history_cookie = cookie('session_history',\@history) ; print header(-cookie=>$history_cookie, -last_modified=>time2str($mtime), -expires=>'-1d') ; print parse_template($title,$body,$mtime ? strftime('%F %T', localtime $mtime) : '',\@history) ; exit ; # The rest is subs and data sub handle_history { my $title = shift ; my @history = cookie('session_history') ; push @history, $title unless grep { defined($_) and lc($_) eq lc($title) } @history ; @history = @history[-(HISTORY_SIZE) .. -1] if @history > HISTORY_SIZE ; return @history ; } sub get_title { my $title ; if (param('save')) { $title = param('title') ; } elsif (param('edit')) { $title = param('edit') ; } else { $title = (join ' ', keywords()) ; } $title =~ s![^\w -]+!!g ; $title ||= STARTPAGE ; return $title ; } sub get_edit { return param('edit') ; } sub get_body { my $body = param('body') || read_file($kikipage) ; $body = $1 if $_ and /^(.+)$/s ; return $body ; } sub read_file { local $/ = undef ; my $filename = shift ; open FILE, '<', $filename or return undef ; flock FILE, LOCK_SH ; my $contents = ; flock FILE, LOCK_UN ; close FILE ; return $contents ; } sub write_file { my $filename = shift ; my $contents = shift ; $contents =~ s!\r\n|\r!\n!g ; rename($filename, "$filename.$mtime") ; open FILE, '>', $filename or die "Could not open file $filename for writing: $!\n" ; flock FILE, LOCK_EX ; print FILE $contents ; flock FILE, LOCK_UN ; close FILE ; } sub parse_template { my ($title, $body, $mtime, $historyref) = @_ ; my @history = @{$historyref} ; my $historylinks = join ' - ', map { (my $href = my $link = $_) =~ s! !+!g; "$link" } @history ; my $template = get_template() ; my $url = $ENV{REDIRECT_URL} || url() ; (my $link = $title) =~ s/ /+/g ; my $edit = get_edit() || $collision ? "Stop editing" : "Edit this page" ; my %tags = ( url => $url, title => $title, body => $body, edit => $edit, history => $historylinks, mtime => $mtime ? "Last edited $mtime" : '', homepage => $homepage ) ; $template =~ s/{(\w+?)}/$tags{$1}/g ; return $template ; } sub get_template { local $/ = undef ; my $template = ; return $template ; } sub get_editform { my $body = shift || '' ; return join '', (h2("Describe \"$title\" here:"), start_form(-action=>"?edit=$title"), textarea(-name=>'body',-default=>"\n$body", -rows=>60, -columns=>100, -wrap=>'soft'), hidden('title', $title), hidden('timestamp', time()),br(), submit('save', 'Save'),p(a({-href=>'?Formatting+rules'},'Formatting rules')), end_form) ; } sub htmlify { local $_ = shift ; s!&!&!g ; s!"!"!g ; #" #s!'!'!g ; #' s!!>!g ; return $_ ; } sub find_file { my $title = shift ; my $page = lc $title ; $page =~ s! !_!g ; $page =~ s![^\w-]+!!g ; $page = $page =~ /^([\w'-]+)/ ? $1 : exit ; return $page ; } sub kikify { local $_ = shift ; $::RD_AUTOACTION = q{ { $return = [@item[1..$#item]] } } ; my $kikiparser = KikiGrammar->new() ; $Parse::RecDescent::KikiGrammar::skip = '' ; my $parsed_text = $kikiparser->page($_) ; defined($parsed_text) or die "Failed to parse.\n"; $parsed_text = flatten($parsed_text) ; return $parsed_text ; } sub flatten { my $result = '' ; foreach (@_) { if ('ARRAY' eq ref $_) { $result .= flatten(@{$_}) ; } else { $result .= $_ ; } } return $result ; } __DATA__ {title}
{body}
History: {history}