User:AnomieBOT/source/tasks/TemplateReplacer15.pm

package tasks::TemplateReplacer15;

=pod

=begin metadata

Bot:      AnomieBOT
Task:     TemplateReplacer15
BRFA:     Wikipedia:Bots/Requests for approval/AnomieBOT 26
Status:   Approved 2009-03-07
Created:  2009-01-15
OnDemand: true

Replace all transclusions of one template with another (or with several others
depending on template parameters, etc), when a template must be renamed.
Normally, this will be the result of a TFD, but it could be used for any
clear-cut case. It will ''not'' be done just to replace a template redirect
when the redirect is going to remain.

=end metadata

=cut

use utf8;
use strict;

use Data::Dumper;
use AnomieBOT::Task;
use vars qw/@ISA/;
@ISA=qw/AnomieBOT::Task/;

# Sequence number
my $seq=10;
my $req="[[User:AnomieBOT/req/Template:fact|request]]";
my $errors="Errors or discussion? Post a note [[User:AnomieBOT/shutoff/TemplateReplacer15|here]] to stop the bot.";
my $force=1;
my $unique=1;

# List of templates to remove/replace in this task. Values may be the new
# template name (simple replacement), the empty string (removal), or a function
# ref (something more complicated).
my %repl=(
    'Template:Citation needed' => \&repl,
);
my @templates=keys %repl;

# If your function ref above fails for some reason, set this true.
my $fail=0;

sub repl {
    my $api=shift;
    my $name=shift;
    my $params=shift;
    my $wikitext=shift;
    my $data=shift;
    my $oname=shift;

    my $found=0;
    my $dt='';
    my $founddate=0;
    my $ret="{{$oname";
    my @reason=();
    foreach ($api->process_paramlist(@$params)){
        if($_->{'name'}=~/^\d+$/){
            $found=1;
            push @reason, $_->{'value'};
            next if $_->{'value'}=~/^\s*$/;
            next if $_->{'value'}=~/^\s*date\s*$/i;
            if($_->{'value'}=~/^\s*(?:January|February|March|April|May|June|July|August|September|October|November|December) \d\d\d\d\s*$/i){
                $_->{'value'}=~s/^\s+|\s+$//g;
                $dt=ucfirst(lc($_->{'value'}));
                pop @reason;
                next;
            }
            next if $_->{'value'}=~/^\s*\d{4}-\d{2}-\d{2}\s*$/;
            $ret.='|reason='.$_->{'value'};
        } else {
            push @reason, $_->{'value'} if $_->{'name'} eq 'reason';
            $founddate=1 if $_->{'name'} eq 'date';
            $ret.='|'.$_->{'text'};
        }
    }
    return undef unless $found;
    if(@reason>1){
        # Multiple named parameters and/or reason=
        $ret="{{$oname";
        foreach ($api->process_paramlist(@$params)){
            if($_->{'name'}=~/^(?:\d+|reason)$/){
                next if($_->{'name'} ne 'reason' && $_->{'value'}=~/^\s*(?:January|February|March|April|May|June|July|August|September|October|November|December) \d\d\d\d\s*$/i);
                $ret.='|reason='.join(' ',@reason) if @reason;
                @reason=();
            } else {
                $ret.='|'.$_->{'text'};
            }
        }
    }
    $ret.="|date=$dt" if(!$founddate && $dt ne '');
    $ret.='}}';
    return ($ret,"or removing deprecated unnamed parameter use in {{citation needed}}");
};

# List of pages to skip
my %skip=(
    # User doesn't care
    'User talk:Chzz/Archive 5'=>1,

    # Using "tlrow" for example tables, which I think can't work "right" with
    # the new parser anyway
    'User:Born2flie/Tools'=>1,
    'User:Faizhaider/inline'=>1,
    'User:Mattisse/inline'=>1,
    'User:Rich Farmbrough/temp85'=>1,
    'User talk:Ultramarine/Tags'=>1,
    'User:Wbfergus/Sandbox'=>1,
    'Wikipedia:Template messages/Cleanup'=>1,
    'Wikipedia:Template messages/Cleanup/Verifiability and sources'=>1,
    'Wikipedia:Template messages/Sources of articles'=>1,
);

# Iterator definitions
my @iter=(
    {
        list    => 'categorymembers',
        cmtitle => 'Category:Articles using citation needed template with unnamed parameter',
        cmlimit => 'max',
    },
);

