00001 <?php
00012
00013 if ( getenv( 'MW_INSTALL_PATH' ) !== false ) {
00014 $IP = getenv( 'MW_INSTALL_PATH' );
00015 } else {
00016 $dir = dirname( __FILE__ ); $IP = "$dir/../../..";
00017 }
00018 require_once( "$IP/maintenance/Maintenance.php" );
00019
00025 class PTCheckDB extends Maintenance {
00026 public function __construct() {
00027 parent::__construct();
00028 $this->mDescription = 'Check the consistency of the databases of the page translation feature and fix problems.';
00029 $this->addOption( 'fix', 'Fix the found problems if possible' );
00030 }
00031
00032 public function execute() {
00033 $fixes = $this->checkSectionTable();
00034 $fixes += $this->checkRevTagTable();
00035
00036 $dbw = wfGetDB( DB_MASTER );
00037 if ( $this->getOption( 'fix' ) ) {
00038 $this->output( "Performing the following fixes:\n" );
00039
00040 foreach ( $fixes as $name => $data ) {
00041 $this->output( "$name: $data[0]...", $name );
00042 $dbw->delete( $data[1], '*', $data[2], __METHOD__ );
00043 }
00044 } else {
00045 $this->output( "Use --fix to perform following fixes:\n" );
00046
00047 foreach ( $fixes as $name => $data ) {
00048 $sql = $dbw->selectSQLtext( $data[1], '*', $data[2] );
00049 $sql = preg_replace( '~^SELECT~', 'DELETE', $sql );
00050 $this->output( "$name: $data[0] - $sql\n" );
00051 }
00052 }
00053 }
00054
00055 protected function checkSectionTable() {
00056 $fixes = array();
00057
00058 $dbr = wfGetDB( DB_SLAVE );
00059 $pages = $dbr->select( 'translate_sections', 'trs_page', null, __METHOD__, array( 'GROUP BY' => 'trs_page' ) );
00060
00061 $this->output( "Found {$pages->numRows()} pages in the section table\n" );
00062 $this->output( "Checking that they match a valid translatable page...\n\n" );
00063
00064 foreach ( $pages as $row ) {
00065 $id = $row->trs_page;
00066 $sections = $dbr->select( 'translate_sections', 'trs_key', array( 'trs_page' => $id ), __METHOD__ );
00067 $title = Title::newFromID( $id );
00068 $sectionNames = $this->getSectionNames( $sections );
00069
00070 $name = $title ? $title->getPrefixedText() : "#$id";
00071 $this->output( "Page $name has {$sections->numRows()} sections [$sectionNames]\n" );
00072
00073 if ( !$title ) {
00074 $name = "#$id";
00075 $deleted = $this->findDeletedPage( $id );
00076 if ( $deleted === false ) {
00077 $this->output( "Page id $id does not correspond to any page\n" );
00078 } else {
00079 $name .= "<$deleted>";
00080 $this->output( "Page id $id corresponds to a deleted page $deleted\n" );
00081 }
00082 $fixes["$name <sections>"] = array( 'delete sections', 'translate_section', array( 'trs_page' => $id ) );
00083 } else {
00084 $name = $title->getPrefixedText();
00085 $page = TranslatablePage::newFromTitle( $title );
00086 $tagged = $page->getReadyTag();
00087 $marked = $page->getMarkedTag();
00088 $latest = $title->getLatestRevId();
00089 $this->output( "Revision numbers: <tagged, marked, latest> <$tagged, $marked, $latest>\n" );
00090 if ( strval( $marked ) === '' ) {
00091 $this->output( "These sections do not belong the current page (anymore?)\n" );
00092 $fixes["$name <sections>"] = array( 'delete sections', 'translate_section', array( 'trs_page' => $id ) );
00093 }
00094 }
00095
00096 $this->output( "\n" );
00097 }
00098
00099 return $fixes;
00100 }
00101
00102 protected function checkRevTagTable() {
00103 $fixes = array();
00104
00105 $dbr = wfGetDB( DB_SLAVE );
00106
00107 $tags = array( 'tp:mark', 'tp:tag', 'tp:transver', 'fuzzy' );
00108
00109 $pages = $dbr->select( 'revtag', 'rt_page', null, __METHOD__, array( 'GROUP BY' => 'rt_page' ) );
00110 $this->output( "Checking that tags match a valid page...\n\n" );
00111
00112 foreach ( $pages as $row ) {
00113 $id = $row->rt_page;
00114 $title = Title::newFromID( $id );
00115 $name = $title ? $title->getPrefixedText() : "#$id";
00116
00117 if ( !$title ) {
00118 $name = "#$id";
00119 $deleted = $this->findDeletedPage( $id );
00120 if ( $deleted === false ) {
00121 $this->output( "Page id $id does not correspond to any page\n" );
00122 $fixes["$name <revtag>"] = array( 'delete tags', 'revtag', array( 'rt_page' => $id ) );
00123 } else {
00124 $name .= "<$deleted>";
00125 }
00126 }
00127 }
00128
00129 $this->output( "Checked {$pages->numRows()} pages in the revtag table\n" );
00130 $this->output( "\n\nValidating tags...\n" );
00131
00132 $result = $dbr->select( 'revtag', '*', null, __METHOD__ );
00133 foreach ( $result as $_ ) {
00134 if ( !isset( $tags[$_->rt_type] ) ) {
00135 $name = $this->idToName( $_->rt_page );
00136 $this->output( "Page $name has unknown tag {$_->rt_type}\n" );
00137 $fixes["$name <revtag:unknown:{$_->rt_type}>"] =
00138 array( 'delete tag', 'revtag', array( 'rt_page' => $id, 'rt_type' => $_->rt_type ) );
00139 continue;
00140 } elseif ( $_->rt_type === RevTag::getType( 'tp:transver' ) ) {
00141 $check = $this->checkTransrevRevision( $rev );
00142 if ( $check !== true ) {
00143 $name = $this->idToName( $_->rt_page );
00144 $this->output( "Page $name has invalid tp:transver: $check\n" );
00145 $fixes["$name <revtag:transver>"] =
00146 array( 'delete tag', 'revtag', array( 'rt_page' => $id, 'rt_type' => $_->rt_type ) );
00147 }
00148 }
00149 }
00150
00151 $this->output( "Checked {$result->numRows()} tags in the revtag table\n\n\n" );
00152
00153 return $fixes;
00154 }
00155
00156 protected function idToName( $id ) {
00157 $title = Title::newFromID( $id );
00158 $name = $title ? $title->getPrefixedText() : "#$id";
00159
00160 if ( !$title ) {
00161 $name .= $this->findDeletedPage( $id );
00162 }
00163
00164 return $name;
00165 }
00166
00167 protected function getSectionNames( $result ) {
00168 $names = array();
00169
00170 foreach ( $result as $section ) {
00171 $names[] = $section->trs_key;
00172 }
00173
00174 return implode( ', ', $names );
00175 }
00176
00177 protected function findDeletedPage( $id ) {
00178 $dbr = wfGetDB( DB_SLAVE );
00179 $page = $dbr->selectRow( 'archive', array( 'ar_namespace', 'ar_title' ),
00180 array( 'ar_page_id' => $id ), __METHOD__ );
00181
00182 if ( $page ) {
00183 $title = Title::makeTitleSafe( $page->ar_namespace, $page->ar_title );
00184 if ( $title ) {
00185 return $title->getPrefixedText();
00186 }
00187 }
00188
00189 return false;
00190 }
00191
00192 protected function checkTransrevRevision( $revId ) {
00193 static $cache = array();
00194
00195 if ( isset( $cache[$revId] ) ) {
00196 return $cache[$revId];
00197 }
00198
00199 $revision = Revision::newFromId( $revId );
00200 if ( !$revision ) {
00201 $cache[$revId] = 'no such revision';
00202 } else {
00203 $title = $revision->getTitle();
00204 if ( !$title ) {
00205 $cache[$revId] = 'no title for the revision';
00206 } else {
00207 $page = TranslatablePage::newFromTitle( $title );
00208 if ( $page->getMarkedTag() === false ) {
00209 $cache[$revId] = 'revision belongs to a page that is not marked for translation';
00210 } else {
00211 $cache[$revId] = true;
00212 }
00213 }
00214 }
00215
00216 return $cache[$revId];
00217 }
00218 }
00219
00220 $maintClass = 'PTCheckDB';
00221 require_once( DO_MAINTENANCE );