]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - maintenance/namespaceDupes.php
MediaWiki 1.16.0
[autoinstallsdev/mediawiki.git] / maintenance / namespaceDupes.php
1 <?php
2 /**
3  * Check for articles to fix after adding/deleting namespaces
4  *
5  * Copyright (C) 2005-2007 Brion Vibber <brion@pobox.com>
6  * http://www.mediawiki.org/
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  * http://www.gnu.org/copyleft/gpl.html
22  *
23  * @ingroup Maintenance
24  */
25
26 require_once( dirname(__FILE__) . '/Maintenance.php' );
27
28 class NamespaceConflictChecker extends Maintenance {
29         public function __construct() {
30                 parent::__construct();
31                 $this->mDescription = "";
32                 $this->addOption( 'fix', 'Attempt to automatically fix errors' );
33                 $this->addOption( 'suffix', "Dupes will be renamed with correct namespace with\n" .
34                                                                         "\t\t<text> Appended after the article name", false, true );
35                 $this->addOption( 'prefix', "Do an explicit check for the given title prefix\n" .
36                                                                         "\t\tappended after the article name", false, true );
37         }
38
39         public function execute() {
40                 global $wgTitle;
41
42                 $this->db = wfGetDB( DB_MASTER );
43                 $wgTitle = Title::newFromText( 'Namespace title conflict cleanup script' );
44
45                 $fix = $this->hasOption( 'fix' );
46                 $suffix = $this->getOption( 'suffix', '' );
47                 $prefix = $this->getOption( 'prefix', '' );
48                 $key = intval( $this->getOption( 'key', 0 ) );
49
50                 if( $prefix ) {
51                         $retval = $this->checkPrefix( $key, $prefix, $fix, $suffix );
52                 } else {
53                         $retval = $this->checkAll( $fix, $suffix );
54                 }
55         
56                 if( $retval ) {
57                         $this->output( "\nLooks good!\n" );
58                 } else {
59                         $this->output( "\nOh noeees\n" );
60                 }
61         }
62
63         /**
64          * @todo Document
65          * @param $fix bool Whether or not to fix broken entries
66          * @param $suffix String Suffix to append to renamed articles
67          */
68         private function checkAll( $fix, $suffix = '' ) {
69                 global $wgContLang, $wgNamespaceAliases, $wgCanonicalNamespaceNames;
70                 global $wgCapitalLinks;
71                 
72                 $spaces = array();
73                 
74                 // List interwikis first, so they'll be overridden
75                 // by any conflicting local namespaces.
76                 foreach( $this->getInterwikiList() as $prefix ) {
77                         $name = $wgContLang->ucfirst( $prefix );
78                         $spaces[$name] = 0;
79                 }
80
81                 // Now pull in all canonical and alias namespaces...
82                 foreach( $wgCanonicalNamespaceNames as $ns => $name ) {
83                         // This includes $wgExtraNamespaces
84                         if( $name !== '' ) {
85                                 $spaces[$name] = $ns;
86                         }
87                 }
88                 foreach( $wgContLang->getNamespaces() as $ns => $name ) {
89                         if( $name !== '' ) {
90                                 $spaces[$name] = $ns;
91                         }
92                 }
93                 foreach( $wgNamespaceAliases as $name => $ns ) {
94                         $spaces[$name] = $ns;
95                 }
96                 foreach( $wgContLang->getNamespaceAliases() as $name => $ns ) {
97                         $spaces[$name] = $ns;
98                 }
99                 
100                 // We'll need to check for lowercase keys as well,
101                 // since we're doing case-sensitive searches in the db.
102                 foreach( $spaces as $name => $ns ) {
103                         $moreNames = array();
104                         $moreNames[] = $wgContLang->uc( $name );
105                         $moreNames[] = $wgContLang->ucfirst( $wgContLang->lc( $name ) );
106                         $moreNames[] = $wgContLang->ucwords( $name );
107                         $moreNames[] = $wgContLang->ucwords( $wgContLang->lc( $name ) );
108                         $moreNames[] = $wgContLang->ucwordbreaks( $name );
109                         $moreNames[] = $wgContLang->ucwordbreaks( $wgContLang->lc( $name ) );
110                         if( !$wgCapitalLinks ) {
111                                 foreach( $moreNames as $altName ) {
112                                         $moreNames[] = $wgContLang->lcfirst( $altName );
113                                 }
114                                 $moreNames[] = $wgContLang->lcfirst( $name );
115                         }
116                         foreach( array_unique( $moreNames ) as $altName ) {
117                                 if( $altName !== $name ) {
118                                         $spaces[$altName] = $ns;
119                                 }
120                         }
121                 }
122                 
123                 ksort( $spaces );
124                 asort( $spaces );
125                 
126                 $ok = true;
127                 foreach( $spaces as $name => $ns ) {
128                         $ok = $this->checkNamespace( $ns, $name, $fix, $suffix ) && $ok;
129                 }
130                 return $ok;
131         }
132
133         /**
134          * Get the interwiki list
135          * @todo Needs to respect interwiki cache!
136          * @return array
137          */
138         private function getInterwikiList() {
139                 $result = $this->db->select( 'interwiki', array( 'iw_prefix' ) );
140                 $prefixes = array();
141                 foreach( $result as $row ) {
142                         $prefixes[] = $row->iw_prefix;
143                 }
144                 $this->db->freeResult( $result );
145                 return $prefixes;
146         }
147
148         /**
149          * @todo Document
150          * @param $ns int A namespace id
151          * @param $name String
152          * @param $fix bool Whether to fix broken entries
153          * @param $suffix String Suffix to append to renamed articles
154          */
155         private function checkNamespace( $ns, $name, $fix, $suffix = '' ) {
156                 $conflicts = $this->getConflicts( $ns, $name );
157                 $count = count( $conflicts );
158                 if( $count == 0 ) {
159                         return true;
160                 }
161
162                 $ok = true;
163                 foreach( $conflicts as $row ) {
164                         $resolvable = $this->reportConflict( $row, $suffix );
165                         $ok = $ok && $resolvable;
166                         if( $fix && ( $resolvable || $suffix != '' ) ) {
167                                 $ok = $this->resolveConflict( $row, $resolvable, $suffix ) && $ok;
168                         }
169                 }
170                 return $ok;
171         }
172         
173         /**
174          * @todo: do this for reals
175          */
176         private function checkPrefix( $key, $prefix, $fix, $suffix = '' ) {
177                 $this->output( "Checking prefix \"$prefix\" vs namespace $key\n" );
178                 return $this->checkNamespace( $key, $prefix, $fix, $suffix );
179         }
180
181         /**
182          * Find pages in mainspace that have a prefix of the new namespace
183          * so we know titles that will need migrating
184          * @param $ns int Namespace id (id for new namespace?)
185          * @param $name String Prefix that is being made a namespace
186          */
187         private function getConflicts( $ns, $name ) {
188                 $page  = 'page';
189                 $table = $this->db->tableName( $page );
190
191                 $prefix     = $this->db->strencode( $name );
192                 $encNamespace = $this->db->addQuotes( $ns );
193
194                 $titleSql = "TRIM(LEADING '$prefix:' FROM {$page}_title)";
195                 if( $ns == 0 ) {
196                         // An interwiki; try an alternate encoding with '-' for ':'
197                         $titleSql = $this->db->buildConcat( array( "'$prefix-'", $titleSql ) );
198                 }
199                                      
200                 $sql = "SELECT {$page}_id    AS id,
201                                {$page}_title AS oldtitle,
202                                $encNamespace AS namespace,
203                                $titleSql     AS title
204                           FROM {$table}
205                          WHERE {$page}_namespace=0
206                            AND {$page}_title " . $this->db->buildLike( $name . ':', $this->db->anyString() );
207
208                 $result = $this->db->query( $sql, __METHOD__ );
209
210                 $set = array();
211                 foreach( $result as $row ) {
212                         $set[] = $row;
213                 }
214                 $this->db->freeResult( $result );
215
216                 return $set;
217         }
218
219         /**
220          * Report any conflicts we find
221          */
222         private function reportConflict( $row, $suffix ) {
223                 $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
224                 if( is_null($newTitle) || !$newTitle->canExist() ) {
225                         // Title is also an illegal title...
226                         // For the moment we'll let these slide to cleanupTitles or whoever.
227                         $this->output( sprintf( "... %d (0,\"%s\")\n",
228                                 $row->id,
229                                 $row->oldtitle ) );
230                         $this->output( "...  *** cannot resolve automatically; illegal title ***\n" );
231                         return false;
232                 }
233
234                 $this->output( sprintf( "... %d (0,\"%s\") -> (%d,\"%s\") [[%s]]\n",
235                         $row->id,
236                         $row->oldtitle,
237                         $newTitle->getNamespace(),
238                         $newTitle->getDBkey(),
239                         $newTitle->getPrefixedText() ) );
240
241                 $id = $newTitle->getArticleId();
242                 if( $id ) {
243                         $this->output( "...  *** cannot resolve automatically; page exists with ID $id ***\n" );
244                         return false;
245                 } else {
246                         return true;
247                 }
248         }
249
250         /**
251          * Resolve any conflicts
252          * @param $row Row from the page table to fix
253          * @param $resolveable bool 
254          * @param $suffix String Suffix to append to the fixed page
255          */
256         private function resolveConflict( $row, $resolvable, $suffix ) {
257                 if( !$resolvable ) {
258                         $this->output( "...  *** old title {$row->title}\n" );
259                         while( true ) {
260                                 $row->title .= $suffix;
261                                 $this->output( "...  *** new title {$row->title}\n" );
262                                 $title = Title::makeTitleSafe( $row->namespace, $row->title );
263                                 if ( ! $title ) {
264                                         $this->output( "... !!! invalid title\n" );
265                                         return false;
266                                 }
267                                 if ( $id = $title->getArticleId() ) {
268                                         $this->output( "...  *** page exists with ID $id ***\n" );
269                                 } else {        
270                                         break;
271                                 }
272                         }
273                         $this->output( "...  *** using suffixed form [[" . $title->getPrefixedText() . "]] ***\n" );
274                 }
275                 $this->resolveConflictOn( $row, 'page', 'page' );
276                 return true;
277         }
278
279         /**
280          * Resolve a given conflict
281          * @param $row Row from the old broken entry
282          * @param $table String Table to update
283          * @param $prefix String Prefix for column name, like page or ar
284          */
285         private function resolveConflictOn( $row, $table, $prefix ) {
286                 $this->output( "... resolving on $table... " );
287                 $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
288                 $this->db->update( $table,
289                         array(
290                                 "{$prefix}_namespace" => $newTitle->getNamespace(),
291                                 "{$prefix}_title"     => $newTitle->getDBkey(),
292                         ),
293                         array(
294                                 "{$prefix}_namespace" => 0,
295                                 "{$prefix}_title"     => $row->oldtitle,
296                         ),
297                         __METHOD__ );
298                 $this->output( "ok.\n" );
299                 return true;
300         }
301 }
302
303 $maintClass = "NamespaceConflictChecker";
304 require_once( DO_MAINTENANCE );