/extensions/Translate/scripts/pagetranslation-check-database.php

https://github.com/ChuguluGames/mediawiki-svn · PHP · 221 lines · 168 code · 38 blank · 15 comment · 24 complexity · 61e406d77fd94066170321f19fc6476d MD5 · raw file

  1. <?php
  2. /**
  3. * Script to check the consistency of the databases of the page translation
  4. * feature and fix problems.
  5. *
  6. * @author Niklas Laxstrom
  7. * @copyright Copyright © 2010-2011, Niklas Laxström
  8. * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
  9. * @file
  10. */
  11. // Standard boilerplate to define $IP
  12. if ( getenv( 'MW_INSTALL_PATH' ) !== false ) {
  13. $IP = getenv( 'MW_INSTALL_PATH' );
  14. } else {
  15. $dir = dirname( __FILE__ ); $IP = "$dir/../../..";
  16. }
  17. require_once( "$IP/maintenance/Maintenance.php" );
  18. /**
  19. * Script to check the consistency of the databases of the page translation
  20. * feature and fix problems.
  21. * @todo Document methods.
  22. */
  23. class PTCheckDB extends Maintenance {
  24. public function __construct() {
  25. parent::__construct();
  26. $this->mDescription = 'Check the consistency of the databases of the page translation feature and fix problems.';
  27. $this->addOption( 'fix', 'Fix the found problems if possible' );
  28. }
  29. public function execute() {
  30. $fixes = $this->checkSectionTable();
  31. $fixes += $this->checkRevTagTable();
  32. $dbw = wfGetDB( DB_MASTER );
  33. if ( $this->getOption( 'fix' ) ) {
  34. $this->output( "Performing the following fixes:\n" );
  35. foreach ( $fixes as $name => $data ) {
  36. $this->output( "$name: $data[0]...", $name );
  37. $dbw->delete( $data[1], '*', $data[2], __METHOD__ );
  38. }
  39. } else {
  40. $this->output( "Use --fix to perform following fixes:\n" );
  41. foreach ( $fixes as $name => $data ) {
  42. $sql = $dbw->selectSQLtext( $data[1], '*', $data[2] );
  43. $sql = preg_replace( '~^SELECT~', 'DELETE', $sql );
  44. $this->output( "$name: $data[0] - $sql\n" );
  45. }
  46. }
  47. }
  48. protected function checkSectionTable() {
  49. $fixes = array();
  50. $dbr = wfGetDB( DB_SLAVE );
  51. $pages = $dbr->select( 'translate_sections', 'trs_page', null, __METHOD__, array( 'GROUP BY' => 'trs_page' ) );
  52. $this->output( "Found {$pages->numRows()} pages in the section table\n" );
  53. $this->output( "Checking that they match a valid translatable page...\n\n" );
  54. foreach ( $pages as $row ) {
  55. $id = $row->trs_page;
  56. $sections = $dbr->select( 'translate_sections', 'trs_key', array( 'trs_page' => $id ), __METHOD__ );
  57. $title = Title::newFromID( $id );
  58. $sectionNames = $this->getSectionNames( $sections );
  59. $name = $title ? $title->getPrefixedText() : "#$id";
  60. $this->output( "Page $name has {$sections->numRows()} sections [$sectionNames]\n" );
  61. if ( !$title ) {
  62. $name = "#$id";
  63. $deleted = $this->findDeletedPage( $id );
  64. if ( $deleted === false ) {
  65. $this->output( "Page id $id does not correspond to any page\n" );
  66. } else {
  67. $name .= "<$deleted>";
  68. $this->output( "Page id $id corresponds to a deleted page $deleted\n" );
  69. }
  70. $fixes["$name <sections>"] = array( 'delete sections', 'translate_section', array( 'trs_page' => $id ) );
  71. } else {
  72. $name = $title->getPrefixedText();
  73. $page = TranslatablePage::newFromTitle( $title );
  74. $tagged = $page->getReadyTag();
  75. $marked = $page->getMarkedTag();
  76. $latest = $title->getLatestRevId();
  77. $this->output( "Revision numbers: <tagged, marked, latest> <$tagged, $marked, $latest>\n" );
  78. if ( strval( $marked ) === '' ) {
  79. $this->output( "These sections do not belong the current page (anymore?)\n" );
  80. $fixes["$name <sections>"] = array( 'delete sections', 'translate_section', array( 'trs_page' => $id ) );
  81. }
  82. }
  83. $this->output( "\n" );
  84. }
  85. return $fixes;
  86. }
  87. protected function checkRevTagTable() {
  88. $fixes = array();
  89. $dbr = wfGetDB( DB_SLAVE );
  90. $tags = array( 'tp:mark', 'tp:tag', 'tp:transver', 'fuzzy' );
  91. $pages = $dbr->select( 'revtag', 'rt_page', null, __METHOD__, array( 'GROUP BY' => 'rt_page' ) );
  92. $this->output( "Checking that tags match a valid page...\n\n" );
  93. foreach ( $pages as $row ) {
  94. $id = $row->rt_page;
  95. $title = Title::newFromID( $id );
  96. $name = $title ? $title->getPrefixedText() : "#$id";
  97. if ( !$title ) {
  98. $name = "#$id";
  99. $deleted = $this->findDeletedPage( $id );
  100. if ( $deleted === false ) {
  101. $this->output( "Page id $id does not correspond to any page\n" );
  102. $fixes["$name <revtag>"] = array( 'delete tags', 'revtag', array( 'rt_page' => $id ) );
  103. } else {
  104. $name .= "<$deleted>";
  105. }
  106. }
  107. }
  108. $this->output( "Checked {$pages->numRows()} pages in the revtag table\n" );
  109. $this->output( "\n\nValidating tags...\n" );
  110. $result = $dbr->select( 'revtag', '*', null, __METHOD__ );
  111. foreach ( $result as $_ ) {
  112. if ( !isset( $tags[$_->rt_type] ) ) {
  113. $name = $this->idToName( $_->rt_page );
  114. $this->output( "Page $name has unknown tag {$_->rt_type}\n" );
  115. $fixes["$name <revtag:unknown:{$_->rt_type}>"] =
  116. array( 'delete tag', 'revtag', array( 'rt_page' => $id, 'rt_type' => $_->rt_type ) );
  117. continue;
  118. } elseif ( $_->rt_type === RevTag::getType( 'tp:transver' ) ) {
  119. $check = $this->checkTransrevRevision( $rev );
  120. if ( $check !== true ) {
  121. $name = $this->idToName( $_->rt_page );
  122. $this->output( "Page $name has invalid tp:transver: $check\n" );
  123. $fixes["$name <revtag:transver>"] =
  124. array( 'delete tag', 'revtag', array( 'rt_page' => $id, 'rt_type' => $_->rt_type ) );
  125. }
  126. }
  127. }
  128. $this->output( "Checked {$result->numRows()} tags in the revtag table\n\n\n" );
  129. return $fixes;
  130. }
  131. protected function idToName( $id ) {
  132. $title = Title::newFromID( $id );
  133. $name = $title ? $title->getPrefixedText() : "#$id";
  134. if ( !$title ) {
  135. $name .= $this->findDeletedPage( $id );
  136. }
  137. return $name;
  138. }
  139. protected function getSectionNames( $result ) {
  140. $names = array();
  141. foreach ( $result as $section ) {
  142. $names[] = $section->trs_key;
  143. }
  144. return implode( ', ', $names );
  145. }
  146. protected function findDeletedPage( $id ) {
  147. $dbr = wfGetDB( DB_SLAVE );
  148. $page = $dbr->selectRow( 'archive', array( 'ar_namespace', 'ar_title' ),
  149. array( 'ar_page_id' => $id ), __METHOD__ );
  150. if ( $page ) {
  151. $title = Title::makeTitleSafe( $page->ar_namespace, $page->ar_title );
  152. if ( $title ) {
  153. return $title->getPrefixedText();
  154. }
  155. }
  156. return false;
  157. }
  158. protected function checkTransrevRevision( $revId ) {
  159. static $cache = array();
  160. if ( isset( $cache[$revId] ) ) {
  161. return $cache[$revId];
  162. }
  163. $revision = Revision::newFromId( $revId );
  164. if ( !$revision ) {
  165. $cache[$revId] = 'no such revision';
  166. } else {
  167. $title = $revision->getTitle();
  168. if ( !$title ) {
  169. $cache[$revId] = 'no title for the revision';
  170. } else {
  171. $page = TranslatablePage::newFromTitle( $title );
  172. if ( $page->getMarkedTag() === false ) {
  173. $cache[$revId] = 'revision belongs to a page that is not marked for translation';
  174. } else {
  175. $cache[$revId] = true;
  176. }
  177. }
  178. }
  179. return $cache[$revId];
  180. }
  181. }
  182. $maintClass = 'PTCheckDB';
  183. require_once( DO_MAINTENANCE );