[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Clean up broken, unparseable upload filenames. 4 * 5 * Usage: php cleanupImages.php [--fix] 6 * Options: 7 * --fix Actually clean up titles; otherwise just checks for them 8 * 9 * Copyright © 2005-2006 Brion Vibber <[email protected]> 10 * https://www.mediawiki.org/ 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation; either version 2 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License along 23 * with this program; if not, write to the Free Software Foundation, Inc., 24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 25 * http://www.gnu.org/copyleft/gpl.html 26 * 27 * @file 28 * @author Brion Vibber <brion at pobox.com> 29 * @ingroup Maintenance 30 */ 31 32 require_once __DIR__ . '/cleanupTable.inc'; 33 34 /** 35 * Maintenance script to clean up broken, unparseable upload filenames. 36 * 37 * @ingroup Maintenance 38 */ 39 class ImageCleanup extends TableCleanup { 40 protected $defaultParams = array( 41 'table' => 'image', 42 'conds' => array(), 43 'index' => 'img_name', 44 'callback' => 'processRow', 45 ); 46 47 public function __construct() { 48 parent::__construct(); 49 $this->mDescription = "Script to clean up broken, unparseable upload filenames"; 50 } 51 52 protected function processRow( $row ) { 53 global $wgContLang; 54 55 $source = $row->img_name; 56 if ( $source == '' ) { 57 // Ye olde empty rows. Just kill them. 58 $this->killRow( $source ); 59 60 return $this->progress( 1 ); 61 } 62 63 $cleaned = $source; 64 65 // About half of old bad image names have percent-codes 66 $cleaned = rawurldecode( $cleaned ); 67 68 // We also have some HTML entities there 69 $cleaned = Sanitizer::decodeCharReferences( $cleaned ); 70 71 // Some are old latin-1 72 $cleaned = $wgContLang->checkTitleEncoding( $cleaned ); 73 74 // Many of remainder look like non-normalized unicode 75 $cleaned = $wgContLang->normalize( $cleaned ); 76 77 $title = Title::makeTitleSafe( NS_FILE, $cleaned ); 78 79 if ( is_null( $title ) ) { 80 $this->output( "page $source ($cleaned) is illegal.\n" ); 81 $safe = $this->buildSafeTitle( $cleaned ); 82 if ( $safe === false ) { 83 return $this->progress( 0 ); 84 } 85 $this->pokeFile( $source, $safe ); 86 87 return $this->progress( 1 ); 88 } 89 90 if ( $title->getDBkey() !== $source ) { 91 $munged = $title->getDBkey(); 92 $this->output( "page $source ($munged) doesn't match self.\n" ); 93 $this->pokeFile( $source, $munged ); 94 95 return $this->progress( 1 ); 96 } 97 98 return $this->progress( 0 ); 99 } 100 101 /** 102 * @param string $name 103 */ 104 private function killRow( $name ) { 105 if ( $this->dryrun ) { 106 $this->output( "DRY RUN: would delete bogus row '$name'\n" ); 107 } else { 108 $this->output( "deleting bogus row '$name'\n" ); 109 $db = wfGetDB( DB_MASTER ); 110 $db->delete( 'image', 111 array( 'img_name' => $name ), 112 __METHOD__ ); 113 } 114 } 115 116 private function filePath( $name ) { 117 if ( !isset( $this->repo ) ) { 118 $this->repo = RepoGroup::singleton()->getLocalRepo(); 119 } 120 121 return $this->repo->getRootDirectory() . '/' . $this->repo->getHashPath( $name ) . $name; 122 } 123 124 private function imageExists( $name, $db ) { 125 return $db->selectField( 'image', '1', array( 'img_name' => $name ), __METHOD__ ); 126 } 127 128 private function pageExists( $name, $db ) { 129 return $db->selectField( 130 'page', 131 '1', 132 array( 'page_namespace' => NS_FILE, 'page_title' => $name ), 133 __METHOD__ 134 ); 135 } 136 137 private function pokeFile( $orig, $new ) { 138 $path = $this->filePath( $orig ); 139 if ( !file_exists( $path ) ) { 140 $this->output( "missing file: $path\n" ); 141 $this->killRow( $orig ); 142 143 return; 144 } 145 146 $db = wfGetDB( DB_MASTER ); 147 148 /* 149 * To prevent key collisions in the update() statements below, 150 * if the target title exists in the image table, or if both the 151 * original and target titles exist in the page table, append 152 * increasing version numbers until the target title exists in 153 * neither. (See also bug 16916.) 154 */ 155 $version = 0; 156 $final = $new; 157 $conflict = ( $this->imageExists( $final, $db ) || 158 ( $this->pageExists( $orig, $db ) && $this->pageExists( $final, $db ) ) ); 159 160 while ( $conflict ) { 161 $this->output( "Rename conflicts with '$final'...\n" ); 162 $version++; 163 $final = $this->appendTitle( $new, "_$version" ); 164 $conflict = ( $this->imageExists( $final, $db ) || $this->pageExists( $final, $db ) ); 165 } 166 167 $finalPath = $this->filePath( $final ); 168 169 if ( $this->dryrun ) { 170 $this->output( "DRY RUN: would rename $path to $finalPath\n" ); 171 } else { 172 $this->output( "renaming $path to $finalPath\n" ); 173 // @todo FIXME: Should this use File::move()? 174 $db->begin( __METHOD__ ); 175 $db->update( 'image', 176 array( 'img_name' => $final ), 177 array( 'img_name' => $orig ), 178 __METHOD__ ); 179 $db->update( 'oldimage', 180 array( 'oi_name' => $final ), 181 array( 'oi_name' => $orig ), 182 __METHOD__ ); 183 $db->update( 'page', 184 array( 'page_title' => $final ), 185 array( 'page_title' => $orig, 'page_namespace' => NS_FILE ), 186 __METHOD__ ); 187 $dir = dirname( $finalPath ); 188 if ( !file_exists( $dir ) ) { 189 if ( !wfMkdirParents( $dir, null, __METHOD__ ) ) { 190 $this->output( "RENAME FAILED, COULD NOT CREATE $dir" ); 191 $db->rollback( __METHOD__ ); 192 193 return; 194 } 195 } 196 if ( rename( $path, $finalPath ) ) { 197 $db->commit( __METHOD__ ); 198 } else { 199 $this->error( "RENAME FAILED" ); 200 $db->rollback( __METHOD__ ); 201 } 202 } 203 } 204 205 private function appendTitle( $name, $suffix ) { 206 return preg_replace( '/^(.*)(\..*?)$/', 207 "\\1$suffix\\2", $name ); 208 } 209 210 private function buildSafeTitle( $name ) { 211 $x = preg_replace_callback( 212 '/([^' . Title::legalChars() . ']|~)/', 213 array( $this, 'hexChar' ), 214 $name ); 215 216 $test = Title::makeTitleSafe( NS_FILE, $x ); 217 if ( is_null( $test ) || $test->getDBkey() !== $x ) { 218 $this->error( "Unable to generate safe title from '$name', got '$x'" ); 219 220 return false; 221 } 222 223 return $x; 224 } 225 } 226 227 $maintClass = "ImageCleanup"; 228 require_once RUN_MAINTENANCE_IF_MAIN;
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |