#!/usr/bin/env perl package _TMP_SCRIPT; use strict; use warnings; use HTTP::Cookies; use LWP::UserAgent; use Data::Dumper; use File::Temp; $|++; my $VERBOSE = 0; my $BaseURL = 'http://intranet.some-domain.com:8080/wiki'; my $CookieDomain = '.some-domain.com'; my $RCDirectory = "$ENV{HOME}/.jspwiki"; my $CookieFile = "$RCDirectory/cookie-jar"; my $ParamsFile = "$RCDirectory/params.ser"; my $UserAgent; my %Params; my %Ligatures = ( '"' => '"', '>' => '>', '<' => '>', '&' => '&', ); ################################################################################ unless (-d $RCDirectory) { system(qq{mkdir -p $RCDirectory}); } END { writeFile($ParamsFile,Data::Dumper::Dumper(\%Params)); } sub setJSPWikiUserProfile { # prime the UA with any session cookies, etc. my $resp = $UserAgent->get("$BaseURL/UserPreferences.jsp"); $resp = $UserAgent->post("$BaseURL/UserPreferences.jsp", { username=>$Params{JSPWikiUserProfile}, ok=>'Set my preferences!', action=>'save', }); } sub readFile { my($file) = @_; my $fh; unless (open $fh, "<", $file ) { die "Error opening file for reading: $file : $!\n"; } my $data; { local $/ = undef; $data = <$fh>; } close $fh; return $data; } sub getUserParam { my($paramName) = @_; unless ($Params{$paramName}) { print "$paramName: "; my $answer = ; chomp $answer; $Params{$paramName} = $answer; } return $Params{$paramName}; } sub getUserAgent { my $cookies = HTTP::Cookies->new(file=>$CookieFile,autosave=>1); my $ua = LWP::UserAgent->new; $ua->cookie_jar($cookies); return $ua; } sub writeFile { my($file,@content) = @_; my $fh; unless (open $fh, ">", $file) { die "Error opening file for writing: $file : $!\n"; } print $fh @content; close $fh; return 1; } sub fetchTopicPage { my($topic,$ver) = @_; my $uri = "$BaseURL/Edit.jsp?page=$topic"; $uri .= "&version=$ver" if $ver; my $resp = $UserAgent->get($uri); return $resp->content; } sub topicToFile { my($topic) = @_; return "$Params{WorkingDirectory}/$topic.txt"; } sub diffTopic { my($topic,$ver1,$ver2) = @_; my $localFile = topicToFile($topic); unless (-r $localFile) { die "Error: local topic file does not exist ($localFile), can not diff until you have a local copy.\n"; } if ($ver2) { system("bash","-c","diff -U 2 <($ $0 get-content $topic $ver1) <($ $0 get-content $topic $ver2)"); return 1; } $ver1 ||= ''; `bash -c 'diff -U 2 <("$" "$0" get-content "$topic" "$ver1") "$localFile"'`; } sub diffAllTopics { foreach my $topic (getLocalTopics()) { my $diff = diffTopic($topic); next unless $diff; print "Topic: $topic\n"; print " Local File: ",topicToFile($topic),"\n"; print " Wiki URL: $BaseURL/wiki.jsp?page=$topic\n"; print $diff; } } sub makeParser { my($data) = @_; my $pos = 0; my $setData = sub { $data = $_[0]; $pos = 0; }; my $start = sub { $pos = 0 }; my $fwd = sub { return -1 if $pos == -1; $pos += $_[0]; $pos = -1 if $pos >= length($data); $pos }; my $bck = sub { return -1 if $pos == -1; $pos -= $_[0]; $pos = -1 if $pos < 0; $pos }; my $bckTo = sub { return -1 if $pos == -1; $pos = rindex $data, $_[0], $pos; }; my $fwdTo = sub { return -1 if $pos == -1; $pos = index $data, $_[0], $pos; }; my $fwdPast = sub { return $pos if $pos == -1; $pos = index $data, $_[0], $pos; return $pos if $pos == -1; $pos += length($_[0]); $pos >= length($data) ? $pos = -1 : $pos; }; my $btwn = sub { return -1 if $pos == -1; my $s = $fwdPast->($_[0]); return undef if $s == -1; my $e = $fwdTo->($_[1]); return undef if $e == -1; my $item = substr $data, $s, $e - $s; return $item; }; my $all = sub { my @all; while (-1 != $pos) { my $item = $btwn->(@_); last unless $item; push @all, $item; } return @all; }; return ($setData,$start,$fwd,$bck,$fwdTo,$fwdPast,$bckTo,$btwn,$all); } sub getPageInfo { my($topic) = @_; my $data = $UserAgent->get("$BaseURL/PageInfo.jsp?page=$topic")->content; #print "$data\n"; my($setData,$start,$fwd,$bck,$fwdTo,$fwdPast,$bckTo,$btwn,$all) = makeParser($data); my $table = $btwn->('Version',''); ($setData,$start,$fwd,$bck,$fwdTo,$fwdPast,$bckTo,$btwn,$all) = makeParser($table); my $s = join("\t",qw(Version Date Author Size Changes from Previous))."\n"; $s .= dumpRow($_) for $all->('',''); return $s; } sub dumpRow { my($row) = @_; my($setData,$start,$fwd,$bck,$fwdTo,$fwdPast,$bckTo,$btwn,$all) = makeParser($row); return join("\t",map { simpleStrip($_) } $all->('',''))."\n"; } sub simpleStrip { my($text) = @_; $text =~ s|<[^>]+>||msg; $text =~ s|\s+| |msg; $text =~ s|^\s+||; $text =~ s|\s+$||; $text; } sub fetchTopicContent { my($topic,$ver) = @_; my $content = fetchTopicPage($topic,$ver); $VERBOSE and print "CONTENT: $content\n"; $content =~ m|(.+?)|ims; my $text = $1; $VERBOSE and print "GOT BACK: $text\n"; unless ($text) { $text = ''; } $text =~ s/$_/$Ligatures{$_}/eg for( keys %Ligatures ); return $text; } sub fetchTopic { my($topic) = @_; my $content = fetchTopicContent($topic); $content = '' unless defined $content; writeFile(topicToFile($topic),$content); } sub getTopicEditTime { my($topic) = @_; my $prev = fetchTopicPage($topic); $prev =~ m|name="edittime"\s+value="(.+?)">|ims; my $edittime = $1; return $edittime; } sub postTopic { my($topic) = @_; my $edittime = getTopicEditTime($topic); my $content = readFile(topicToFile($topic)); my $result = $UserAgent->post ( "$BaseURL/Edit.jsp?page=$topic", { page=>$topic, action=>'save', edittime=>$edittime, text=>$content, ok => 'Save', } ); print "Response Code: ",$result->code,"\n"; print $result->content; } sub postAllTopics { foreach my $topic (getLocalTopics()) { if (diffTopic($topic)) { print "Posting: $topic\n"; postTopic($topic); } } } sub chip ($) { my($s) = @_; substr($s,0,1) = ''; $s; } sub getTopicsFromContent { my($content) = @_; $content =~ s|\{{3}.+?\}{3}||gms; grep /^[A-Z]/, map { chop $_; $_ = chip $_; my @parts = split('\|',$_,2); $parts[-1]; } ( $content =~ m|\[[^\[\]]+?\]|msg ); } sub listTopicsFromPage { my($topic) = @_; getTopicsFromContent(fetchTopicContent($topic)); } sub getLocalTopics { map { s|/([^/]+).txt$||; $1; } glob "$Params{WorkingDirectory}/*.txt"; } {my %seen; sub dumpWiki { my($startTopic) = @_; return if $seen{$startTopic}++; print "DUMP: $startTopic:\n"; fetchTopic($startTopic); foreach my $topic (listTopicsFromPage($startTopic)) { unless ($topic) { warn "Huh, got a zero-len topic from $startTopic\n"; next; } dumpWiki($topic); } }} sub displayHelp { exec('perldoc',$0); } sub editTopic { my($topic) = @_; if (-e topicToFile($topic) && diffTopic($topic)) { print "Error, you have a local copy of $topic and it is different\n"; print diffTopic($topic),"\n"; print "Please fix this situation before editing the topic in this way.\n"; print "Shall I update it now? (Throwing away local diffs) [y/n]: "; my $answer = ; chomp $answer; return undef unless 0 == index($answer,'y'); print "Overwring local copy with fresh copy from wiki\n"; fetchTopic($topic); } fetchTopic($topic) unless -e topicToFile($topic); # fix for Keith }:-) if ($ENV{EDITOR} =~ m|/?gvim$| ) { system($ENV{EDITOR},'-f',topicToFile($topic)); } else { system($ENV{EDITOR}||'vi',topicToFile($topic)); } if (my $diff = diffTopic($topic)) { print $diff,"\n"; print "Commit back to the wiki [y/n]? "; my $answer = ; chomp $answer; return undef unless 0 == index($answer,'y'); postTopic($topic); } } ################################################################################ if (-e $ParamsFile) { my $params = readFile($ParamsFile); $VERBOSE and print "PARMS: $params\n"; my $VAR1; # for Dumper support %Params = %{ eval $params }; } my $UserNameWasSet = exists $Params{JSPWikiUserProfile}; getUserParam($_) for qw(JSPWikiUserProfile WorkingDirectory); $UserAgent = getUserAgent(); setJSPWikiUserProfile() unless $UserNameWasSet; my %dispatch = ( 'get' => sub { fetchTopic(@_) }, 'put' => sub { postTopic(@_) }, 'get-raw' => sub { print "$_[0]:\n",fetchTopicPage(@_),"\n"; }, 'get-content' => sub { print fetchTopicContent(@_); }, 'diff' => sub { print diffTopic(@_) }, 'page-info' => sub { print getPageInfo(@_) }, 'list-topics' => sub { print join("\n",listTopicsFromPage(@_)),"\n"; }, 'get-topics' => sub { fetchTopic($_) for listTopicsFromPage(@_); }, 'dump-wiki' => sub { dumpWiki($_[0]||'Main') }, 'edit' => sub { editTopic(@_) }, ); my %noTopicDispatch = ( 'diff' => \&diffAllTopics, 'put' => \&postAllTopics, 'dump-wiki' => sub { dumpWiki($_[0]||'Main') }, 'help' => sub { displayHelp() }, ); my %allCommands = map { $_=>1 } (keys(%noTopicDispatch),keys(%dispatch)); my $usage = "$0 topic\n Commands: (".join('|',sort(keys %allCommands)).")\n"; my($cmd,$topic,@args) = @ARGV; unless ($cmd) { die "Error: you must supply a command on the command line: $usage\n"; } if (not($topic) && exists $noTopicDispatch{$cmd}) { $noTopicDispatch{$cmd}->(@args); exit 0; } unless ($topic) { print "Error: you must supply a topic on the command line: $usage\n"; print "Your currently downloaded topics are:\n"; print " $_\n" for getLocalTopics(); exit 1; } unless (exists $dispatch{$cmd}) { die "Error: you supplied an invalid command: '$cmd': $usage\n"; } $dispatch{$cmd}->($topic,@args); exit 0; __DATA__ =head1 NAME jspwiki - Wiki Interaction and Editing Tool =head1 SYNOPSIS jspwiki edit TopicName jspwiki get TopicName jspwiki put TopicName jspwiki get-raw TopicName jspwiki get-content TopicName jspwiki diff TopicName jspwiki diff TopicName ver1 jspwiki diff TopicName ver1 ver2 jspwiki page-info TopicName jspwiki list-topics TopicName jspwiki get-topics TopicName jspwiki dump-wiki jspwiki dump-wiki TopicName jspwiki help =head1 DESCRIPTION This tool interacts with the HMS JSPWiki instance at http://intranet.some-domain.com:8080/wiki. It works similarly to CVS. It can check out a Wiki topic, check back in a Wiki topic, diff and spider the Wiki (dumping it to the local file system). =head1 COMMANDS edit topic-name Fetch the topic content from the Wiki, execute your editor, then push the changed file (if it changed) back to the Wiki. get topic-name Retreives a topic to a local file. put topic-name put Pushes a topic back to the wiki from a local file. With no topic name as an argument it puts all the topics from your local working directory back to the wiki (if they are different). get-raw topic-name Prints the raw HTML for a topic page. get-content topic-name Prints the content for a topic. diff topic-name diff topic-name ver1 diff topic-name ver1 ver2 With no arguments, this shows a diff for each topic you have in your local working copy. With a single topic name as an argument it shows a diff between the wiki and your local working copy. With version arguments after the topic name it shows the diff between those versions. If the second version is left blank, it diffs between the specified version and the local copy. page-info topic-name Dumps the version history of the page as tab data. list-topics topic-name Lists the topics that are linked to by the given topic. get-topics topic-name Fetches the topics linked to by the given topic. dump-wiki dump-wiki topic-name Spiders the wiki from the given topic-name (defaults to Main) to your local working directory. help Displays this document. =head1 ENVIRONMENT HOME - the location of the .jspwiki parameters. EDITOR - the editor invoked when you edit a topic. =head1 FILES $HOME/.jspwiki/cookie-jar This stores cookies for the Wiki, like your JSPWikiUserProfile. $HOME/.jspwiki/params.ser This is a store for jspwiki settings, such as the local working directory. =head1 AUTHORS =head1 SEE ALSO cvs http://www.jspwiki.org/ http://intranet.some-domain.com:8080/wiik =cut