1<?php 2/** 3 * Refresh file headers from metadata. 4 * 5 * Usage: php refreshFileHeaders.php 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 * http://www.gnu.org/copyleft/gpl.html 21 * 22 * @file 23 * @ingroup Maintenance 24 */ 25 26use MediaWiki\MediaWikiServices; 27 28require_once __DIR__ . '/Maintenance.php'; 29 30/** 31 * Maintenance script to refresh file headers from metadata 32 * 33 * @ingroup Maintenance 34 */ 35class RefreshFileHeaders extends Maintenance { 36 public function __construct() { 37 parent::__construct(); 38 $this->addDescription( 'Script to update file HTTP headers' ); 39 $this->addOption( 'verbose', 'Output information about each file.', false, false, 'v' ); 40 $this->addOption( 'start', 'Name of file to start with', false, true ); 41 $this->addOption( 'end', 'Name of file to end with', false, true ); 42 $this->addOption( 'media_type', 'Media type to filter for', false, true ); 43 $this->addOption( 'major_mime', 'Major mime type to filter for', false, true ); 44 $this->addOption( 'minor_mime', 'Minor mime type to filter for', false, true ); 45 $this->addOption( 46 'refreshContentType', 47 'Set true to refresh file content type from mime data in db', 48 false, 49 false 50 ); 51 $this->setBatchSize( 200 ); 52 } 53 54 public function execute() { 55 $repo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo(); 56 $start = str_replace( ' ', '_', $this->getOption( 'start', '' ) ); // page on img_name 57 $end = str_replace( ' ', '_', $this->getOption( 'end', '' ) ); // page on img_name 58 // filter by img_media_type 59 $media_type = str_replace( ' ', '_', $this->getOption( 'media_type', '' ) ); 60 // filter by img_major_mime 61 $major_mime = str_replace( ' ', '_', $this->getOption( 'major_mime', '' ) ); 62 // filter by img_minor_mime 63 $minor_mime = str_replace( ' ', '_', $this->getOption( 'minor_mime', '' ) ); 64 65 $count = 0; 66 $dbr = $this->getDB( DB_REPLICA ); 67 68 $fileQuery = LocalFile::getQueryInfo(); 69 70 do { 71 $conds = [ "img_name > {$dbr->addQuotes( $start )}" ]; 72 73 if ( strlen( $end ) ) { 74 $conds[] = "img_name <= {$dbr->addQuotes( $end )}"; 75 } 76 77 if ( strlen( $media_type ) ) { 78 $conds[] = "img_media_type = {$dbr->addQuotes( $media_type )}"; 79 } 80 81 if ( strlen( $major_mime ) ) { 82 $conds[] = "img_major_mime = {$dbr->addQuotes( $major_mime )}"; 83 } 84 85 if ( strlen( $minor_mime ) ) { 86 $conds[] = "img_minor_mime = {$dbr->addQuotes( $minor_mime )}"; 87 } 88 89 $res = $dbr->select( $fileQuery['tables'], 90 $fileQuery['fields'], 91 $conds, 92 __METHOD__, 93 [ 94 'LIMIT' => $this->getBatchSize(), 95 'ORDER BY' => 'img_name ASC' 96 ], 97 $fileQuery['joins'] 98 ); 99 100 if ( $res->numRows() > 0 ) { 101 $row1 = $res->current(); 102 $this->output( "Processing next {$res->numRows()} row(s) starting with {$row1->img_name}.\n" ); 103 $res->rewind(); 104 } 105 106 $backendOperations = []; 107 108 foreach ( $res as $row ) { 109 $file = $repo->newFileFromRow( $row ); 110 $headers = $file->getContentHeaders(); 111 if ( $this->getOption( 'refreshContentType', false ) ) { 112 $headers['Content-Type'] = $row->img_major_mime . '/' . $row->img_minor_mime; 113 } 114 115 if ( count( $headers ) ) { 116 $backendOperations[] = [ 117 'op' => 'describe', 'src' => $file->getPath(), 'headers' => $headers 118 ]; 119 } 120 121 // Do all of the older file versions... 122 foreach ( $file->getHistory() as $oldFile ) { 123 $headers = $oldFile->getContentHeaders(); 124 if ( count( $headers ) ) { 125 $backendOperations[] = [ 126 'op' => 'describe', 'src' => $oldFile->getPath(), 'headers' => $headers 127 ]; 128 } 129 } 130 131 if ( $this->hasOption( 'verbose' ) ) { 132 $this->output( "Queued headers update for file '{$row->img_name}'.\n" ); 133 } 134 135 $start = $row->img_name; // advance 136 } 137 138 $backendOperationsCount = count( $backendOperations ); 139 $count += $backendOperationsCount; 140 141 $this->output( "Updating headers for {$backendOperationsCount} file(s).\n" ); 142 $this->updateFileHeaders( $repo, $backendOperations ); 143 } while ( $res->numRows() === $this->getBatchSize() ); 144 145 $this->output( "Done. Updated headers for $count file(s).\n" ); 146 } 147 148 /** 149 * @param LocalRepo $repo 150 * @param array $backendOperations 151 */ 152 protected function updateFileHeaders( $repo, $backendOperations ) { 153 $status = $repo->getBackend()->doQuickOperations( $backendOperations ); 154 155 if ( !$status->isGood() ) { 156 $this->error( "Encountered error: " . print_r( $status, true ) ); 157 } 158 } 159} 160 161$maintClass = RefreshFileHeaders::class; 162require_once RUN_MAINTENANCE_IF_MAIN; 163