MediaWiki REL1_31
SpecialRandomInCategory.php
Go to the documentation of this file.
1<?php
51 protected $extra = []; // Extra SQL statements
53 protected $category = false; // Title object of category
55 protected $maxOffset = 30; // Max amount to fudge randomness by.
57 private $maxTimestamp = null;
59 private $minTimestamp = null;
60
61 public function __construct( $name = 'RandomInCategory' ) {
62 parent::__construct( $name );
63 }
64
69 public function setCategory( Title $cat ) {
70 $this->category = $cat;
71 $this->maxTimestamp = null;
72 $this->minTimestamp = null;
73 }
74
75 protected function getFormFields() {
76 $this->addHelpLink( 'Help:RandomInCategory' );
77
78 return [
79 'category' => [
80 'type' => 'title',
81 'namespace' => NS_CATEGORY,
82 'relative' => true,
83 'label-message' => 'randomincategory-category',
84 'required' => true,
85 ]
86 ];
87 }
88
89 public function requiresWrite() {
90 return false;
91 }
92
93 public function requiresUnblock() {
94 return false;
95 }
96
97 protected function getDisplayFormat() {
98 return 'ooui';
99 }
100
101 protected function alterForm( HTMLForm $form ) {
102 $form->setSubmitTextMsg( 'randomincategory-submit' );
103 }
104
105 protected function setParameter( $par ) {
106 // if subpage present, fake form submission
107 $this->onSubmit( [ 'category' => $par ] );
108 }
109
110 public function onSubmit( array $data ) {
111 $cat = false;
112
113 $categoryStr = $data['category'];
114
115 if ( $categoryStr ) {
116 $cat = Title::newFromText( $categoryStr, NS_CATEGORY );
117 }
118
119 if ( $cat && $cat->getNamespace() !== NS_CATEGORY ) {
120 // Someone searching for something like "Wikipedia:Foo"
121 $cat = Title::makeTitleSafe( NS_CATEGORY, $categoryStr );
122 }
123
124 if ( $cat ) {
125 $this->setCategory( $cat );
126 }
127
128 if ( !$this->category && $categoryStr ) {
129 $msg = $this->msg( 'randomincategory-invalidcategory',
130 wfEscapeWikiText( $categoryStr ) );
131
132 return Status::newFatal( $msg );
133
134 } elseif ( !$this->category ) {
135 return false; // no data sent
136 }
137
138 $title = $this->getRandomTitle();
139
140 if ( is_null( $title ) ) {
141 $msg = $this->msg( 'randomincategory-nopages',
142 $this->category->getText() );
143
144 return Status::newFatal( $msg );
145 }
146
147 $this->getOutput()->redirect( $title->getFullURL() );
148 }
149
154 public function getRandomTitle() {
155 // Convert to float, since we do math with the random number.
156 $rand = (float)wfRandom();
157 $title = null;
158
159 // Given that timestamps are rather unevenly distributed, we also
160 // use an offset between 0 and 30 to make any biases less noticeable.
161 $offset = mt_rand( 0, $this->maxOffset );
162
163 if ( mt_rand( 0, 1 ) ) {
164 $up = true;
165 } else {
166 $up = false;
167 }
168
169 $row = $this->selectRandomPageFromDB( $rand, $offset, $up );
170
171 // Try again without the timestamp offset (wrap around the end)
172 if ( !$row ) {
173 $row = $this->selectRandomPageFromDB( false, $offset, $up );
174 }
175
176 // Maybe the category is really small and offset too high
177 if ( !$row ) {
178 $row = $this->selectRandomPageFromDB( $rand, 0, $up );
179 }
180
181 // Just get the first entry.
182 if ( !$row ) {
183 $row = $this->selectRandomPageFromDB( false, 0, true );
184 }
185
186 if ( $row ) {
187 return Title::makeTitle( $row->page_namespace, $row->page_title );
188 }
189
190 return null;
191 }
192
204 protected function getQueryInfo( $rand, $offset, $up ) {
205 $op = $up ? '>=' : '<=';
206 $dir = $up ? 'ASC' : 'DESC';
207 if ( !$this->category instanceof Title ) {
208 throw new MWException( 'No category set' );
209 }
210 $qi = [
211 'tables' => [ 'categorylinks', 'page' ],
212 'fields' => [ 'page_title', 'page_namespace' ],
213 'conds' => array_merge( [
214 'cl_to' => $this->category->getDBkey(),
215 ], $this->extra ),
216 'options' => [
217 'ORDER BY' => 'cl_timestamp ' . $dir,
218 'LIMIT' => 1,
219 'OFFSET' => $offset
220 ],
221 'join_conds' => [
222 'page' => [ 'INNER JOIN', 'cl_from = page_id' ]
223 ]
224 ];
225
227 $minClTime = $this->getTimestampOffset( $rand );
228 if ( $minClTime ) {
229 $qi['conds'][] = 'cl_timestamp ' . $op . ' ' .
230 $dbr->addQuotes( $dbr->timestamp( $minClTime ) );
231 }
232
233 return $qi;
234 }
235
241 protected function getTimestampOffset( $rand ) {
242 if ( $rand === false ) {
243 return false;
244 }
245 if ( !$this->minTimestamp || !$this->maxTimestamp ) {
246 try {
247 list( $this->minTimestamp, $this->maxTimestamp ) = $this->getMinAndMaxForCat( $this->category );
248 } catch ( Exception $e ) {
249 // Possibly no entries in category.
250 return false;
251 }
252 }
253
254 $ts = ( $this->maxTimestamp - $this->minTimestamp ) * $rand + $this->minTimestamp;
255
256 return intval( $ts );
257 }
258
266 protected function getMinAndMaxForCat( Title $category ) {
268 $res = $dbr->selectRow(
269 'categorylinks',
270 [
271 'low' => 'MIN( cl_timestamp )',
272 'high' => 'MAX( cl_timestamp )'
273 ],
274 [
275 'cl_to' => $this->category->getDBkey(),
276 ],
277 __METHOD__,
278 [
279 'LIMIT' => 1
280 ]
281 );
282 if ( !$res ) {
283 throw new MWException( 'No entries in category' );
284 }
285
286 return [ wfTimestamp( TS_UNIX, $res->low ), wfTimestamp( TS_UNIX, $res->high ) ];
287 }
288
296 private function selectRandomPageFromDB( $rand, $offset, $up, $fname = __METHOD__ ) {
298
299 $query = $this->getQueryInfo( $rand, $offset, $up );
300 $res = $dbr->select(
301 $query['tables'],
302 $query['fields'],
303 $query['conds'],
304 $fname,
305 $query['options'],
306 $query['join_conds']
307 );
308
309 return $res->fetchObject();
310 }
311
312 protected function getGroupName() {
313 return 'redirects';
314 }
315}
wfRandom()
Get a random decimal value between 0 and 1, in a way not likely to give duplicate values for any real...
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings,...
Definition Setup.php:112
Special page which uses an HTMLForm to handle processing.
string $par
The sub-page of the special page.
Object handling generic submission, CSRF protection, layout and other logic for UI forms.
Definition HTMLForm.php:130
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
MediaWiki exception.
getOutput()
Get the OutputPage being used for this instance.
msg( $key)
Wrapper around wfMessage that sets the current context.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Special page to direct the user to a random page.
setParameter( $par)
Maybe do something interesting with the subpage parameter.
requiresWrite()
Whether this action requires the wiki not to be locked.
__construct( $name='RandomInCategory')
getDisplayFormat()
Get display format for the form.
requiresUnblock()
Whether this action cannot be executed by a blocked user.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
getRandomTitle()
Choose a random title.
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
onSubmit(array $data)
Process the form on POST submission.
getMinAndMaxForCat(Title $category)
Get the lowest and highest timestamp for a category.
setCategory(Title $cat)
Set which category to use.
getQueryInfo( $rand, $offset, $up)
selectRandomPageFromDB( $rand, $offset, $up, $fname=__METHOD__)
getFormFields()
Get an HTMLForm descriptor array.
Represents a title within MediaWiki.
Definition Title.php:39
$res
Definition database.txt:21
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition deferred.txt:11
const NS_CATEGORY
Definition Defines.php:88
the array() calling protocol came about after MediaWiki 1.4rc1.
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:964
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition hooks.txt:1620
returning false will NOT prevent logging $e
Definition hooks.txt:2176
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
const DB_REPLICA
Definition defines.php:25