File Coverage

blib/lib/Socialtext/Wikrad.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Socialtext::Wikrad;
2 1     1   22679 use strict;
  1         2  
  1         33  
3 1     1   5 use warnings;
  1         2  
  1         25  
4 1     1   389 use Curses::UI;
  0            
  0            
5             use Carp qw/croak/;
6             use File::Path qw/mkpath/;
7             use base 'Exporter';
8             our @EXPORT_OK = qw/$App/;
9              
10             our $VERSION = '0.07';
11              
12             =head1 NAME
13              
14             Socialtext::Wikrad - efficient wiki browsing and editing
15              
16             =head1 SYNOPSIS
17              
18             my $app = Socialtext::Wikrad->new(rester => $rester);
19             $app->set_page( $starting_page );
20             $app->run;
21              
22             =cut
23              
24             our $App;
25              
26             sub new {
27             my $class = shift;
28             $App = {
29             history => [],
30             save_dir => "$ENV{HOME}/wikrad",
31             @_ ,
32             };
33             die 'rester is mandatory' unless $App->{rester};
34             $App->{rester}->agent_string("wikrad/$VERSION");
35             bless $App, $class;
36             $App->_setup_ui;
37             return $App;
38             }
39              
40             sub run {
41             my $self = shift;
42              
43             my $quitter = sub { exit };
44             $self->{cui}->set_binding( $quitter, "\cq");
45             $self->{cui}->set_binding( $quitter, "\cc");
46             $self->{win}{viewer}->set_binding( $quitter, 'q');
47              
48             $self->{cui}->reset_curses;
49             $self->{cui}->mainloop;
50             }
51              
52             sub save_dir {
53             my $self = shift;
54             my $dir = $self->{save_dir};
55             unless (-d $dir) {
56             mkpath $dir or die "Can't mkpath $dir: $!";
57             }
58             return $dir;
59             }
60              
61             sub set_page {
62             my $self = shift;
63             my $page = shift;
64             my $workspace = shift;
65             my $no_history = shift;
66              
67             my $pb = $self->{win}{page_box};
68             my $wksp = $self->{win}{workspace_box};
69              
70             unless ($no_history) {
71             push @{ $self->{history} }, {
72             page => $pb->text,
73             wksp => $wksp->text,
74             pos => $self->{win}{viewer}{-pos},
75             };
76             }
77             $self->set_workspace($workspace) if $workspace;
78             unless (defined $page) {
79             $self->{rester}->accept('text/plain');
80             $page = $self->{rester}->get_homepage;
81             }
82             $pb->text($page);
83             $self->load_page;
84             }
85              
86             sub set_last_tagged_page {
87             my $self = shift;
88             my $tag = shift;
89             my $r = $self->{rester};
90              
91             $r->accept('text/plain');
92             my @pages = $r->get_taggedpages($tag);
93             $self->set_page(shift @pages);
94             }
95              
96             sub download {
97             my $self = shift;
98             my $current_page = $self->{win}{page_box}->text;
99             $self->{cui}->leave_curses;
100              
101             my $r = $self->{rester};
102            
103             my $dir = $self->_unique_filename($current_page);
104             mkdir $dir or die "Error creating directory $dir: $!";
105              
106             my %ct = (
107             html => 'text/html',
108             wiki => 'text/x.socialtext-wiki',
109             );
110              
111             while (my ($ext, $ct) = each %ct) {
112             $r->accept($ct);
113             my $file = "$dir/content.$ext";
114             open my $fh, ">$file" or die "Can't open $file: $!";
115             print $fh $r->get_page($current_page);
116             close $fh or die "Can't open $file: $!";
117             }
118            
119             # Fetch attachments
120             $r->accept('perl_hash');
121             my $attachments = $r->get_page_attachments($current_page);
122              
123             for my $a (@$attachments) {
124             my $filename = "$dir/$a->{name}";
125             my ( $status, $content ) = $r->_request(
126             uri => $a->{uri},
127             method => 'GET',
128             );
129             if ($status != 200) {
130             warn "Error downloading $filename: $status";
131             next;
132             }
133             open my $fh, ">$filename" or die "Can't open $filename: $!\n";
134             print $fh $content;
135             close $fh or die "Error writing to $filename: $!\n";
136             print "Downloaded $filename\n";
137             }
138             }
139              
140             sub _unique_filename {
141             my $self = shift;
142             my $original = shift;
143             my $filename = $original;
144             my $i = 0;
145             while (-e $filename) {
146             $i++;
147             $filename = "$original.$i";
148             }
149             return $filename;
150             }
151              
152             sub set_workspace {
153             my $self = shift;
154             my $wksp = shift;
155             $self->{win}{workspace_box}->text($wksp);
156             $self->{rester}->workspace($wksp);
157             }
158              
159             sub go_back {
160             my $self = shift;
161             my $prev = pop @{ $self->{history} };
162             if ($prev) {
163             $self->set_page($prev->{page}, $prev->{wksp}, 1);
164             $self->{win}{viewer}{-pos} = $prev->{pos};
165             }
166             }
167              
168             sub get_page {
169             return $App->{win}{page_box}->text;
170             }
171              
172             sub load_page {
173             my $self = shift;
174             my $current_page = $self->{win}{page_box}->text;
175              
176             if (! $current_page) {
177             $self->{cui}->status('Fetching list of pages ...');
178             $self->{rester}->accept('text/plain');
179             my @pages = $self->{rester}->get_pages;
180             $self->{cui}->nostatus;
181             $App->{win}->listbox(
182             -title => 'Choose a page',
183             -values => \@pages,
184             change_cb => sub {
185             my $page = shift;
186             $App->set_page($page) if $page;
187             },
188             );
189             return;
190             }
191              
192             $self->{cui}->status("Loading page $current_page ...");
193             $self->{rester}->accept('text/x.socialtext-wiki');
194             my $page_text = $self->{rester}->get_page($current_page);
195             $page_text = $self->_render_wikitext_wafls($page_text);
196             $self->{cui}->nostatus;
197             $self->{win}{viewer}->text($page_text);
198             $self->{win}{viewer}->cursor_to_home;
199             }
200              
201             sub _setup_ui {
202             my $self = shift;
203             $self->{cui} = Curses::UI->new( -color_support => 1 );
204             $self->{win} = $self->{cui}->add('main', 'Socialtext::Wikrad::Window');
205             $self->{cui}->leave_curses;
206             }
207              
208             sub _render_wikitext_wafls {
209             my $self = shift;
210             my $text = shift;
211              
212             if ($text =~ m/{st_(?:iteration|project)stories: <([^>]+)>}/) {
213             my $tag = $1;
214             my $replace_text = "Stories for tag: '$tag':\n";
215             $self->{rester}->accept('text/plain');
216             my @pages = $self->{rester}->get_taggedpages($tag);
217            
218             $replace_text .= join("\n", map {"* [$_]"} @pages);
219             $replace_text .= "\n";
220             $text =~ s/{st_(?:iteration|project)stories: <[^>]+>}/$replace_text/;
221             }
222              
223             return $text;
224             }
225              
226              
227             1;