SpecialRandompage.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. <?php
  2. /**
  3. * Implements Special:Randompage
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. * @ingroup SpecialPage
  22. * @author Rob Church <robchur@gmail.com>, Ilmari Karonen
  23. */
  24. use MediaWiki\MediaWikiServices;
  25. /**
  26. * Special page to direct the user to a random page
  27. *
  28. * @ingroup SpecialPage
  29. */
  30. class RandomPage extends SpecialPage {
  31. private $namespaces; // namespaces to select pages from
  32. protected $isRedir = false; // should the result be a redirect?
  33. protected $extra = []; // Extra SQL statements
  34. public function __construct( $name = 'Randompage' ) {
  35. $this->namespaces = MediaWikiServices::getInstance()->getNamespaceInfo()->
  36. getContentNamespaces();
  37. parent::__construct( $name );
  38. }
  39. public function getNamespaces() {
  40. return $this->namespaces;
  41. }
  42. public function setNamespace( $ns ) {
  43. if ( !$ns || $ns < NS_MAIN ) {
  44. $ns = NS_MAIN;
  45. }
  46. $this->namespaces = [ $ns ];
  47. }
  48. // select redirects instead of normal pages?
  49. public function isRedirect() {
  50. return $this->isRedir;
  51. }
  52. public function execute( $par ) {
  53. if ( is_string( $par ) ) {
  54. // Testing for stringiness since we want to catch
  55. // the empty string to mean main namespace only.
  56. $this->setNamespace(
  57. MediaWikiServices::getInstance()->getContentLanguage()->getNsIndex( $par ) );
  58. }
  59. $title = $this->getRandomTitle();
  60. if ( is_null( $title ) ) {
  61. $this->setHeaders();
  62. // Message: randompage-nopages, randomredirect-nopages
  63. $this->getOutput()->addWikiMsg( strtolower( $this->getName() ) . '-nopages',
  64. $this->getNsList(), count( $this->namespaces ) );
  65. return;
  66. }
  67. $redirectParam = $this->isRedirect() ? [ 'redirect' => 'no' ] : [];
  68. $query = array_merge( $this->getRequest()->getValues(), $redirectParam );
  69. unset( $query['title'] );
  70. $this->getOutput()->redirect( $title->getFullURL( $query ) );
  71. }
  72. /**
  73. * Get a comma-delimited list of namespaces we don't have
  74. * any pages in
  75. * @return string
  76. */
  77. private function getNsList() {
  78. $contLang = MediaWikiServices::getInstance()->getContentLanguage();
  79. $nsNames = [];
  80. foreach ( $this->namespaces as $n ) {
  81. if ( $n === NS_MAIN ) {
  82. $nsNames[] = $this->msg( 'blanknamespace' )->plain();
  83. } else {
  84. $nsNames[] = $contLang->getNsText( $n );
  85. }
  86. }
  87. return $contLang->commaList( $nsNames );
  88. }
  89. /**
  90. * Choose a random title.
  91. * @return Title|null Title object (or null if nothing to choose from)
  92. */
  93. public function getRandomTitle() {
  94. $randstr = wfRandom();
  95. $title = null;
  96. if ( !Hooks::run(
  97. 'SpecialRandomGetRandomTitle',
  98. [ &$randstr, &$this->isRedir, &$this->namespaces, &$this->extra, &$title ]
  99. ) ) {
  100. return $title;
  101. }
  102. $row = $this->selectRandomPageFromDB( $randstr );
  103. /* If we picked a value that was higher than any in
  104. * the DB, wrap around and select the page with the
  105. * lowest value instead! One might think this would
  106. * skew the distribution, but in fact it won't cause
  107. * any more bias than what the page_random scheme
  108. * causes anyway. Trust me, I'm a mathematician. :)
  109. */
  110. if ( !$row ) {
  111. $row = $this->selectRandomPageFromDB( "0" );
  112. }
  113. if ( $row ) {
  114. return Title::makeTitleSafe( $row->page_namespace, $row->page_title );
  115. }
  116. return null;
  117. }
  118. protected function getQueryInfo( $randstr ) {
  119. $redirect = $this->isRedirect() ? 1 : 0;
  120. $tables = [ 'page' ];
  121. $conds = array_merge( [
  122. 'page_namespace' => $this->namespaces,
  123. 'page_is_redirect' => $redirect,
  124. 'page_random >= ' . $randstr
  125. ], $this->extra );
  126. $joinConds = [];
  127. // Allow extensions to modify the query
  128. Hooks::run( 'RandomPageQuery', [ &$tables, &$conds, &$joinConds ] );
  129. return [
  130. 'tables' => $tables,
  131. 'fields' => [ 'page_title', 'page_namespace' ],
  132. 'conds' => $conds,
  133. 'options' => [
  134. 'ORDER BY' => 'page_random',
  135. 'LIMIT' => 1,
  136. ],
  137. 'join_conds' => $joinConds
  138. ];
  139. }
  140. private function selectRandomPageFromDB( $randstr, $fname = __METHOD__ ) {
  141. $dbr = wfGetDB( DB_REPLICA );
  142. $query = $this->getQueryInfo( $randstr );
  143. $res = $dbr->select(
  144. $query['tables'],
  145. $query['fields'],
  146. $query['conds'],
  147. $fname,
  148. $query['options'],
  149. $query['join_conds']
  150. );
  151. return $dbr->fetchObject( $res );
  152. }
  153. protected function getGroupName() {
  154. return 'redirects';
  155. }
  156. }