Content deleted Content added
Updating published sources: PERTableUpdater: * Ok, that was dumb. |
Updating published sources: PERTableUpdater: * Use {{tl|v}} to generate view and history links. |
||
(45 intermediate revisions by 4 users not shown) | |||
Line 13:
Created: 2011-12-01
Update [[User:AnomieBOT/PERTable]], [[User:AnomieBOT/
[[User:AnomieBOT/EPERTable]], [[User:AnomieBOT/SPERTable]],
[[User:AnomieBOT/COIREQTable]], and [[User:AnomieBOT/PREQTable]].
=end metadata
Line 24 ⟶ 26:
use AnomieBOT::Task qw/:time bunchlist/;
use URI::Escape;
use HTML::Entities;
use Data::Dumper;
use vars qw/@ISA/;
@ISA=qw/AnomieBOT::Task/;
my %protact=(
'modify' => 'Modified',
Line 77 ⟶ 74:
$api->store->{"SPER pages"}=$api->store->{"pages1"};
delete $api->store->{"pages1"};
}
if(($api->store->{"ver"}//1) < 2){
for my $tag (qw/PER TPER SPER/) {
next unless exists($api->store->{"$tag pages"});
my %old = %{$api->store->{"$tag pages"}};
my %new = ();
while(my ($k,$v) = each %old) {
$new{$k}{$v->{'talk'}} = $v if exists($v->{'talk'});
}
$api->store->{"$tag pages"} = \%new;
}
$api->store->{"ver"} = 2;
}
# Flush warnings daily
my $ts = time;
$ts -= $ts % 86400;
if ( ($api->store->{'warnedBadRegexDate'} // 0) < $ts) {
$api->store->{'warnedBadRegex'} = {};
$api->store->{'warnedBadRegexDate'} = $ts;
}
Line 82 ⟶ 99:
# that mechanism
my %tb=();
my
[ 1, '
[ 2, '
[ 3, undef, 'MediaWiki:Titlewhitelist' ],
);
my ($
my $tb;
if ( $page=~/^meta:(.+)$/ ) {
$tb=$api->copy( wikibase => 'https://meta.wikimedia.org/w/', assert => 'user' )->rawpage( $1 );
} else {
$tb=$api->rawpage($page);
}
if($tb->{'code'} ne 'success'){
$api->warn("Failed to load $page: ".$tb->{'error'}."\n");
return 60;
}
my $ln = 0;
for my $line (split /\r?\n/, $tb->{'content'}){
$ln++;
my $re=$line;
my %opts=();
Line 110 ⟶ 136:
$re="(?-i:$re)" if($opts{'casesensitive'}//0);
$re=~s!(\{\{\s*ns\s*:\s*(.+?)\s*\}\})! replace_ns($api,$2) // $1 !ge;
# Try to escape left-braces that aren't quantifiers or parameters to escapes
my $tmp = $re;
$re=~s#(?<!\\[a-zA-Z])(?<!\\)\{(?!\d+(?:,\d*)?})#\\{#g;
$self->warnBadRegex( $api, "$page:$ln: Escaped left-braces in regex (old): $tmp" ) if $tmp ne $re;
$self->warnBadRegex( $api, "$page:$ln: Escaped left-braces in regex (new): $re" ) if $tmp ne $re;
# Validate each line, in case someone screws up the blacklist page
eval {
no warnings;
qr/^(?:$re)$/si;
};
if ( $@ ) {
$self->warnBadRegex( $api, "$page:$ln: Ignoring bad regex '$re': $@\n");
next;
}
# Log non-fatal warnings too.
eval {
use warnings FATAL => 'all';
qr/^(?:$re)$/si;
};
if ( $@ ) {
$self->warnBadRegex( $api, "$page:$ln: Warning: $@\n");
}
# Let's just hope no one ever uses {{int:}} here...
$tb{$re}={
i => $i,
source => $name ? "[[$page|$name]]" : undef,
line => $line,
opts => \%opts
} unless(($opts{'moveonly'}//0) || ($opts{'newaccountonly'}//0));
}
}
# Fields are:
# 0: Namespaces to color "attention" instead of "normal"
# 1: "Tag", also used in the name of the subpage the table is put on
# 2: Category name, no prefix
# 3: Type of request, i.e. "$type edit requests"
# 4: URL fragment for request links
# 5: NID component of the urn links
# 6: List of color-classes to apply based on page protection level:
# 0: unprotected
# 1: semi-protected
# 2: semi-protected via title blacklist
# 3: extended-confirmed protected
# 4: template protected
# 5: User JSON page
# 6: User CSS/JS page
# 7: fully protected
# 8: "fully" protected via title blacklist
# 9: cascading protection
# 10: MediaWiki-namespace page
# 11: MediaWiki-namespace CSS/JS page
my @data=(
[[10,828],'PER','Wikipedia fully protected edit requests','protected','editprotected','x-wp-editprotected',[qw/
[[10,828],'
[[10,828],'EPER','Wikipedia extended-confirmed-protected edit requests','extended-confirmed-protected','editextendedprotected','x-wp-editextendedprotected',[qw/error error error normal error error error error error error error error/]],
[[10,828],'SPER','Wikipedia semi-protected edit requests','semi-protected','editsemiprotected','x-wp-editsemiprotected',[qw/error normal caution error error error error error error error error error/]],
[[],'IPER','Wikipedia interface-protected edit requests','interface-protected','editinterfaceprotected','x-wp-editinterfaceprotected',[qw/error error error error error error normal error error error error normal/]],
[[0],'COIREQ','Wikipedia conflict of interest edit requests','COI','requestedit','x-wp-requestedit',[qw/normal caution caution caution error error error error error error error error/]],
[[],'PREQ','Wikipedia partial-block edit requests','partial block','editpartiallyblocked','x-wp-editpartiallyblocked',[qw/normal caution caution caution error error error error error error error error/]],
);
my $starttime=time;
for my $data (@data){
my ($
my $iter=$api->iterator(
generator => 'categorymembers',
gcmtitle => "Category:$cat",
gcmlimit => 'max',
prop => 'info|extlinks',
elprotocol => 'urn',
ellimit => 'max',
);
my %oldpages=%{$api->store->{"$tag pages"}//{}};
Line 140 ⟶ 222:
}
next unless $p->{'ns'}&1;
my
if($
my $
$
utf8::decode( $url );
$url =~ s/_/ /g;
$url;
} else {
();
}
} @{$p->{'extlinks'}//[]};
unless(@pages){
my $t=$p->{'title'};
if($p->{'ns'}==1){
$t=~s/^Talk://;
} else {
$t=~s/^([^:]+) talk:/$1:/;
}
push @pages, $t;
}
for my $t (@pages) {
my $tt = $p->{'title'};
$pages{$t}{$tt}=($oldpages{$t}{$tt} // {
title => $t,
talk => $p->{'title'},
touched => ISO2timestamp($p->{'touched'}),
});
$pages{$t}{$tt}{'reqisredir'} = defined( $p->{'redirect'} );
delete $pages{$t}{$tt}{'color'};
delete $pages{$t}{$tt}{'prottype'};
delete $pages{$t}{$tt}{'reason'};
delete $pages{$t}{$tt}{'logtitle'};
}
}
$api->store->{"$tag pages"}=\%pages;
$api->store->{"ver"} = 2;
if(%pages){
Line 171 ⟶ 272:
}
my $t=$p->{'title'};
my ($k,$pd) = each %{$pages{$t}}; # Get first
# Protection scoring "bitmap":
#
#
#
#
#
#
#
# 0x20 = Extended-confirmed protection
# 0x10 = Semi-protection
# 0x08 = Directly-applied protection
# 0x02 = Title blacklist protection
# Highest score by int value "wins".
my $protscore=0;
$pd->{'prottype'}='Not protected';
$pd->{'reason'}='';
if($p->{'ns'}==8 && ($p->{'contentmodel'} eq 'javascript' || $t=~m!\.js$!)){
$pd->{'prottype'}='Site JS page';
$protscore=0xc000;
} elsif($p->{'ns'}==8 && ($p->{'contentmodel'} eq 'css' || $t=~m!\.css$!)){
$pd->{'prottype'}='Site CSS page';
$protscore=0xc000;
} elsif($p->{'ns'}==8){
$pd->{'prottype'}='MediaWiki page';
$protscore=
} elsif($p->{'ns'}==2 && ($p->{'contentmodel'} eq 'javascript' || $t=~m!/.*\.js$!)){
$pd->{'prottype'}='User JS page';
$protscore=
} elsif($p->{'ns'}==2 && ($p->{'contentmodel'} eq 'css' || $t=~m!/.*\.css$!)){
$pd->{'prottype'}='User CSS page';
$protscore=
} elsif($p->{'ns'}==2 && ($p->{'contentmodel'} eq 'json' || $t=~m!/.*\.json$!)){
$pd->{'prottype'}='User JSON page';
$protscore=0x80;
}
my $tb = undef;
while(my ($re,$data)=each %tb){
next if $tb && $tb->{'i'} >= $data->{'i'};
next unless(exists($p->{'missing'}) || ($data->{'opts'}{'noedit'}//0));
next unless $t=~/^(?:$re)$/si;
}
if ( $tb && $tb->{'source'} ) {
my $sc=exists($tb->{'opts'}{'autoconfirmed'})?0x12:0x42;
next if $sc<$protscore;
$pd->{'prottype'}=$
my $line=$
$pd->{'reason'}=qq(Matching line: <syntaxhighlight lang="text"
$protscore=$sc;
}
Line 207 ⟶ 329:
my $expiry=undef;
my $pg=$t;
my $prottype = exists( $p->{'missing'} ) ? 'create' : 'edit';
for my $pp (@{$p->{'protection'}//[]}){
next unless $pp->{'type'} eq
my $sc=0;
$sc|=
$sc|=
$sc|=0x20 if $pp->{'level'} eq 'extendedconfirmed';
$sc|=0x10 if $pp->{'level'} eq 'autoconfirmed';
$sc|=exists($pp->{'source'})?0x2000:0x08;
$sc|=0x2000 if exists($pp->{'cascade'});
next if $sc<$protscore;
if(exists($pp->{'source'})){
Line 219 ⟶ 346:
$pg=$t;
$pd->{'prottype'}='Fully protected' if $pp->{'level'} eq 'sysop';
$pd->{'prottype'}='Template-protected' if $pp->{'level'} eq 'templateeditor';
$pd->{'prottype'}='Extended-confirmed protected' if $pp->{'level'} eq 'extendedconfirmed';
$pd->{'prottype'}='Semiprotected' if $pp->{'level'} eq 'autoconfirmed';
$pd->{'prottype'}.=' with cascading' if exists($pp->{'cascade'});
}
$pd->{'prottype'}.=strftime(', expires %F at %T UTC', gmtime ISO2timestamp($pp->{'expiry'})) if $pp->{'expiry'} ne 'infinity';
Line 229 ⟶ 359:
if($protscore & 0x10){
$pd->{'color'}=$colors->[1];
$pd->{'color'}=$colors->[2] if($protscore &
}
$pd->{'color'}=$colors->[3] if($protscore & 0x20);
if($protscore & 0x40){
$pd->{'color'}=$colors->[4] if($protscore & 0x40);
$pd->{'color'}=$colors->[
}
$pd->{'color'}=$colors->[
$pd->{'color'}=
$pd->{'color'}=$colors->[7] if($protscore & 0x1000);
$pd->{'color'}=$colors->[9] if($protscore & 0x2000);
$pd->{'color'}=$colors->[10] if($protscore & 0x4000);
$pd->{'color'}=$colors->[11] if($protscore & 0x8000);
$pd->{'color'}='attention' if($pd->{'color'} eq 'normal' && grep($p->{'ns'}==$_, @$attentionns));
if($pd->{'reason'} eq ''){
Line 252 ⟶ 387:
}
if($le->{'action'} eq 'move_prot'){
$from="From [[:".$le->{'
$iter=$api->iterator(
list => 'logevents',
letype => 'protect',
letitle => $le->{'
lestart => $le->{'timestamp'},
);
Line 270 ⟶ 405:
}
$pd->{'logtitle'}=$pg;
$pd->{'isredir'}=defined( $p->{'redirect'} );
# now fill in the rest
my @keys = qw/prottype reason color logtitle/;
while(my ($k, $pd2) = each %{$pages{$t}}) {
@{$pd2}{@keys} = @{$pd}{@keys};
}
}
}
# The formatting here is a little strange, for backwards compat
my
my $txt = qq(<noinclude>{{User:AnomieBOT/PERTableHeader}}</noinclude>\n);
$txt.=qq(<div class="veblenbot-pertable">\n);
$txt.=qq(<templatestyles src="Template:Edit_fully-protected/color_legend/styles.css"/>\n);
$txt.=qq({| class="wikitable" style="padding:0em"\n);
$txt.=qq(|-\n);
my $ct=scalar
my $s=($ct==1?'':'s');
my $pg='User:AnomieBOT/'.$tag.'Table';
$txt.=qq(! <section begin="count" />$ct<section end="count" /> [[:Category:$cat|$type edit request$s]]
$txt.=qq(|-\n);
$txt.=qq(|\n);
Line 288 ⟶ 433:
$txt.=qq(! Protection level\n);
$txt.=qq(! class = "unsortable" | Last protection log entry\n);
for my $p (sort { my $x = $a->{'touched'} <=> $b->{'touched'}; $x = $a->{'title'}
my $c=
my $t=$p->{'title'};
my $et=encodetitle($p->{'logtitle'});
my $tt=$p->{'talk'};
my $pt=$p->{'prottype'};
my $r=$p->{'reason'};
my $
my $
$txt.=qq(|- class="protectededit-legend-$c"\n);
$txt.=qq(| $tl ($ttl)\n);
$txt.=strftime("| %F %H:%M\n", gmtime $p->{'touched'});
$txt.=qq(| $pt <span class="plainlinks">([//en.wikipedia.org/w/index.php?title=Special:Log&type=protect&page=$et log])</span>\n);
Line 316 ⟶ 462:
next;
}
my $intxt=$tok->{'revisions'}[0]{'slots'}{'main'}{'*'}//'';
$intxt=~s/^\s+|\s+$//;
$intxt=~s#<!--TS-->.*<!--/TS-->#<!--TS-->~~~~~<!--/TS-->#g;
Line 331 ⟶ 477:
$t=0 if $t<0;
return $t;
}
sub warnBadRegex {
my ($self, $api, $msg) = @_;
$msg =~ s/\s+$//;
my $file = __FILE__;
$msg =~ s/ at \Q$file\E line \d+\.$//;
if ( ! defined( $api->{'noedit'} ) ) {
my $warned = $api->store->{'warnedBadRegex'};
return if exists( $warned->{$msg} );
$warned->{$msg} = 1;
$api->store->{'warnedBadRegex'} = $warned;
}
$api->warn( "$msg\n" );
}
Line 338 ⟶ 501:
$c=~s/</</g;
$c=~s/>/>/g;
$c=~s/~/~/g;
$c=~s/\|\]\]/]]/g; # Pipe trick
$c=~s/\[\[\|/[[/g; # Reverse pipe trick
return $c;
}
|