sub new {
    my $class=shift;
    my $self=$class->SUPER::new();
    bless $self, $class;
    return $self;
}

=pod

=for info
Approved 2009-03-07<br />[[Wikipedia:Bots/Requests for approval/AnomieBOT 26]]

=cut

sub approved {
    return -1;
}

sub run {
    my ($self, $api)=@_;
    my $res;

    $api->task('TemplateReplacer15',0,10,qw/d::Redirects d::Templates d::WikiProjectTagging/);

    # Spend a max of 5 minutes on this task before restarting
    my $endtime=time()+300;

    # Get a list of templates redirecting to our targets
    my %templates=$api->redirects_to_resolved(keys %repl);
    if(exists($templates{''})){
        $api->warn("Failed to get redirects to target templates: ".$templates{''}{'error'}."\n");
        return 60;
    }
    my ($k,$v);
    $templates{$k}=$repl{$v} while(($k,$v)=each %templates);

    foreach my $iterdef (@iter){
        my $iter=$api->iterator(%$iterdef);
        while($_=$iter->next){
            if(!$_->{'_ok_'}){
                $api->warn("Failed to retrieve transclusion list: ".$_->{'error'}."\n");
                return 60;
            }

            next if exists($skip{$_->{'title'}});

            my $pageid=$_->{'pageid'};
            next if(!$force && exists($api->store->{$pageid}) && $api->store->{$pageid}>=$seq);

            my $title=$_->{'title'};
            $api->log("Checking for templates in $title");

            # WTF?
            if(exists($_->{'missing'})){
                $api->warn("$title is missing? WTF?\n");
                next;
            }

            # Ok, check the page
            my $tok=$api->edittoken($title, 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 $title: ".$tok->{'error'}."\n");
                next;
            }
            next if exists($tok->{'missing'});

            # Get page text
            my $intxt=$tok->{'revisions'}[0]{'slots'}{'main'}{'*'};

            # Perform the removal
            my @repl=();
            my @remv=();
            $fail=0;
            my $outtxt=$api->process_templates($intxt, sub {
                return undef if $fail;
                my $name=shift;
                my $params=shift;
                my $wikitext=shift;
                my $data=shift;
                my $oname=shift;

                return undef unless exists($templates{"Template:$name"});
                my $t=$templates{"Template:$name"};
                my ($ret,$log);
                if(ref($t) eq 'CODE'){
                    ($ret,$log)=$t->($api, $name, $params, $wikitext, $data, $oname);
                    return undef if $fail;
                    return undef unless defined($ret);
                } elsif($t eq ''){
                    ($ret,$log)=('','');
                } else {
                    $oname=~s/^(\s*)\S(?:.*\S)?(\s*)$/$1$t$2/s;
                    $ret="{{$oname";
                    $ret.='|'.join('|', @$params) if @$params;
                    $ret.="}}";
                    $log="{{$name}} with {{$t}}";
                }
                if($log eq ''){
                    push @remv, "{{$name}}";
                } else {
                    push @repl, $log;
                }
                return $ret;
            });
            next if $fail;

            # Need to edit?
            if($outtxt ne $intxt){
                if($unique){
                    my %x;
                    %x=(); @x{@repl}=(); @repl=keys %x;
                    %x=(); @x{@remv}=(); @remv=keys %x;
                }
                $repl[-1]='and '.$repl[-1] if @repl>1;
                $remv[-1]='and '.$remv[-1] if @remv>1;
                my $summary='';
                if(@remv){
                    $summary.='Removing '.join((@remv>2)?', ':' ', @remv);
                }
                if(@repl){
                    $summary.=(@remv?'; and replacing ':'Replacing ').join((@repl>2)?', ':' ', @repl);
                }
                $summary.=" per $req";
                $summary.=". $errors" if defined($errors);
                $api->log("$summary in $title");
                my $r=$api->edit($tok, $outtxt, $summary, 1, 1);
                if($r->{'code'} ne 'success'){
                    $api->warn("Write failed on $title: ".$r->{'error'}."\n");
                    next;
                }
            } else {
                $api->log("Nothing to do in $title");
            }
            $api->store->{$pageid}=$seq;

            # If we've been at it long enough, let another task have a go.
            return 0 if time()>=$endtime;
        }
    }

    # No more pages to check, try again in 10 minutes or so in case of errors.
    #$api->log("TemplateReplacer15 may be DONE!");
    return 600;
}

1;