MediaWiki  master
ApiMoveTest.php
Go to the documentation of this file.
1 <?php
2 
4 
12 class ApiMoveTest extends ApiTestCase {
19  protected function assertMoved( $from, $to, $id, $opts = null ) {
20  $opts = (array)$opts;
21 
23  $fromTitle = Title::newFromText( $from );
24  $toTitle = Title::newFromText( $to );
25 
26  $this->assertTrue( $toTitle->exists(),
27  "Destination {$toTitle->getPrefixedText()} does not exist" );
28 
29  if ( in_array( 'noredirect', $opts ) ) {
30  $this->assertFalse( $fromTitle->exists(),
31  "Source {$fromTitle->getPrefixedText()} exists" );
32  } else {
33  $this->assertTrue( $fromTitle->exists(),
34  "Source {$fromTitle->getPrefixedText()} does not exist" );
35  $this->assertTrue( $fromTitle->isRedirect(),
36  "Source {$fromTitle->getPrefixedText()} is not a redirect" );
37 
38  $target = Revision::newFromTitle( $fromTitle )->getContent()->getRedirectTarget();
39  $this->assertSame( $toTitle->getPrefixedText(), $target->getPrefixedText() );
40  }
41 
42  $this->assertSame( $id, $toTitle->getArticleID() );
43  }
44 
51  protected function createPage( $name ) {
52  return $this->editPage( $name, 'Content' )->value['revision']->getPage();
53  }
54 
55  public function testFromWithFromid() {
56  $this->setExpectedException( ApiUsageException::class,
57  'The parameters "from" and "fromid" can not be used together.' );
58 
59  $this->doApiRequestWithToken( [
60  'action' => 'move',
61  'from' => 'Some page',
62  'fromid' => 123,
63  'to' => 'Some other page',
64  ] );
65  }
66 
67  public function testMove() {
68  $name = ucfirst( __FUNCTION__ );
69 
70  $id = $this->createPage( $name );
71 
72  $res = $this->doApiRequestWithToken( [
73  'action' => 'move',
74  'from' => $name,
75  'to' => "$name 2",
76  ] );
77 
78  $this->assertMoved( $name, "$name 2", $id );
79  $this->assertArrayNotHasKey( 'warnings', $res[0] );
80  }
81 
82  public function testMoveById() {
83  $name = ucfirst( __FUNCTION__ );
84 
85  $id = $this->createPage( $name );
86 
87  $res = $this->doApiRequestWithToken( [
88  'action' => 'move',
89  'fromid' => $id,
90  'to' => "$name 2",
91  ] );
92 
93  $this->assertMoved( $name, "$name 2", $id );
94  $this->assertArrayNotHasKey( 'warnings', $res[0] );
95  }
96 
97  public function testMoveNonexistent() {
98  $this->setExpectedException( ApiUsageException::class,
99  "The page you specified doesn't exist." );
100 
101  $this->doApiRequestWithToken( [
102  'action' => 'move',
103  'from' => 'Nonexistent page',
104  'to' => 'Different page'
105  ] );
106  }
107 
108  public function testMoveNonexistentId() {
109  $this->setExpectedException( ApiUsageException::class,
110  'There is no page with ID 2147483647.' );
111 
112  $this->doApiRequestWithToken( [
113  'action' => 'move',
114  'fromid' => pow( 2, 31 ) - 1,
115  'to' => 'Different page',
116  ] );
117  }
118 
119  public function testMoveToInvalidPageName() {
120  $this->setExpectedException( ApiUsageException::class, 'Bad title "[".' );
121 
122  $name = ucfirst( __FUNCTION__ );
123  $id = $this->createPage( $name );
124 
125  try {
126  $this->doApiRequestWithToken( [
127  'action' => 'move',
128  'from' => $name,
129  'to' => '[',
130  ] );
131  } finally {
132  $this->assertSame( $id, Title::newFromText( $name )->getArticleID() );
133  }
134  }
135 
136  public function testMoveWhileBlocked() {
137  $this->assertNull( DatabaseBlock::newFromTarget( '127.0.0.1' ), 'Sanity check' );
138 
139  $block = new DatabaseBlock( [
140  'address' => self::$users['sysop']->getUser()->getName(),
141  'by' => self::$users['sysop']->getUser()->getId(),
142  'reason' => 'Capriciousness',
143  'timestamp' => '19370101000000',
144  'expiry' => 'infinity',
145  'enableAutoblock' => true,
146  ] );
147  $block->insert();
148 
149  $name = ucfirst( __FUNCTION__ );
150  $id = $this->createPage( $name );
151 
152  try {
153  $this->doApiRequestWithToken( [
154  'action' => 'move',
155  'from' => $name,
156  'to' => "$name 2",
157  ] );
158  $this->fail( 'Expected exception not thrown' );
159  } catch ( ApiUsageException $ex ) {
160  $this->assertSame( 'You have been blocked from editing.', $ex->getMessage() );
161  $this->assertNotNull( DatabaseBlock::newFromTarget( '127.0.0.1' ), 'Autoblock spread' );
162  } finally {
163  $block->delete();
164  self::$users['sysop']->getUser()->clearInstanceCache();
165  $this->assertSame( $id, Title::newFromText( $name )->getArticleID() );
166  }
167  }
168 
169  // @todo File moving
170 
171  public function testPingLimiter() {
172  $this->setExpectedException( ApiUsageException::class,
173  "You've exceeded your rate limit. Please wait some time and try again." );
174 
175  $name = ucfirst( __FUNCTION__ );
176 
177  $this->setMwGlobals( 'wgMainCacheType', 'hash' );
178 
179  $this->mergeMwGlobalArrayValue( 'wgRateLimits',
180  [ 'move' => [ '&can-bypass' => false, 'user' => [ 1, 60 ] ] ] );
181 
182  $id = $this->createPage( $name );
183 
184  $res = $this->doApiRequestWithToken( [
185  'action' => 'move',
186  'from' => $name,
187  'to' => "$name 2",
188  ] );
189 
190  $this->assertMoved( $name, "$name 2", $id );
191  $this->assertArrayNotHasKey( 'warnings', $res[0] );
192 
193  try {
194  $this->doApiRequestWithToken( [
195  'action' => 'move',
196  'from' => "$name 2",
197  'to' => "$name 3",
198  ] );
199  } finally {
200  $this->assertSame( $id, Title::newFromText( "$name 2" )->getArticleID() );
201  $this->assertFalse( Title::newFromText( "$name 3" )->exists(),
202  "\"$name 3\" should not exist" );
203  }
204  }
205 
206  public function testTagsNoPermission() {
207  $this->setExpectedException( ApiUsageException::class,
208  'You do not have permission to apply change tags along with your changes.' );
209 
210  $name = ucfirst( __FUNCTION__ );
211 
212  ChangeTags::defineTag( 'custom tag' );
213 
214  $this->setGroupPermissions( 'user', 'applychangetags', false );
215 
216  $id = $this->createPage( $name );
217 
218  try {
219  $this->doApiRequestWithToken( [
220  'action' => 'move',
221  'from' => $name,
222  'to' => "$name 2",
223  'tags' => 'custom tag',
224  ] );
225  } finally {
226  $this->assertSame( $id, Title::newFromText( $name )->getArticleID() );
227  $this->assertFalse( Title::newFromText( "$name 2" )->exists(),
228  "\"$name 2\" should not exist" );
229  }
230  }
231 
232  public function testSelfMove() {
233  $this->setExpectedException( ApiUsageException::class,
234  'The title is the same; cannot move a page over itself.' );
235 
236  $name = ucfirst( __FUNCTION__ );
237  $this->createPage( $name );
238 
239  $this->doApiRequestWithToken( [
240  'action' => 'move',
241  'from' => $name,
242  'to' => $name,
243  ] );
244  }
245 
246  public function testMoveTalk() {
247  $name = ucfirst( __FUNCTION__ );
248 
249  $id = $this->createPage( $name );
250  $talkId = $this->createPage( "Talk:$name" );
251 
252  $res = $this->doApiRequestWithToken( [
253  'action' => 'move',
254  'from' => $name,
255  'to' => "$name 2",
256  'movetalk' => '',
257  ] );
258 
259  $this->assertMoved( $name, "$name 2", $id );
260  $this->assertMoved( "Talk:$name", "Talk:$name 2", $talkId );
261 
262  $this->assertArrayNotHasKey( 'warnings', $res[0] );
263  }
264 
265  public function testMoveTalkFailed() {
266  $name = ucfirst( __FUNCTION__ );
267 
268  $id = $this->createPage( $name );
269  $talkId = $this->createPage( "Talk:$name" );
270  $talkDestinationId = $this->createPage( "Talk:$name 2" );
271 
272  $res = $this->doApiRequestWithToken( [
273  'action' => 'move',
274  'from' => $name,
275  'to' => "$name 2",
276  'movetalk' => '',
277  ] );
278 
279  $this->assertMoved( $name, "$name 2", $id );
280  $this->assertSame( $talkId, Title::newFromText( "Talk:$name" )->getArticleID() );
281  $this->assertSame( $talkDestinationId,
282  Title::newFromText( "Talk:$name 2" )->getArticleID() );
283  $this->assertSame( [ [
284  'message' => 'articleexists',
285  'params' => [],
286  'code' => 'articleexists',
287  'type' => 'error',
288  ] ], $res[0]['move']['talkmove-errors'] );
289 
290  $this->assertArrayNotHasKey( 'warnings', $res[0] );
291  }
292 
293  public function testMoveSubpages() {
294  $name = ucfirst( __FUNCTION__ );
295 
296  $this->mergeMwGlobalArrayValue( 'wgNamespacesWithSubpages', [ NS_MAIN => true ] );
297  $this->resetServices();
298 
299  $pages = [ $name, "$name/1", "$name/2", "Talk:$name", "Talk:$name/1", "Talk:$name/3" ];
300  $ids = [];
301  foreach ( array_merge( $pages, [ "$name/error", "$name 2/error" ] ) as $page ) {
302  $ids[$page] = $this->createPage( $page );
303  }
304 
305  $res = $this->doApiRequestWithToken( [
306  'action' => 'move',
307  'from' => $name,
308  'to' => "$name 2",
309  'movetalk' => '',
310  'movesubpages' => '',
311  ] );
312 
313  foreach ( $pages as $page ) {
314  $this->assertMoved( $page, str_replace( $name, "$name 2", $page ), $ids[$page] );
315  }
316 
317  $this->assertSame( $ids["$name/error"],
318  Title::newFromText( "$name/error" )->getArticleID() );
319  $this->assertSame( $ids["$name 2/error"],
320  Title::newFromText( "$name 2/error" )->getArticleID() );
321 
322  $results = array_merge( $res[0]['move']['subpages'], $res[0]['move']['subpages-talk'] );
323  foreach ( $results as $arr ) {
324  if ( $arr['from'] === "$name/error" ) {
325  $this->assertSame( [ [
326  'message' => 'articleexists',
327  'params' => [],
328  'code' => 'articleexists',
329  'type' => 'error'
330  ] ], $arr['errors'] );
331  } else {
332  $this->assertSame( str_replace( $name, "$name 2", $arr['from'] ), $arr['to'] );
333  }
334  $this->assertCount( 2, $arr );
335  }
336 
337  $this->assertArrayNotHasKey( 'warnings', $res[0] );
338  }
339 
340  public function testMoveNoPermission() {
341  $this->setExpectedException( ApiUsageException::class,
342  'You must be a registered user and [[Special:UserLogin|logged in]] to move a page.' );
343 
344  $name = ucfirst( __FUNCTION__ );
345 
346  $id = $this->createPage( $name );
347 
348  $user = new User();
349 
350  try {
351  $this->doApiRequestWithToken( [
352  'action' => 'move',
353  'from' => $name,
354  'to' => "$name 2",
355  ], null, $user );
356  } finally {
357  $this->assertSame( $id, Title::newFromText( "$name" )->getArticleID() );
358  $this->assertFalse( Title::newFromText( "$name 2" )->exists(),
359  "\"$name 2\" should not exist" );
360  }
361  }
362 
363  public function testSuppressRedirect() {
364  $name = ucfirst( __FUNCTION__ );
365 
366  $id = $this->createPage( $name );
367 
368  $res = $this->doApiRequestWithToken( [
369  'action' => 'move',
370  'from' => $name,
371  'to' => "$name 2",
372  'noredirect' => '',
373  ] );
374 
375  $this->assertMoved( $name, "$name 2", $id, 'noredirect' );
376  $this->assertArrayNotHasKey( 'warnings', $res[0] );
377  }
378 
380  $name = ucfirst( __FUNCTION__ );
381 
382  $this->setGroupPermissions( 'sysop', 'suppressredirect', false );
383  $id = $this->createPage( $name );
384 
385  $res = $this->doApiRequestWithToken( [
386  'action' => 'move',
387  'from' => $name,
388  'to' => "$name 2",
389  'noredirect' => '',
390  ] );
391 
392  $this->assertMoved( $name, "$name 2", $id );
393  $this->assertArrayNotHasKey( 'warnings', $res[0] );
394  }
395 
396  public function testMoveSubpagesError() {
397  $name = ucfirst( __FUNCTION__ );
398 
399  // Subpages are allowed in talk but not main
400  $idBase = $this->createPage( "Talk:$name" );
401  $idSub = $this->createPage( "Talk:$name/1" );
402 
403  $res = $this->doApiRequestWithToken( [
404  'action' => 'move',
405  'from' => "Talk:$name",
406  'to' => $name,
407  'movesubpages' => '',
408  ] );
409 
410  $this->assertMoved( "Talk:$name", $name, $idBase );
411  $this->assertSame( $idSub, Title::newFromText( "Talk:$name/1" )->getArticleID() );
412  $this->assertFalse( Title::newFromText( "$name/1" )->exists(),
413  "\"$name/1\" should not exist" );
414 
415  $this->assertSame( [ 'errors' => [ [
416  'message' => 'namespace-nosubpages',
417  'params' => [ '' ],
418  'code' => 'namespace-nosubpages',
419  'type' => 'error',
420  ] ] ], $res[0]['move']['subpages'] );
421 
422  $this->assertArrayNotHasKey( 'warnings', $res[0] );
423  }
424 }
testMoveSubpagesError()
static clearCaches()
Definition: Title.php:3168
const NS_MAIN
Definition: Defines.php:60
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
testMoveNoPermission()
Exception used to abort API execution with an error.
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
testSuppressRedirectNoPermission()
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that&#39;s attached to a given link target...
Definition: Revision.php:137
doApiRequestWithToken(array $params, array $session=null, User $user=null, $tokenType='auto')
Convenience function to access the token parameter of doApiRequest() more succinctly.
testTagsNoPermission()
$res
Definition: database.txt:21
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:767
API Database medium.
Definition: ApiMoveTest.php:12
testSuppressRedirect()
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
testMoveToInvalidPageName()
assertMoved( $from, $to, $id, $opts=null)
Definition: ApiMoveTest.php:19
testFromWithFromid()
Definition: ApiMoveTest.php:55
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:35
static defineTag( $tag)
Set ctd_user_defined = 1 in change_tag_def without checking that the tag name is valid.
Definition: ChangeTags.php:921
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
createPage( $name)
Shortcut function to create a page and return its id.
Definition: ApiMoveTest.php:51
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
testMoveWhileBlocked()
testMoveNonexistent()
Definition: ApiMoveTest.php:97
testMoveNonexistentId()
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1454
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:322