wiki.pl: Port some fixes from upstream
[Orgmuse.git] / retag
blobbf039afbb0622b2951398525b7ad3fa6295edece
1 #!/usr/bin/perl -w
3 # Copyright (C) 2007 Alex Schroeder <alex@gnu.org>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 require LWP;
19 use Getopt::Std;
21 our ($opt_v, $opt_w, $opt_f);
23 # We make our own specialization of LWP::UserAgent that asks for
24 # user/password if document is protected.
26 package RequestAgent;
27 @ISA = qw(LWP::UserAgent);
29 sub new {
30 my $self = LWP::UserAgent::new(@_);
31 $self;
34 sub get_basic_credentials {
35 my($self, $realm, $uri) = @_;
36 return split(':', $main::opt_w, 2);
40 my $usage = qq{$0 [-i URL] [-t SECONDS]
41 \t[-u USERNAME] [-p PASSWORD] [-w USERNAME:PASSWORD]
42 \t[-f FORMAT] [-a TAG] [-d TAG] [TARGET]
44 TARGET is the base URL for the wiki. Visiting this URL should show you
45 its homepage.
47 You add a TAG using -a and delete it using -d. Multiple tags can be
48 separated by a space or a comma.
50 FORMAT defaults to [[tag:TheTag]]. If you use just words, specify -f1.
52 Provide the page names to retag on STDIN or use -i to point to a page.
53 You can use the index action with the raw parameter. See example
54 below.
56 The list of page names should use the MIME type text/plain.
58 By default, retag will tag a page every five seconds. Use -t to
59 override this. SECONDS is the number of seconds to wait between
60 requests.
62 The edits will show up on the list of changes as anonymous edits. If
63 you want to provide a USERNAME, you can use -u to do so.
65 If you want to tag pages on a locked wiki, you need to provide a
66 PASSWORD using -p.
68 On the other hand, if your wiki is protected by so-called "basic
69 authentication" -- that is, if you need to provide a username and
70 password before you can even view the site -- then you can pass those
71 along using the -w option. Separate username and password using a
72 colon.
74 Example:
76 retag -i 'http://www.emacswiki.org/cgi-bin/alex?search=tag%3Akitsunemori+2006+2007;context=0;raw=1' \\
77 -u AlexSchroeder -a MondayGroup http://www.emacswiki.org/cgi-bin/alex
80 sub UrlEncode {
81 my $str = shift;
82 return '' unless $str;
83 my @letters = split(//, $str);
84 my @safe = ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '-', '_', '.', '!', '~', '*', "'", '(', ')', '#');
85 foreach my $letter (@letters) {
86 my $pattern = quotemeta($letter);
87 if (not grep(/$pattern/, @safe)) {
88 $letter = sprintf("%%%02x", ord($letter));
91 return join('', @letters);
94 sub GetRaw {
95 my ($uri) = @_;
96 my $ua = RequestAgent->new;
97 my $response = $ua->get($uri);
98 print "no response\n" unless $response->code;
99 print "GET ", $response->code, " ", $response->message, "\n" if $opt_v;
100 return $response->content if $response->is_success;
103 my $FreeLinkPattern = "([-,.()' _0-9A-Za-z\x80-\xff]+)";
105 sub PostRaw {
106 my ($uri, $id, $data, $username, $password) = @_;
107 my $ua = RequestAgent->new;
108 my $response = $ua->post($uri, {title=>$id, text=>$data, raw=>1,
109 question=>1, recent_edit=>'on',
110 username=>$username, pwd=>$password});
111 my $status = $response->code . ' ' . $response->message;
112 warn "POST $id failed: $status.\n" unless $response->is_success;
115 sub tag {
116 my ($target, $interval, $username, $password,
117 $pageref, $addref, $delref) = @_;
118 foreach my $id (@$pageref) {
119 print "$id\n";
120 my $page = UrlEncode ($id);
121 my $data = GetRaw("$target?action=browse;id=$page;raw=1");
122 # Every page starts with a new copy.
123 my %tags = map { $_ => 1 } @$addref;
124 # The current code does not remove tags sprinkled all over the
125 # page. The code will in fact add those tags to the final tagline.
126 if ($data =~ /\n\nTags: (.*)/) {
127 my $tags = $1;
128 if ($opt_f) {
129 foreach my $tag (split /,\s*/, $1) {
130 $tags{$tag} = 1;
132 } else {
133 while ($tags =~ /\[\[tag:$FreeLinkPattern(\|[^]|]+)?\]\]/ogi) {
134 $tags{$1} = 1;
137 foreach my $tag (@$delref) {
138 delete $tags{$tag};
141 my $newtags;
142 if ($opt_f) {
143 $newtags = join(', ', sort keys %tags);
144 } else {
145 $newtags = join(' ', map { "\[\[tag:$_\]\]" } sort keys %tags);
147 # The code will not remove the tagline if the last tag is removed.
148 # It will add a tagline if there is none.
149 $data =~ s/\n\nTags: .*/\n\nTags: $newtags/ or $data .= "\n\nTags: $newtags";
150 PostRaw($target, $id, $data, $username, $password);
151 sleep($interval);
155 sub main {
156 our($opt_h, $opt_i, $opt_t, $opt_d, $opt_u, $opt_p);
157 getopts('hvi:t:u:p:w:a:d:f:');
158 die $usage if $opt_h;
159 die "Missing tags to add or delete. Use -a TAG or -d TAG.\n"
160 unless $opt_a or $opt_d;
161 my $interval = $opt_t ? $opt_t : 5;
162 my (@add, @delete);
163 @add = split(/[ ,]+/, $opt_a) if $opt_a;
164 @delete = split(/[ ,]+/, $opt_d) if $opt_d;
165 my $username = $opt_u;
166 my $password = $opt_p;
167 my $target = shift(@ARGV);
168 die "You need to provide exactly one target URL. Use -h for more help.\n"
169 unless $target and not @ARGV;
170 my @pages = ();
171 if ($opt_i) {
172 my $data = GetRaw($opt_i);
173 @pages = split(/\n/, $data);
174 } else {
175 print "List of pages:\n";
176 while (<STDIN>) {
177 chomp;
178 push(@pages, $_);
181 die "The list of pages is missing. Use -i.\n" unless @pages;
182 tag($target, $interval, $username, $password, \@pages, \@add, \@delete);
185 main();