User:AnomieBOT/source/tasks/OnThisDayTagger.pm
Appearance
Approved 2009-05-26 Wikipedia:Bots/Requests for approval/AnomieBOT 30 |
package tasks::OnThisDayTagger;
=pod
=begin metadata
Bot: AnomieBOT
Task: OnThisDayTagger
BRFA: Wikipedia:Bots/Requests for approval/AnomieBOT 30
Status: Approved 2009-05-26
Created: 2009-05-14
Process each anniversary article (e.g. [[Wikipedia:Selected
anniversaries/October 31]] for 2008-10-31, 2007-10-31, 2006-10-31, and so on)
to extract the bolded links and tag the corresponding article talk pages with
{{tl|OnThisDay}}.
=end metadata
=cut
use utf8;
use strict;
use AnomieBOT::Task qw/:time/;
use Data::Dumper;
use POSIX;
use Digest::SHA qw/sha256_base64/;
use vars qw/@ISA/;
@ISA=qw/AnomieBOT::Task/;
my @months=qw/January February March April May June July August September October November December/;
my $monthre="(?:".join("|",@months).")";
my $skip_links_re=join('|',
"$monthre \\d{1,2}",
'\d{4}',
'List of historical anniversaries',
'List of days of the year',
);
$skip_links_re=qr/^(?:$skip_links_re)$/;
my @skip_templates=(
'Template:Archive box',
'Template:Archives',
);
my $whine_to='User talk:AnomieBOT';
my $mode='live'; # 'list' or 'live'
my $list='/tmp/otd-pages.txt';
sub new {
my $class=shift;
my $self=$class->SUPER::new();
$self->{'order'}=100;
$self->{'nextday'}=0;
bless $self, $class;
return $self;
}
=pod
=for info
Approved 2009-05-26<br />[[Wikipedia:Bots/Requests for approval/AnomieBOT 30]]
=cut
sub approved {
return 2;
}
sub run {
my ($self, $api)=@_;
my $res;
$api->task('OnThisDayTagger', 0, 10, qw/d::Talk d::Timestamp d::Templates d::Redirects/);
my $screwup=' Errors? [[User:'.$api->user.'/shutoff/OnThisDayTagger]]';
$api->store->{'nextday'}=5643 unless exists($api->store->{'nextday'});
my $starttime=time;
my $today=day_from_timestamp($starttime);
if($mode eq 'list'){
die "Could not open $list: $!\n" unless open(X, '<:utf8', $list);
my %revs=();
for(my $m=1; $m<=12; $m++){
for(my $d=1; $d<=31; $d++){
my $md=sprintf("%02d-%02d",$m,$d);
next unless strftime("%m-%d",0,0,0,$d,$m-1,100) eq $md;
my $res=$api->query([],
titles => strftime("Wikipedia:Selected anniversaries/%B %-d",0,0,0,$d,$m-1,100),
prop => 'revisions',
rvprop => 'ids|timestamp',
rvlimit => 'max',
);
if($res->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$res->{'content'}."\n");
return 300;
}
if($res->{'code'} ne 'success'){
$api->warn("Failed to fetch revisions for $md: $res->{error}\n");
sleep(10);
redo;
}
$revs{$md}=(values(%{$res->{'query'}{'pages'}}))[0]{'revisions'};
}
}
my %revids=();
for(my $day=0; $day<$today; $day++){
my $start=$api->ISO2timestamp(day("%Y-%m-%dT00:00:00Z",$day+1));
foreach (@{$revs{day('%m-%d',$day)}}){
my $t=$api->ISO2timestamp($_->{'timestamp'});
if($t<$start){
$revids{day('%F',$day)}=$_->{'revid'};
last;
}
}
}
my %pages=();
while(<X>){
next unless /^\* \[\[(.*?)\]\]: (\d{4}-\d{2}-\d{2}(?:, \d{4}-\d{2}-\d{2})*)\s*$/;
my $title=$1;
my @dates=split /, /, $2;
my %dates=();
foreach (@dates){
die "Bad date: $_" unless exists($revids{$_});
$dates{$_}=$revids{$_};
}
# Check the page
my $res=$api->query(
titles => $title,
prop => 'categories',
cllimit => 'max',
clcategories => 'Category:All disambiguation pages',
);
if($res->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$res->{'content'}."\n");
return 300;
}
if($res->{'code'} ne 'success'){
$api->warn(day("Failed to load links for %F: ", $self->{'nextday'}).$res->{'error'}."\n");
sleep(10);
redo;
}
my $page=(values(%{$res->{'query'}{'pages'}}))[0];
if(exists($page->{'missing'})){
die "$title does not exist";
} elsif(exists($page->{'redirect'})){
die "$title is a redirect";
} elsif($page->{'ns'}!=0){
die "$title is a non-article";
} elsif(exists($page->{'categories'}) && @{$page->{'categories'}}){
die "$title is a disambiguation page";
}
# Tag the talk page
$title="Talk:$title";
$api->log("Tagging $title");
my $tok=$api->edittoken("$title", EditRedirect => 1);
if($tok->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$tok->{'content'}."\n");
return 300;
}
if($tok->{'code'} ne 'success'){
$api->warn("Failed to get edit token for $title: ".$tok->{'error'}."\n");
sleep(10);
redo;
}
if(exists($tok->{'redirect'})){
die "$title is a redirect, cannot tag";
}
my $intxt=$tok->{'revisions'}[0]{'slots'}{'main'}{'*'} // '';
my $outtxt=$self->tag($api, $intxt, %dates);
return 300 unless defined($outtxt);
if($intxt ne $outtxt){
$res=$api->edit($tok, $outtxt, "Adding/updating {{OnThisDay}}. $screwup", 0, 1);
if($res->{'code'} ne 'success'){
$api->warn("Write failed on $title: $res->{error}\n");
sleep(10);
redo;
}
}
}
close X;
return undef;
}
if($mode eq 'live'){
if($self->{'nextday'}==0 && exists($api->store->{'nextday'})){
my $t=$api->store->{'nextday'};
$self->{'nextday'}=$t if $t=~/^\d+$/;
}
# Iterate over all our pages
while($self->{'nextday'}<$today){
# First, create the categories if they don't already exist
my $tok=$api->edittoken(day("Category:Selected anniversaries (%Y)", $self->{'nextday'}), EditRedir=>1);
if($tok->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$tok->{'content'}."\n");
return 300;
}
if($tok->{'code'} ne 'success'){
$api->warn("Failed to get edit token for year category: ".$tok->{'error'}."\n");
} elsif(exists($tok->{'missing'})){
$api->edit($tok, "[[Category:Selected anniversaries]]", "Creating dated subcategory, to prevent redlinks. $screwup", 0, 1);
}
$tok=$api->edittoken(day("Category:Selected anniversaries (%B %Y)", $self->{'nextday'}), EditRedir=>1);
if($tok->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$tok->{'content'}."\n");
return 300;
}
if($tok->{'code'} ne 'success'){
$api->warn("Failed to get edit token for month category: ".$tok->{'error'}."\n");
} elsif(exists($tok->{'missing'})){
my $y=day("%Y", $self->{'nextday'});
my $m=day("%m", $self->{'nextday'});
$api->edit($tok, "[[Category:Selected anniversaries ($y)|0$m]]", "Creating dated subcategory, to prevent redlinks. $screwup", 0, 1);
}
# Load day page
my $day=$self->{'nextday'};
my $fday=day("%F", $self->{'nextday'});
my $page=load_page_for_day($api, $day);
if($page->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$page->{'content'}."\n");
return 300;
}
if($page->{'code'} ne 'success'){
$api->warn("Failed to fetch page for $fday: $page->{error}\n");
return 60;
}
my $revid=$page->{'revisions'}[0]{'revid'};
# Strip out non-rendered content
my ($txt, $nowiki)=$api->strip_nowiki($page->{'revisions'}[0]{'slots'}{'main'}{'*'});
while(my ($k,$v)=each %$nowiki){
$nowiki->{$k}='' if $v=~/^<!--/;
}
$txt=$1 if $txt=~m!<onlyinclude>(.*?)</onlyinclude>!s;
$txt=~s!<noinclude>(.*?)</noinclude>!!gs;
$txt=~s!</?includeonly>!!g;
# Replace time-varying templates
$txt=~s/\{\{IsLeapYear\}\}/{{IsLeapYear|{{CURRENTYEAR}}}}/g;
my $x=day("%-d", $self->{'nextday'});
$txt=~s/\{\{CURRENTDAY\}\}/$x/g;
$x=day("%w", $self->{'nextday'});
$txt=~s/\{\{CURRENTDOW\}\}/$x/g;
$x=day("%m", $self->{'nextday'});
$txt=~s/\{\{CURRENTMONTH\}\}/$x/g;
$x=day("%Y", $self->{'nextday'});
$txt=~s/\{\{CURRENTYEAR\}\}/$x/g;
$txt=$api->replace_nowiki($txt, $nowiki);
# Expand templates
$res=$api->query(
action => 'expandtemplates',
title => $page->{'title'},
text => $txt,
prop => 'wikitext',
);
if($res->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$res->{'content'}."\n");
return 300;
}
if($res->{'code'} ne 'success'){
$api->warn(day("Failed to expand templates for %F: ", $self->{'nextday'}).$res->{'error'}."\n");
$api->debug(2,"Text was $txt\n");
return 60;
}
# Transform ''' to <b>
$txt=doAllQuotes($api, $res->{'expandtemplates'}{'wikitext'});
# Extract just the bold parts
$txt=join('',$txt=~m!<b>(.*?)</b>!g);
# Extract the links
$res=$api->query(
action => 'parse',
title => $page->{'title'},
text => $txt,
prop => 'links',
);
if($res->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$res->{'content'}."\n");
return 300;
}
if($res->{'code'} ne 'success'){
$api->warn(day("Failed to expand templates for %F: ", $self->{'nextday'}).$res->{'error'}."\n");
$api->debug(2,"Text was $txt\n");
return 60;
}
my @links=map($_->{'*'}, grep($_->{'ns'}==0 && exists($_->{'exists'}), @{$res->{'parse'}{'links'}}));
# Filter out month/year links
@links=grep(!/$skip_links_re/o, @links);
# Resolve redirects & check for dabs
my %res=();
my @err=();
while(@links){
my @l=splice(@links,0,500);
my $res=$api->query(
titles => join('|',@l),
prop => 'categories',
cllimit => 'max',
clcategories => 'Category:All disambiguation pages',
redirects => 1,
);
if($res->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$res->{'content'}."\n");
return 300;
}
if($res->{'code'} ne 'success'){
$api->warn(day("Failed to load links for %F: ", $self->{'nextday'}).$res->{'error'}."\n");
return 60;
}
my %map=();
$map{$_->{'from'}}=$_->{'to'} foreach (@{$res->{'query'}{'redirects'}});
my %pages=map { $_->{'title'}=>$_ } values %{$res->{'query'}{'pages'}};
foreach my $l (@l){
my $t=$api->apply_redirect_map( $l, \%map );
if(!exists($pages{$t})){
$api->warn("No result for $l ".day("(%F)", $day)."\n");
return 60;
}
my $page=$pages{$t};
if(exists($page->{'missing'})){
push @err, "* [[:$l]] does not exist";
} elsif(exists($page->{'redirect'})){
push @err, "* [[:$l]] is a double redirect";
} elsif($page->{'ns'}!=0){
push @err, "* [[:$l]] redirects to a non-article";
} elsif(exists($page->{'categories'}) && @{$page->{'categories'}}){
push @err, "* [[:$l]] is a disambiguation page";
} else {
$res{$t}=[] unless exists($res{$t});
push @{$res{$t}}, $l;
}
}
}
# Tag the talk pages
foreach my $otitle (keys %res){
my $title="Talk:$otitle";
$api->log("Tagging $title for ".day("%F", $day));
my $tok=$api->edittoken("$title", EditRedirect => 1);
if($tok->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$tok->{'content'}."\n");
return 300;
} elsif($tok->{'code'} eq 'botexcluded'){
push @err, "* I am excluded from editing [[$title]], cannot tag for [[".join(']] / [[', @{$res{$otitle}})."]]";
next;
} elsif($tok->{'code'} ne 'success'){
$api->warn("Failed to get edit token for $title: ".$tok->{'error'}."\n");
return 60;
}
if(exists($tok->{'redirect'})){
push @err, "* [[$title]] is a redirect, cannot tag for [[".join(']] / [[', @{$res{$otitle}})."]]";
next;
}
my $intxt=$tok->{'revisions'}[0]{'slots'}{'main'}{'*'} // '';
my $outtxt=$self->tag($api, $intxt, $fday => $revid);
return 300 unless defined($outtxt);
if($intxt ne $outtxt){
$res=$api->edit($tok, $outtxt, "Adding/updating {{OnThisDay}} for $fday. $screwup", 0, 1);
if($res->{'code'} ne 'success'){
$api->warn("Write failed on $title: $res->{error}\n");
return 60;
}
}
}
# Whine about errors
if(@err){
$api->log("Whining about bad links on ".day("%F", $day));
my $err=day("Errors processing [[Wikipedia:Selected anniversaries/%B %-d]]", $self->{'nextday'});
$res=$api->whine($err, "The following links could not be processed:\n".join("\n", @err)."\nPlease add {{tl|OnThisDay}} to them manually using oldid $revid, I will not be retrying. Thanks.", Summary => $err, Pagename => $whine_to, NoSmallPrint => 1);
if($res->{'code'} eq 'shutoff'){
$api->warn("Task disabled: ".$res->{'content'}."\n");
return 300;
}
if($res->{'code'} ne 'success'){
$api->warn(day("Could not complain about %F: ", $self->{'nextday'}).$res->{'error'}."\n");
return 60;
}
}
# Done! Next day
$api->store->{"nextday"}=++$self->{'nextday'};
}
my $next=86400-($starttime%86400)-(time-$starttime);
$next=0 if $self->{'nextday'}<$today;
return $next;
}
die "Invalid mode '$mode'";
}
# The anniversary pages were created 2004-02-26 through 2004-02-28; We count
# 2004-02-26 as day 0. This function does strftime on the day number.
# Thankfully, it correctly converts
sub day {
my $fmt=shift;
my $day=shift;
return strftime($fmt,0,0,0,$day+26,1,104);
}
sub day_from_timestamp {
my $ts=shift;
my ($min,$max)=(0,5000);
my $target=strftime("%F", gmtime $ts);
while(1){
my $day=day("%F",$max);
if($day eq $target){
return $max;
} elsif($day lt $target){
$min=$max;
$max+=$max;
} else {
last;
}
}
while($min!=$max){
my $d=POSIX::floor(($min+$max)/2);
my $day=day("%F",$d);
if($day eq $target){
return $d;
} elsif($day lt $target){
$min=$d+1;
} else {
$max=$d;
}
}
die "WTF? Searching for $target, got to $min which is ".day("%F",$min);
}
sub load_page_for_day {
my $api=shift;
my $day=shift;
return $api->store->{"day $day"} if(exists($api->store->{"day $day"}));
my $start=$api->ISO2timestamp(day("%Y-%m-%dT00:00:00Z",$day+1));
my $iter=$api->iterator(
titles => day("Wikipedia:Selected anniversaries/%B %-d",$day),
rvlimit => 1,
rvprop => 'ids|timestamp|content',
rvslots => 'main',
rvdir => 'older',
rvstart => $start,
prop => 'revisions',
);
while(my $page=$iter->next){
return $page unless $page->{'_ok_'};
$page->{'code'}='success';
my $t=$api->ISO2timestamp($page->{'revisions'}[0]{'timestamp'});
if($t<$start){
$api->store->{"day $day"}=$page;
$api->store->{"day $day"}{'cached'}=1;
$page->{'cached'}=0;
return $page;
}
}
}
# Parse single-quotes in the same way MediaWiki does.
sub doAllQuotes {
my $api=shift;
my ($text,$nowiki)=$api->strip_nowiki(shift);
my $outtxt='';
my @lines=split(/\n/, $text);
$outtxt.=doQuotes($_)."\n" foreach (@lines);
$outtxt=substr($outtxt,0,-1);
return $api->replace_nowiki($outtxt,$nowiki);
}
sub doQuotes {
my $text=shift;
my @arr=split(/(''+)/, $text);
return $text if @arr == 1;
my $numbold=0;
my $numitalics=0;
for(my $i=1; $i<@arr; $i+=2){
my $l=length($arr[$i]);
if($l==4){
# Four 's => assume one plain text + bold
$arr[$i-1].="'";
$arr[$i]="'''";
$l=3;
} elsif($l>5){
# More than five 's => assume N-5 plain text + bold + italic
$arr[$i-1].="'"x($l-5);
$arr[$i]="'''''";
$l=5;
}
$numitalics++ if($l==2 || $l==5);
$numbold++ if($l==3 || $l==5);
}
# Odd number of each => guess one of the bolds is really plain + italic
# Somewhat odd, but to match MediaWiki's parser...
if(($numbold&1) && ($numitalics&1)){
my $firstsingleletterword = -1;
my $firstmultiletterword = -1;
my $firstspace = -1;
for(my $i=1; $i<@arr; $i+=2){
next unless length($arr[$i])==3;
my $x1=substr($arr[$i-1],-1);
my $x2=substr($arr[$i-1],-2,1);
if($x1 eq ' '){
$firstspace=$i if $firstspace == -1;
} elsif($x2 eq ' '){
$firstsingleletterword=$i if $firstsingleletterword == -1;
} else {
$firstmultiletterword=$i if $firstmultiletterword == -1;
}
}
if($firstsingleletterword != -1){
$arr[$firstsingleletterword]="''";
$arr[$firstsingleletterword-1].="'";
} elsif($firstmultiletterword != -1){
$arr[$firstmultiletterword]="''";
$arr[$firstmultiletterword-1].="'";
} elsif($firstspace != -1){
$arr[$firstspace]="''";
$arr[$firstspace-1].="'";
}
}
# Now, convert to HTML
my $output='';
my $buffer='';
my $state='';
for(my $i=0; $i<@arr; $i++){
my $r=$arr[$i];
if($i&1){
if(length($r)==2){
if($state eq 'i'){
$output.='</i>'; $state='';
} elsif($state eq 'bi'){
$output.='</i>'; $state='b';
} elsif($state eq 'ib'){
$output.='</b></i><b>'; $state='b';
} elsif($state eq 'both'){
$output.="<b><i>$buffer</i>"; $state='b';
} else {
$output.='<i>'; $state.='i';
}
} elsif(length($r)==3){
if($state eq 'b'){
$output.='</b>'; $state='';
} elsif($state eq 'bi'){
$output.='</i></b><i>'; $state='i';
} elsif($state eq 'ib'){
$output.='</b>'; $state='i';
} elsif($state eq 'both'){
$output.="<i><b>$buffer</b>"; $state='i';
} else {
$output.='<b>'; $state.='b';
}
} elsif(length($r)==5){
if($state eq 'b'){
$output.='</b><i>'; $state='i';
} elsif($state eq 'i'){
$output.='</i><b>'; $state='b';
} elsif($state eq 'bi'){
$output.='</i></b>'; $state='';
} elsif($state eq 'ib'){
$output.='</b></i>'; $state='';
} elsif($state eq 'both'){
$output.="<i><b>$buffer</b></i>"; $state='';
} else {
$buffer=''; $state='both';
}
}
} else {
if($state eq 'both'){
$buffer.=$r;
} else {
$output.=$r;
}
}
}
$output.='</b>' if($state eq 'b' || $state eq 'ib');
$output.='</i>' if($state eq 'i' || $state eq 'bi' || $state eq 'ib');
$output.='</b>' if($state eq 'bi');
$output.="<b><i>$buffer</i></b>" if($state eq 'both' && $buffer ne '');
return $output;
}
sub tag {
my $self=shift;
my $api=shift;
my $txt=shift;
my %dates=@_;
my %redir=$api->redirects_to_resolved('Template:On this day');
if(exists($redir{''})){
$api->warn("Could not load list of redirects to Template:On this day: ".$redir{''}{'error'}."\n");
return undef;
}
my %redir2=$api->redirects_to_resolved('Template:Article history');
if(exists($redir2{''})){
$api->warn("Could not load list of redirects to Template:Article history: ".$redir2{''}{'error'}."\n");
return undef;
}
# Update an existing OnThisDay template?
my $done=0;
$txt=$api->process_templates($txt, sub {
my $name=shift;
my $params=shift;
shift; # $wikitext
shift; # $data
my $oname=shift;
return unless exists($redir{"Template:$name"});
# Read existing parameters
my %p=();
my $mx=0;
foreach (@$params){
$mx=$1 if(/^\s*(?:date|oldid)(\d+)\s*=/ && $mx < $1);
if(/^\s*(date\d+)\s*=\s*(\d{4}-\d{2}-\d{2})\s*$/){
$p{$1}=$2;
} elsif(/^\s*(date\d+)\s*=\s*((?i)$monthre)\s+(\d{1,2})(?:\s*,)?\s+(\d{4})\s*$/){
my $m; for($m=0; $m<@months; $m++){ last if lc($months[$m]) eq lc($2); }
$p{$1}=sprintf("%04d-%02d-%02d", $4, $m+1, $3);
} elsif(/^\s*(date\d+)\s*=\s*(\d{1,2})\s+((?i)$monthre)(?:\s*,)?\s+(\d{4})\s*$/){
my $m; for($m=0; $m<@months; $m++){ last if lc($months[$m]) eq lc($3); }
$p{$1}=sprintf("%04d-%02d-%02d", $4, $m+1, $2);
} elsif(/^\s*(oldid\d+)\s*=\s*(\d+)\s*$/){
$p{$1}=$2;
}
}
@$params=();
for(my $i=1; $i<=$mx; $i++){
$dates{$p{"date$i"}}=$p{"oldid$i"} if(exists($p{"date$i"}) && exists($p{"oldid$i"}) && !exists($dates{$p{"date$i"}}));
}
# (Re-)insert parameters
my $i=1;
$i++ while grep(/^\s*oldid$i\s*=/, @$params);
foreach my $date (sort keys %dates){
push @$params, "date$i=$date", "oldid$i=".$dates{$date};
$i++;
}
$done=1;
return "{{$oname|".join('|', @$params)."}}";
});
return $txt if $done;
# Update an existing ArticleHistory template?
$txt=$api->process_templates($txt, sub {
my $name=shift;
my $params=shift;
shift; # $wikitext
shift; # $data
my $oname=shift;
return unless exists($redir2{"Template:$name"});
# Find next unused number, and check which dates aren't already used
my %p=();
my $mx=0;
foreach (@$params){
$mx=$1 if(/^\s*otd(\d+)(?:date|oldid)\s*=/ && $mx < $1);
$mx=1 if(/^\s*otd(?:date|oldid)\s*=/ && $mx < 1);
if(/^\s*(otd\d*date)\s*=\s*(\d{4}-\d{2}-\d{2})\s*$/){
$p{$1}=$2;
} elsif(/^\s*(otd\d*date)\s*=\s*((?i)$monthre)\s+(\d{1,2})(?:\s*,)?\s+(\d{4})\s*$/){
my $m; for($m=0; $m<@months; $m++){ last if lc($months[$m]) eq lc($2); }
$p{$1}=sprintf("%04d-%02d-%02d", $4, $m+1, $3);
} elsif(/^\s*(otd\d*date)\s*=\s*(\d{1,2})\s+((?i)$monthre)(?:\s*,)?\s+(\d{4})\s*$/){
my $m; for($m=0; $m<@months; $m++){ last if lc($months[$m]) eq lc($3); }
$p{$1}=sprintf("%04d-%02d-%02d", $4, $m+1, $2);
} elsif(/^\s*(otd\d*oldid)\s*=\s*(\d+)\s*$/){
$p{$1}=$2;
}
}
# Delete any already-listed dates
delete $dates{$p{"otddate"}} if(exists($p{"otddate"}) && exists($p{"otdoldid"}) && exists($dates{$p{"otddate"}}));
for(my $i=1; $i<=$mx; $i++){
delete $dates{$p{"otd${i}date"}} if(exists($p{"otd${i}date"}) && exists($p{"otd${i}oldid"}) && exists($dates{$p{"otd${i}date"}}));
}
# Add new parameters
foreach my $date (sort keys %dates){
++$mx;
push @$params, "otd${mx}date=$date", "otd${mx}oldid=".$dates{$date}."\n";
}
$done=1;
return "{{$oname|".join('|', @$params)."}}";
});
return $txt if $done;
if(!exists($self->{'loaded skip redirects'})){
my %skip=$api->redirects_to_resolved(@skip_templates);
if(exists($skip{''})){
$api->warn("Could not load list of redirects for skip templates: ".$skip{''}{'error'}."\n");
return undef;
}
@skip_templates=keys %skip;
$self->{'loaded skip redirects'}=1;
}
# No, add a new one.
my $templ='{{OnThisDay';
my $i=1;
foreach my $date (sort keys %dates){
$templ.="|date$i=$date|oldid$i=".$dates{$date};
$i++;
}
$templ.="}}";
my $nowiki;
($txt,$nowiki)=$api->strip_nowiki($txt);
my $outtmpl={};
$txt=$api->process_templates($txt, \&_strip_templates, $outtmpl);
$txt="$templ\n$txt" unless $txt=~s/^((?:\s*\x02[a-zA-Z0-9_-]+\x03)*[ \t]*)(?:$|(?=\n))/$1\n$templ/;
$txt=_unstrip_templates($txt, $outtmpl);
return $api->replace_nowiki($txt, $nowiki);
}
sub _strip_templates {
my ($name, $params, $wikitext, $data) = @_;
return undef if $name=~/^#/;
return undef if grep(/^\s*small\s*=\s*(?!no|n|0)\S/, @$params);
return undef if grep("Template:$name" eq $_, @skip_templates);
$wikitext=_unstrip_templates($wikitext,$data);
my $tmp = $wikitext;
utf8::encode( $tmp ) if utf8::is_utf8( $tmp );
my $tag="\x02".sha256_base64($tmp)."\x03";
$tag=~tr!+/=!-_!d;
$data->{$tag}=$wikitext;
return $tag;
}
sub _unstrip_templates {
my $wikitext=shift;
my $templ=shift;
$wikitext=~s!(\x02[a-zA-Z0-9_-]+\x03)! $templ->{$1} // $1 !gioe;
return $wikitext;
}
1;