package WWW::Mechanize::TWiki;
use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter WWW::Mechanize);
our %EXPORT_TAGS = ( 'all' => [ qw() ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw();
our $VERSION = '0.10';
use WWW::Mechanize;
use HTML::TableExtract;
################################################################################
sub new {
my $class = shift;
my %args = @_;
my $self = $class->SUPER::new( %args );
return $self;
}
################################################################################
sub cgibin {
my $self = shift;
my $cgibin = shift or $self->{cgibin};
die "no cgibin?" unless $cgibin;
my $opts = shift;
$self->{cgibin} = $cgibin;
$self->{scriptSuffix} = $opts->{scriptSuffix} || '';
return $self;
}
################################################################################
sub pub
{
my $self = shift;
my $pub = shift or $self->{pub};
die "no pub?" unless $pub;
return $self->{pub} = $pub;
}
################################################################################
sub getPageList
{
my $self = shift;
my $iWeb = shift;
my $tagStartTopics = '__TOPICS__';
my $xxx = $self->search( $iWeb, {
skin => '', # has no (real/positive) effect during a search :-(
nosearch => 'on',
nototal => 'on',
scope => 'topic',
search => '.+',
regex => 'on',
format => '$topic',
separator => '$n',
header => "!$tagStartTopics",
} );
my $topic = $xxx->content();
$topic =~ s|^.+?$tagStartTopics||s; # strip up to start tag
$topic =~ s|
.+?$||s; # strip after formatted output
my @topics = ();
while ( $topic =~ /([^<]+?)<\/topic>/gi )
{
push @topics, "$iWeb.$1";
# push @topics, $1;
}
return @topics;
}
################################################################################
sub getAttachmentsList
{
my $self = shift;
my $topic = shift;
my $parms = shift;
my @attachments = ();
my $attachments = $self->attach( $topic )->content();
my @cols = qw( Attachment Comment Attribute );
# qw(I Attachment Action Size Date Who Comment Attribute)
my $te = HTML::TableExtract->new( headers => [ @cols ] ) or die $!;
$te->parse( $attachments );
foreach my $row ($te->rows)
{
my %attach = ();
my $idxCol = 0;
foreach my $col ( @cols )
{
my $data = $row->[ $idxCol++ ];
$data =~ s/^\s+//;
$data =~ s/\s+$//;
$attach{$col} = $data;
}
( my $attachTopic = $topic ) =~ s|\.|\/|;
$attach{_filename} = $attach{Attachment};
$attach{Attachment} = "$self->{pub}/$attachTopic/" . $attach{_filename};
push @attachments, {
%attach,
};
}
return @attachments;
}
################################################################################
# maps function calls into twiki urls
sub AUTOLOAD {
our ($AUTOLOAD);
no strict 'refs';
(my $action = $AUTOLOAD) =~ s/.*:://;
*$AUTOLOAD = sub {
my ($self, $topic, $args) = @_;
die "no cgibin" unless $self->{cgibin};
die "no topic on action=[$action]" unless $topic;
(my $url = URI->new( "$self->{cgibin}/$action$self->{scriptSuffix}/$topic" ))->query_form( $args );
my $response = $self->get( $url );
my $u = URI->new( $url );
my $error = {};
if ( grep { /^oops\b/ } $u->path_segments() )
{
my %form = $u->query_form();
( $error->{error} = $form{template} ) =~ s/^oops(.+?)/$1/;
# ( $error->{error} = $form{template} ) =~ s/^oops(.+?)err/$1/;
# delete $form{template};
# convert all the named (semi-) generic param# parameters into a perl array
map {
push @{ $error->{message} }, $form{ $_ }
} sort grep { /^param\d+$/ } keys %form;
}
# print STDERR Data::Dumper::Dumper( $response );
# http://localhost/~twiki/cgi-bin/twiki/oops/TWikitestcases/ATasteOfTWiki?template=oopssaveerr¶m1=Save%20attachment%20error%20/Users/twiki/Sites/htdocs/twiki/TWikitestcases/ATasteOfTWiki/TWikiInstaller.smlp%20is%20not%20writable
# print STDERR Data::Dumper::Dumper( $response->request );
$response;
};
goto &$AUTOLOAD;
}
################################################################################
sub DESTROY
{
}
1;
__END__
=head1 NAME
WWW::Mechanize::TWiki - WWW::Mechanize subclass to navigate TWiki wikis
=head1 SYNOPSIS
This document describes a subclass of WWW::Mechanize. Knowledge of WWW::Mechanize usage is assumed.
use Basename;
use WWW::Mechanize::TWiki;
my $mech = WWW::Mechanize::TWiki->new( agent => File::Basename::basename( $0 ), autocheck => 1 ) or die $!;
$mech->cgibin( 'http://ntwiki.ethermage.net/~develop/cgi-bin', { scriptSuffix => '' } );
# get a list of topics in the _default web (typically somewhere around 11 topics)
my @topics = $mech->getPageList( '_default' );
# create a new page (no modifications, just use the template)
my $topic = 'Tinderbox.TestsReportSvn' .$svnRev";
$mech->edit( "$topic", {
topicparent => 'WebHome',
templatetopic => 'TestReportTemplate',
formtemplate => 'TestReportForm',
} );
$mech->click_button( value => 'Save' );
# attach a file to the newly-created topic
$mech->follow_link( text => 'Attach' );
$mech->submit_form( fields => {
filepath => 'report.txt',
filecomment => `date`,
hidefile => undef,
} );
# change a topic
$mech->edit( "$topic", 1 );
$mech->field( text => 'New topic text' );
$mech->click_button( value => 'Save' );
# append to a topic
$mech->edit( "$topic", 1 );
my $text = $mech->field( text );
$text .= " * Adding to the text! `date`";
$mech->field( text => $text );
$mech->click_button( value => 'Save' );
=head1 DESCRIPTION
WWW::Mechanize::TWiki provides a programatic interface to TWiki's REST interface. It does this by
mapping perl functions and data structures onto a TWiki URI.
For example, WWW::Mechanize::TWiki will turn this method call
$mech->edit( 'Tinderbox.TestsReportSvn', {
topicparent => 'WebHome',
templatetopic => 'TestReportTemplate',
formtemplate => 'TestReportForm',
} );
into the following URI: (encoding as needed)
http://twiki.org/cgi-bin/twiki/edit/Tinderbox.TestReport?topicparent=WebHome;templatetopic=TestReportTemplate;formtemplate=TestReportForm
(or http://twiki.org/cgi-bin/twiki/edit.cgi/Tinderbox.TestReport..., or
http://twiki.org/cgi-bin/twiki/edit.pl/Tinderbox.TestReport..., etc. depending
on the scriptSuffix option passed to cgibin())
This is the added functionality on top of CPAN:WWW::Mechanize.
CPAN:WWW::Mechanize functions can still be called, naturally.
=head2 Setup / Configuration
=head3 cgibin( cgi-uri, { scriptSuffix } );
Gets or sets the URI cgi-bin directory of the TWiki scripts
$mech->cgibin( 'http://twiki.org/cgi-bin/twiki/' );
print $mech->cgibin();
>http://twiki.org/cgi-bin/twiki/
$mech->cgibin( 'http://tinderbox.wbniv.wikihosting.com/cgi-bin/twiki/', { scriptSuffix => '.cgi' } );
print $mech->cgibin();
>http://tinderbox.wbniv.wikihosting.com/cgi-bin/twiki/
=head3 pub( pub-uri );
Gets or sets the URI of the TWiki pub directory
setting pub is optional, although generally recommended. it is required for downloading or managing
attachments.
=head2 Web Methods
=head3 getPageList( webName );
Returns an array of (fully-qualified) topic names for the specified webName
my @topics = $mech->getPageList( '_default' );
print "@topics\n";
>WebChanges WebHome WebIndex WebLeftBar WebNotify WebPreferences WebRss WebSearch WebSearchAdvanced WebStatistics WebTopicList
my @topics = $mech->getPageList( '_empty' );
print "@topics\n";
>
=head2 Topic Methods
=head3 getAttachmentsList( topicName );
Returns an array of attachments of a fully-qualified topicName (includes wiki web name).
Each array element is a hash reference which is keyed by the column names.
my @attachments = getAttachmentsList( 'TWiki.WabiSabi' );
print Data::Dumper::Dumper( \@attachments );
>$VAR1 = [
> { 'filename' => 'report.txt', comment => '', hidden => '' },
> { 'filename' => 'report2.txt', comment => '', hidden => 'h' },
>];
=head2 Automatic Methods and Parameters
Invoking method that isn't listed above will construct a URI based on
the method's name and its parameters (in a hash reference) and
forwards it using WWW::Mechanize::get().
=head2 EXPORT
None by default.
=head1 DEPENDENCIES
CPAN:WWW::Mechanize
CPAN:HTML::TableExtract
=head1 SEE ALSO
WWW::Mechanize, http://twiki.org
=head1 TODO
cgibin and pub parameters should be able to be specified in the constructor
document use with CPAN:LWP::UserAgent::TWiki::TWikiGuest
(and understand how to make other agents for use with LDAP, etc.)
getAttachmentList is very specific, but it is built upon a general algorithm
to convert a table into a perl array of hash references; make a method
publically available
look into ways for a TWiki installation to "publish" its interface
=head1 AUTHOR
Will Norris, Ewbniv@cpan.orgE
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2004 by Will Norris
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.4 or,
at your option, any later version of Perl 5 you may have available.
=cut