summaryrefslogtreecommitdiff
path: root/bin/wiki/vendor/addwiki/mediawiki-api
diff options
context:
space:
mode:
Diffstat (limited to 'bin/wiki/vendor/addwiki/mediawiki-api')
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/.gitignore9
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/.scrutinizer.yml13
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/.travis.yml52
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/LICENSE.md264
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/README.md74
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/RELEASENOTES.md111
-rwxr-xr-xbin/wiki/vendor/addwiki/mediawiki-api/bin/install-mediawiki.sh55
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/composer.json44
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/Makefile225
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/category_traverser.rst28
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/conf.py80
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/contributing.rst44
-rwxr-xr-xbin/wiki/vendor/addwiki/mediawiki-api/docs/file_uploader.rst28
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/index.rst25
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/make.bat281
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/namespace_getter.rst54
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/page_list_getter.rst88
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/page_purger.rst34
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/phpcs.xml11
-rwxr-xr-xbin/wiki/vendor/addwiki/mediawiki-api/phpunit.xml.dist21
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/CategoryLoopException.php32
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/AnonymousGenerator.php41
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/ApiGenerator.php27
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/FluentGenerator.php68
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/MediawikiFactory.php240
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/CategoryTraverser.php161
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/FileUploader.php139
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/ImageRotator.php56
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/LogListGetter.php83
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/NamespaceGetter.php108
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageDeleter.php105
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageGetter.php246
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageListGetter.php147
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageMover.php68
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageProtector.php55
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PagePurger.php116
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageRestorer.php76
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageWatcher.php37
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Parser.php47
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionDeleter.php39
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionPatroller.php47
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRestorer.php39
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRollbacker.php73
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionSaver.php96
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionUndoer.php48
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Service.php23
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserBlocker.php45
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserCreator.php83
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserGetter.php63
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserRightsChanger.php59
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/blue ℳ𝒲β™₯π“Šπ“ƒπ’Ύπ’Έβ„΄π’Ήβ„―.pngbin0 -> 2807 bytes
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/namespaces.json241
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/CategoryTraverserTest.php203
-rwxr-xr-xbin/wiki/vendor/addwiki/mediawiki-api/tests/integration/FileUploaderTest.php75
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/NamespaceGetterTest.php86
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageIntegrationTest.php66
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageListGetterTest.php99
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/TestEnvironment.php116
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/UserIntegrationTest.php35
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/phpunit.xml.dist18
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/AnonymousGeneratorTest.php26
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/FluentGeneratorTest.php55
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/MediawikiFactoryTest.php54
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Service/PagePurgerTest.php187
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/phpunit.xml.dist13
65 files changed, 5282 insertions, 0 deletions
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/.gitignore b/bin/wiki/vendor/addwiki/mediawiki-api/.gitignore
new file mode 100644
index 00000000..2bd8a05e
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/.gitignore
@@ -0,0 +1,9 @@
+.idea
+vendor
+composer.lock
+test.php
+phpunit.xml
+nbproject
+docs/_build
+log
+
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/.scrutinizer.yml b/bin/wiki/vendor/addwiki/mediawiki-api/.scrutinizer.yml
new file mode 100644
index 00000000..ffc976e3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/.scrutinizer.yml
@@ -0,0 +1,13 @@
+inherit: true
+
+tools:
+ php_code_sniffer: true
+ php_cpd: true
+ php_cs_fixer: true
+ php_loc: true
+ php_mess_detector: true
+ php_pdepend: true
+ php_analyzer: true
+ sensiolabs_security_checker: true
+ external_code_coverage:
+ timeout: 300 \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/.travis.yml b/bin/wiki/vendor/addwiki/mediawiki-api/.travis.yml
new file mode 100644
index 00000000..26793151
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/.travis.yml
@@ -0,0 +1,52 @@
+language: php
+
+php:
+ - 5.5
+ - 5.6
+ - 7.0
+
+env:
+ matrix:
+ - TEST_SUITE=unit
+ # All the currently-supported versions from https://www.mediawiki.org/wiki/Version_lifecycle
+ - TEST_SUITE=integration MEDIAWIKI_VERSION=1.26.4
+ - TEST_SUITE=integration MEDIAWIKI_VERSION=1.27.3
+ - TEST_SUITE=integration MEDIAWIKI_VERSION=1.28.2
+ - TEST_SUITE=integration MEDIAWIKI_VERSION=1.29.1
+ global:
+ - MEDIAWIKI_API_URL='http://127.0.0.1:8081/api.php'
+
+matrix:
+ include:
+ - php: hhvm
+ env: TEST_SUITE=unit
+ before_install:
+ install:
+ - composer install
+ - php: 7.1
+ env: TEST_SUITE=unit
+ before_install:
+ install:
+ - composer install
+
+before_install:
+ - bin/install-mediawiki.sh
+
+install:
+ - php -S 127.0.0.1:8081 -t build/mediawiki >/dev/null 2>&1 &
+ - composer install
+
+script:
+ - $TRAVIS_BUILD_DIR/vendor/bin/phpunit --coverage-clover=$TRAVIS_BUILD_DIR/coverage.clover $TRAVIS_BUILD_DIR/tests/$TEST_SUITE
+ - $TRAVIS_BUILD_DIR/vendor/bin/phpcs
+
+after_script:
+ - wget https://scrutinizer-ci.com/ocular.phar
+ - php ocular.phar code-coverage:upload --format=php-clover $TRAVIS_BUILD_DIR/coverage.clover
+
+notifications:
+ irc:
+ channels:
+ - "chat.freenode.net##add"
+ on_success: change
+ on_failure: always
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/LICENSE.md b/bin/wiki/vendor/addwiki/mediawiki-api/LICENSE.md
new file mode 100644
index 00000000..0671f06a
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/LICENSE.md
@@ -0,0 +1,264 @@
+The GNU General Public License, Version 2, June 1991 (GPLv2)
+============================================================
+
+> Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+> 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+
+Preamble
+--------
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most
+of the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you can
+do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show them
+these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually
+obtain patent licenses, in effect making the program proprietary. To prevent
+this, we have made it clear that any patent must be licensed for everyone's free
+use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+
+Terms And Conditions For Copying, Distribution And Modification
+---------------------------------------------------------------
+
+**0.** This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program or
+work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or
+translated into another language. (Hereinafter, translation is included without
+limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+**1.** You may copy and distribute verbatim copies of the Program's source code
+as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the Program
+a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+**2.** You may modify your copy or copies of the Program or any portion of it,
+thus forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+* **a)** You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+* **b)** You must cause any work that you distribute or publish, that in whole
+ or in part contains or is derived from the Program or any part thereof, to
+ be licensed as a whole at no charge to all third parties under the terms of
+ this License.
+
+* **c)** If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use in the
+ most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may redistribute
+ the program under these conditions, and telling the user how to view a copy
+ of this License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on the Program is
+ not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License, and
+its terms, do not apply to those sections when you distribute them as separate
+works. But when you distribute the same sections as part of a whole which is a
+work based on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the entire whole,
+and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based on the
+Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+**3.** You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+* **a)** Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2 above on
+ a medium customarily used for software interchange; or,
+
+* **b)** Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections 1
+ and 2 above on a medium customarily used for software interchange; or,
+
+* **c)** Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only for
+ noncommercial distribution and only if you received the program in object
+ code or executable form with such an offer, in accord with Subsection b
+ above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface
+definition files, plus the scripts used to control compilation and installation
+of the executable. However, as a special exception, the source code distributed
+need not include anything that is normally distributed (in either source or
+binary form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component itself
+accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source code
+from the same place counts as distribution of the source code, even though third
+parties are not compelled to copy the source along with the object code.
+
+**4.** You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so
+long as such parties remain in full compliance.
+
+**5.** You are not required to accept this License, since you have not signed
+it. However, nothing else grants you permission to modify or distribute the
+Program or its derivative works. These actions are prohibited by law if you do
+not accept this License. Therefore, by modifying or distributing the Program (or
+any work based on the Program), you indicate your acceptance of this License to
+do so, and all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+**6.** Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these terms and
+conditions. You may not impose any further restrictions on the recipients'
+exercise of the rights granted herein. You are not responsible for enforcing
+compliance by third parties to this License.
+
+**7.** If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License. If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution of
+the Program by all those who receive copies directly or indirectly through you,
+then the only way you could satisfy both it and this License would be to refrain
+entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and the
+section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices. Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+**8.** If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original copyright
+holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded. In such
+case, this License incorporates the limitation as if written in the body of this
+License.
+
+**9.** The Free Software Foundation may publish revised and/or new versions of
+the General Public License from time to time. Such new versions will be similar
+in spirit to the present version, but may differ in detail to address new
+problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies
+a version number of this License which applies to it and "any later version",
+you have the option of following the terms and conditions either of that version
+or of any later version published by the Free Software Foundation. If the
+Program does not specify a version number of this License, you may choose any
+version ever published by the Free Software Foundation.
+
+**10.** If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of
+all derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+
+No Warranty
+-----------
+
+**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
+BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
+OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/README.md b/bin/wiki/vendor/addwiki/mediawiki-api/README.md
new file mode 100644
index 00000000..325958fc
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/README.md
@@ -0,0 +1,74 @@
+mediawiki-api
+==================
+[![Build Status](https://travis-ci.org/addwiki/mediawiki-api.png?branch=master)](https://travis-ci.org/addwiki/mediawiki-api)
+[![Code Coverage](https://scrutinizer-ci.com/g/addwiki/mediawiki-api/badges/coverage.png?s=5bce1c1f0939d278ac715c7846b679a61401b1de)](https://scrutinizer-ci.com/g/addwiki/mediawiki-api/)
+[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/addwiki/mediawiki-api/badges/quality-score.png?s=4182ebaf18fb0b22af9bc3e7941fd4e3524c932e)](https://scrutinizer-ci.com/g/addwiki/mediawiki-api/)
+[![Dependency Status](https://www.versioneye.com/user/projects/54b92f798d55087422000030/badge.svg?style=flat)](https://www.versioneye.com/user/projects/54b92f798d55087422000030)
+
+On Packagist:
+[![Latest Stable Version](https://poser.pugx.org/addwiki/mediawiki-api/version.png)](https://packagist.org/packages/addwiki/mediawiki-api)
+[![Download count](https://poser.pugx.org/addwiki/mediawiki-api/d/total.png)](https://packagist.org/packages/addwiki/mediawiki-api)
+
+Issue tracker: https://phabricator.wikimedia.org/project/profile/1490/
+
+## Installation
+
+Use composer to install the library and all its dependencies:
+
+ composer require "addwiki/mediawiki-api:~0.7.0"
+
+## Example Usage
+
+```php
+// Load all the stuff
+require_once( __DIR__ . '/vendor/autoload.php' );
+
+// Log in to a wiki
+$api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+$api->login( new \Mediawiki\Api\ApiUser( 'username', 'password' ) );
+$services = new \Mediawiki\Api\MediawikiFactory( $api );
+
+// Get a page
+$page = $services->newPageGetter()->getFromTitle( 'Foo' );
+
+// Edit a page
+$content = new \Mediawiki\DataModel\Content( 'New Text' );
+$revision = new \Mediawiki\DataModel\Revision( $content, $page->getPageIdentifier() );
+$services->newRevisionSaver()->save( $revision );
+
+// Move a page
+$services->newPageMover()->move(
+ $services->newPageGetter()->getFromTitle( 'FooBar' ),
+ new Title( 'FooBar' )
+);
+
+// Delete a page
+$services->newPageDeleter()->delete(
+ $services->newPageGetter()->getFromTitle( 'DeleteMe!' ),
+ array( 'reason' => 'Reason for Deletion' )
+);
+
+// Create a new page
+$newContent = new \Mediawiki\DataModel\Content( 'Hello World' );
+$title = new \Mediawiki\DataModel\Title( 'New Page' );
+$identifier = new \Mediawiki\DataModel\PageIdentifier( $title );
+$revision = new \Mediawiki\DataModel\Revision( $newContent, $identifier );
+$services->newRevisionSaver()->save( $revision );
+
+// List all pages in a category
+$pages = $services->newPageListGetter()->getPageListFromCategoryName( 'Category:Cat name' );
+```
+
+## Running the integration tests
+
+To run the integration tests, you need to have a running MediaWiki instance. The tests will create pages and categories without using a user account so it's best if you use a test instance. Furthermore you need to turn off rate limiting by adding the line
+
+ $wgGroupPermissions['*']['noratelimit'] = true;
+
+to the `LocalSettings.php` of your MediaWiki.
+
+By default, the tests will use the URL `http://localhost/w/api.php` as the API endpoint. If you have a different URL (e.g. `http://localhost:8080/w/api.php`), you need to configure the URL as an environemnt variable before running the tests. Example:
+
+ export MEDIAWIKI_API_URL='http://localhost:8080/w/api.php'
+
+**Warning:** Running the integration tests can take a long time to complete.
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/RELEASENOTES.md b/bin/wiki/vendor/addwiki/mediawiki-api/RELEASENOTES.md
new file mode 100644
index 00000000..8d8bb9a1
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/RELEASENOTES.md
@@ -0,0 +1,111 @@
+Release Notes
+=============
+
+These are the release notes for [mediawiki-api](http://addwiki.readthedocs.io/projects/mediawiki-api/).
+
+## Version 0.7.2 (20th November 2017)
+
+* New parent class for all API service classes,
+ with protected access on `Service::$api` to make it easier to subclass any services.
+* File uploading improved, with the option of [chunked uploading](https://www.mediawiki.org/wiki/API:Upload#Chunked_uploading).
+* Various fixes and improvements to the testing set-up and coding standards.
+
+## Version 0.7.1 (8th March 2017)
+
+* Fixed dependancy on addwiki/mediawiki-datamodel
+
+## Version 0.7 (March 2017)
+
+* Documentation! This package now has a
+ [dedicated documentation website](https://addwiki.readthedocs.io/projects/mediawiki-api/).
+* A new NamespaceGetter service with which you can get all namespaces,
+ or a single namespace by localised name, alias, or canonical name
+ ([#39](https://github.com/addwiki/mediawiki-api/pull/39), [#41](https://github.com/addwiki/mediawiki-api/pull/41)).
+* A new CategoryTraverser service for descending (all levels of) category trees
+ and either retrieving all pages or performing some action on each page.
+* A new method to PagePurger for purging multiple pages at once ([#36](https://github.com/addwiki/mediawiki-api/pull/36)).
+* All methods of the PageListGetter now continue their queries where the first request doesn't retrieve the whole result set
+ ([#31](https://github.com/addwiki/mediawiki-api/pull/31)).
+* Bug [#40](https://github.com/addwiki/mediawiki-api/pull/40) fixed with `RevisionSaver::save()` overwriting EditInfo if null.
+* Integration tests: more documentation about how to run integration tests locally,
+ and the tests are running on Travis CI.
+* Lots of fixes to coding-standards and in-code documentation.
+
+## Version 0.6 (3 August 2016)
+
+* Adds newParser method to factory
+* Use the new API continuation mode
+* Fix ignored bot assertion in EditInfo
+
+## Version 0.5.1 (7 September 2015)
+
+* Adds ApiGenerator interface
+* Adds AnonymousGenerator implementation of Generator
+* Adds FluentGenerator implementation of Generator
+
+## Version 0.5 (4 September 2015)
+
+####Breaks
+
+* LogListGetter now requires mediawiki verison 1.25 or above
+* PageListGetter now requires mediawiki verison 1.25 or above
+* Removed ALL Options objects
+
+####Additions
+
+* Introduces RevisionUndoer service
+* Introduces UserCreator service
+* Introduces FileUploader service
+* Introduces ImageRotator service
+
+####Libs
+
+* Using mediawiki-api-base 1.0
+* Using mediawiki-datamodel 0.6
+
+## Version 0.4 (13 January 2015)
+
+* Issue#8 PageListGetter methods now construct pages with a Title object rather than string
+* Page(Deleter|ListGetter|Mover|Protector|Restorer), User(Blocker|RightsChanger) service methods now require an *Options object rather than a selection of parameters.
+* Implemented PageListGetter::getRandom
+
+## Version 0.3 (2014-06-24)
+
+* Removes NewEditInfo and NewRevision
+* Moved basic api functionality to a separate base lib (mediawiki-api-base)
+* Repos renamed to Getters
+* PageGetter, RevisionSaver and UserGetter moved to the Service Namespace
+* Introduces MediawikiFactory
+* Introduces PageDeleter service
+* Introduces PageListGetter service
+* Introduces PageProtector service
+* Introduces PagePurger service
+* Introduces RevisionDeleter service
+* Introduces RevisionPatroller service
+* Introduces RevisionRollbacker service
+* Introduces UserBlocker service
+* Introduces UserRightsChanger service
+* Introduces PageRestorer service
+* Introduces RevisionRestorer service
+* Correctly handle non existent users in UserGetter
+
+
+## Version 0.2 (2014-02-23)
+
+* Altered everything for changed in mediawiki-datamodel
+* Removed Edit << action class
+* Introduces NewEditInfo and NewRevision
+
+
+## Version 0.1 (2014-02-23)
+
+Initial release with the following features:
+
+* MediawikiApi
+* ApiUser
+* MediawikiSession
+* UsageExceptions
+* PageRepo
+* UserRepo
+* EditSaver
+* Edit << action
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/bin/install-mediawiki.sh b/bin/wiki/vendor/addwiki/mediawiki-api/bin/install-mediawiki.sh
new file mode 100755
index 00000000..030830ad
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/bin/install-mediawiki.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+##
+## This script installs MediaWiki to ./build/mediawiki (relative to the directory from which it's called).
+##
+
+## Check inputs.
+if [ -z $MEDIAWIKI_VERSION ]; then
+ echo "You must specify the MEDIAWIKI_VERSION environment variable"
+ exit 0
+fi
+
+## Set some paths.
+BUILDDIR=$(cd $(dirname "$0"); pwd -P)"/../build"
+if [ ! -d $BUILDDIR ]; then
+ mkdir "$BUILDDIR"
+fi
+INSTALLDIR="$BUILDDIR/mediawiki"
+if [ -d "$INSTALLDIR" ]; then
+ rm -r "$INSTALLDIR"
+fi
+echo "Installing MediaWiki $MEDIAWIKI_VERSION to $INSTALLDIR"
+
+## Get the required version, and unpack it to `./build/mediawiki`.
+if [ ! -s "$BUILDDIR/$MEDIAWIKI_VERSION.tar.gz" ]; then
+ wget --directory-prefix="$BUILDDIR" "https://github.com/wikimedia/mediawiki/archive/$MEDIAWIKI_VERSION.tar.gz"
+fi
+cd "$BUILDDIR"
+echo "Unpacking"
+tar -zxf "$MEDIAWIKI_VERSION.tar.gz"
+mv "mediawiki-$MEDIAWIKI_VERSION" $INSTALLDIR
+
+## Install MediaWiki.
+cd "$INSTALLDIR"
+WIKIDB="test_wiki1"
+echo "Creating database as MySQL root user"
+PASSARG=""
+if [ -n "$DBPASS" ]; then
+ PASSARG="-p$DBPASS"
+fi
+mysql "$PASSARG" -uroot -e "DROP DATABASE IF EXISTS $WIKIDB"
+mysql "$PASSARG" -uroot -e "CREATE DATABASE $WIKIDB"
+echo "Updating dependencies (Composer)"
+composer install
+echo "Installing TestWiki1 wiki"
+php maintenance/install.php --dbtype mysql --dbuser "root" --dbpass "$DBPASS" --dbname $WIKIDB --scriptpath "" --pass admin123 TestWiki1 admin
+
+# Add some extra configuration to LocalSettings.php
+cat << 'EOF' >> "$INSTALLDIR/LocalSettings.php"
+$wgEnableUploads = true;
+$wgShowExceptionDetails = true;
+$wgCacheDirectory = __DIR__."/images/tmp";
+$wgServer = "http://127.0.0.1:8081";
+$wgUsePathInfo = false;
+$wgJobRunRate = 200;
+EOF
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/composer.json b/bin/wiki/vendor/addwiki/mediawiki-api/composer.json
new file mode 100644
index 00000000..f49a19ca
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/composer.json
@@ -0,0 +1,44 @@
+{
+ "name": "addwiki/mediawiki-api",
+ "type": "library",
+ "description": "A MediaWiki API library",
+ "keywords": ["Mediawiki"],
+ "license": "GPL-2.0+",
+ "authors": [
+ {
+ "name": "Addshore"
+ }
+ ],
+ "require": {
+ "addwiki/mediawiki-api-base": "~2.4",
+ "addwiki/mediawiki-datamodel": "~0.7.0"
+ },
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "^0.9.2",
+ "mediawiki/mediawiki-codesniffer": "^13.0",
+ "phpunit/phpunit": "~4.8",
+ "monolog/monolog": "^1.23"
+ },
+ "autoload": {
+ "psr-4": {
+ "Mediawiki\\Api\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Mediawiki\\Api\\Test\\": ["tests/integration", "tests/unit"]
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.7.x-dev"
+ }
+ },
+ "scripts": {
+ "fix": "phpcbf",
+ "test": [
+ "parallel-lint . --exclude vendor",
+ "phpcs -ps"
+ ]
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/Makefile b/bin/wiki/vendor/addwiki/mediawiki-api/docs/Makefile
new file mode 100644
index 00000000..46208266
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/Makefile
@@ -0,0 +1,225 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " epub3 to make an epub3"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+ @echo " dummy to check syntax errors of document sources"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/mediawiki-api.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/mediawiki-api.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/mediawiki-api"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mediawiki-api"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: epub3
+epub3:
+ $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
+ @echo
+ @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+
+.PHONY: dummy
+dummy:
+ $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
+ @echo
+ @echo "Build finished. Dummy builder generates no files."
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/category_traverser.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/category_traverser.rst
new file mode 100644
index 00000000..497398e8
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/category_traverser.rst
@@ -0,0 +1,28 @@
+Category Traversal
+==================
+
+The CategoryTraverser class is used to start at one Category page in a wiki's category hierarchy
+and descend through that category's children, grandchildren, and so on.
+The basic output of this is a Pages object containing all the pages in the category tree.
+It is also possible to register callbacks that will be called
+for every subcategory or other page (i.e. anything not a category).
+
+Basic usage
+-----------
+
+To get all pages in a category or any of its subcategories.
+
+.. code-block:: php
+ :linenos:
+
+ // Construct the API.
+ $api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+ $services = new \Mediawiki\Api\MediawikiFactory( $api );
+ $categoryTraverser = $services->newCategoryTraverser();
+
+ // Get the root category.
+ $rootCatIdent = new PageIdentifier( new Title( 'Category:Categories' ) );
+ $rootCat = $this->factory->newPageGetter()->getFromPageIdentifier( $pageIdentifier );
+
+ // Get all pages.
+ $allPages = $categoryTraverser->descend( $rootCat );
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/conf.py b/bin/wiki/vendor/addwiki/mediawiki-api/docs/conf.py
new file mode 100644
index 00000000..3310f585
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/conf.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+import sys, os
+from sphinx.highlighting import lexers
+from pygments.lexers.web import PhpLexer
+
+lexers['php'] = PhpLexer(startinline=True, linenos=1)
+lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1)
+primary_domain = 'php'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'mediawiki-api'
+copyright = '2016, addwiki'
+author = 'addwiki'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.6'
+# The full version, including alpha/beta/rc tags.
+release = '0.6'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'default'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'mediawiki-apidoc'
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/contributing.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/contributing.rst
new file mode 100644
index 00000000..c2cd2813
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/contributing.rst
@@ -0,0 +1,44 @@
+Contributing
+============
+
+We welcome all contributions, be they code, documentation, or even just ideas about how to make this package better!
+
+The best way to get started is to browse the `#addwiki board on Phabricator`_
+and either work on one of the tasks already there or create a new one with details of what you want to work on.
+
+.. _Addwiki board on Phabricator: https://phabricator.wikimedia.org/tag/addwiki/
+
+Get the code
+------------
+
+The code is `hosted on GitHub`_. Clone the repository with::
+
+ $ git clone https://github.com/addwiki/mediawiki-api.git
+
+.. _hosted on GitHub: https://github.com/addwiki/mediawiki-api
+
+Run the tests
+-------------
+
+After cloning the repository and updating the dependencies with Composer,
+you should be able to run all **unit** tests with::
+
+ ./vendor/bin/phpunit ./tests/unit
+
+To run the **integration** tests you need to set up a local MediaWiki installation
+(including with a ``admin`` administrator user with password ``admin123``)
+and tell ``phpunit`` where to find it.
+
+1. Copy ``./phpunit.xml.dist`` to ``./phpunit.xml`` and add the following section::
+
+ <php>
+ <env name="MEDIAWIKI_API_URL" value="http://localhost/path/to/your/wiki/api.php" />
+ </php>
+
+2. Create and promote a new user::
+
+ $ php mediawiki/maintenance/createAndPromote.php --sysop WikiSysop wiki123sysop
+
+Now all integration tests can be run with::
+
+ ./vendor/bin/phpunit ./tests/integration
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/file_uploader.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/file_uploader.rst
new file mode 100755
index 00000000..9f8b534f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/file_uploader.rst
@@ -0,0 +1,28 @@
+Uploading files
+===============
+
+Basic usage
+-----------
+
+To upload a single, small-sized file:
+
+.. code-block:: php
+ :linenos:
+
+ // Construct the API.
+ $api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+ $services = new \Mediawiki\Api\MediawikiFactory( $api );
+ $fileUploader = $services->newFileUploader();
+
+ // Upload the file.
+ $fileUploader->upload( 'The_file.png', '/full/path/to/the_file.png' );
+
+If you need to work with larger files, you can switch to chunked uploading:
+
+.. code-block:: php
+ :linenos:
+
+ // Upload the file in 10 MB chunks.
+ $fileUploader = $services->newFileUploader();
+ $fileUploader->setChunkSize( 1024 * 1024 * 10 );
+ $fileUploader->upload( 'The_file.png', '/full/path/to/the_file.png' );
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/index.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/index.rst
new file mode 100644
index 00000000..558107b3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/index.rst
@@ -0,0 +1,25 @@
+Documentation for addwiki/mediawiki-api
+=======================================
+
+Welcome to the documentation for the ``addwiki/mediawiki-api`` package!
+This is part of the Addwiki_ family of PHP packages.
+
+.. _Addwiki: http://addwiki.readthedocs.io/
+
+Quick links:
+
+* This documentation: http://addwiki.readthedocs.io/projects/mediawiki-api/
+* Source code: https://github.com/addwiki/mediawiki-api/
+* Issue tracker: https://phabricator.wikimedia.org/project/profile/1490/
+
+Contents
+--------
+
+.. toctree::
+ :maxdepth: 2
+
+ page_list_getter.rst
+ category_traverser.rst
+ namespace_getter.rst
+ file_uploader.rst
+ contributing.rst
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/make.bat b/bin/wiki/vendor/addwiki/mediawiki-api/docs/make.bat
new file mode 100644
index 00000000..a5507331
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/make.bat
@@ -0,0 +1,281 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. epub3 to make an epub3
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ echo. coverage to run coverage check of the documentation if enabled
+ echo. dummy to check syntax errors of document sources
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+REM Check if sphinx-build is available and fallback to Python version if any
+%SPHINXBUILD% 1>NUL 2>NUL
+if errorlevel 9009 goto sphinx_python
+goto sphinx_ok
+
+:sphinx_python
+
+set SPHINXBUILD=python -m sphinx.__init__
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+:sphinx_ok
+
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\mediawiki-api.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\mediawiki-api.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "epub3" (
+ %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "coverage" (
+ %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of coverage in the sources finished, look at the ^
+results in %BUILDDIR%/coverage/python.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+if "%1" == "dummy" (
+ %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. Dummy builder generates no files.
+ goto end
+)
+
+:end
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/namespace_getter.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/namespace_getter.rst
new file mode 100644
index 00000000..c362833f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/namespace_getter.rst
@@ -0,0 +1,54 @@
+Getting Namespaces
+==================
+
+The Name Space Getter allows you to search for namespaces and their aliases and to list all namespaces of a wiki.
+
+To use it, first get a new NamespaceGetter object from the factory:
+
+.. code-block:: php
+
+ $api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+ $services = new \Mediawiki\Api\MediawikiFactory( $api );
+ $namespaceGetter = $services->newNamespaceGetter();
+
+
+Looking for a namespace
+-----------------------
+
+If you've got a page name like ``File:awesome_cats.jpg`` and want to know its namespace ID and possible localized names
+and aliases, use the following code:
+
+.. code-block:: php
+
+ $fileNamespace = $namespaceGetter->getNamespaceByName( 'File' );
+ printf( "Name in local language: %s\n", $fileNamespace->getLocalName() );
+ printf( "Possible aliases: %s\n", implode( ', ', $fileNamespace->getAliases() ) );
+ // ... etc
+
+``getNamespaceByName`` accepts the canonical name, the local name and aliases. If you want to match only the canonical
+name, use ``getNamespaceByCanonicalName`` instead.
+
+
+Getting a namespaced page
+-------------------------
+
+If you have a page title that is not in the default namespace, you can't pass the page name string ``PageGetter`` but
+must construct a ``Title`` object instead:
+
+.. code-block:: php
+
+ $pageName = 'User:MalReynolds';
+ $nameParts = explode( ':', $pageName, 2 );
+ $namespace = $namespaceGetter->getNamespaceByName( $nameParts[0] );
+ $title = new \Mediawiki\DataModel\Title( $nameParts[1], $namespace->getId() );
+ $page = $services->newPageGetter()->getFromTitle( $title );
+
+
+Listing all namespaces
+----------------------
+
+.. code-block:: php
+
+ foreach( $namespaceGetter->getNamespaces() as $namespace ) {
+ echo $namespace->getLocalName() . "\n";
+ } \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_list_getter.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_list_getter.rst
new file mode 100644
index 00000000..aad9f8a8
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_list_getter.rst
@@ -0,0 +1,88 @@
+Page Lists
+==========
+
+The Page List Getter allows you to retrieve lists of pages based on various criteria.
+It takes care of continuing queries where they span multiple requests,
+ensuring that you get all pages in your result set.
+This means that for some lists of pages a great many requests will be sent,
+and you should account for this possible performance problem when you request these lists
+(e.g. by running these as a background process and caching the results).
+
+To use it, first get a new PageListGetter object from the factory:
+
+.. code-block:: php
+
+ $api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+ $services = new \Mediawiki\Api\MediawikiFactory( $api );
+ $pageListGetter = $services->newPageListGetter();
+
+The examples below all use this ``$pageListGetter`` object.
+
+All methods of the PageListGetter return ``Page`` objects;
+this class is part of the `addwiki/mediawiki-datamodel`_ package,
+and is documented in `that page's documentation`_.
+
+.. _addwiki/mediawiki-datamodel: https://packagist.org/packages/addwiki/mediawiki-datamodel
+.. _that page's documentation: http://addwiki.readthedocs.io/projects/mediawiki-datamodel/
+
+All pages in a category
+-----------------------
+
+Note that the category name as provided should also include the 'Category' namespace prefix
+(in the language of the wiki, or in canonical English form).
+
+.. code-block:: php
+
+ $examplePages = $pageListGetter->getPageListFromCategoryName( 'Category:Example pages' );
+ foreach ( $examplePages->asArray() as $exPage ) {
+ echo $exPage->getTitle()->getText();
+ }
+
+Pages that transclude a template
+--------------------------------
+
+Although generally it is templates that are transcluded,
+any page may be and so any page title can be passed to this method.
+
+.. code-block:: php
+
+ $usingTestTemplate = $pageListGetter->getPageListFromPageTransclusions( 'Template:Test' );
+
+Pages that link to a given page
+-------------------------------
+
+Get the list of pages that link to a particular page.
+
+.. code-block:: php
+
+ $backLinks = $pageListGetter->getFromWhatLinksHere( 'Test page' );
+
+Pages with a given prefix
+-------------------------
+
+Find pages that have a particular prefix to their title.
+This can also be used to find subpages of any page.
+
+.. code-block:: php
+
+ $backLinks = $pageListGetter->getFromPrefix( 'A page/' );
+
+Random pages
+------------
+
+Get up to ten random pages at a time.
+This method takes the same arguments as the API `list=random`_ query.
+
+.. _list=random: https://www.mediawiki.org/wiki/API:Random
+
+* ``rnlimit`` How many pages to get. No more than 10 (20 for bots) allowed. Default: 1.
+* ``rnnamespace`` Pipe-separate list of namespace IDs.
+* ``rnfilterredir`` How to filter for redirects. Possible values: ``all``, ``redirects``, ``nonredirects``. Default: ``nonredirects``.
+
+.. code-block:: php
+
+ $backLinks = $pageListGetter->getRandom( [
+ 'rnlimit' => 7,
+ 'rnnamespace' => '3|5|6',
+ 'rnfilterredir' => 'all',
+ ] );
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_purger.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_purger.rst
new file mode 100644
index 00000000..84cd7523
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_purger.rst
@@ -0,0 +1,34 @@
+Page Purger
+===========
+
+``PagePurger`` allows you to purge a single ``Page`` or multiple ``Pages``.
+You can also check whether the ``Page`` or ``Pages`` have been purged successfully.
+
+To get started you need to create ``PagePurger`` object:
+
+.. code-block:: php
+
+$api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+$pagePurger = new \Mediawiki\Api\Service\PagePurger( $api );
+
+Purge
+-----
+
+Purge a single ``Page``. It will return a ``boolean`` that indicates if the purge operation was successful.
+
+Example:
+
+.. code-block:: php
+
+$page = new \Mediawiki\DataModel\Page(...);
+$pagePurger->purge( $page );
+
+PurgePages
+----------
+
+Purges every ``Page`` in the ``Pages`` object at once. It will return a new ``Pages`` object *with the purged ``Page``(s) only!*
+
+.. code-block:: php
+
+$pages = new \Mediawiki\DataModel\Pages(...);
+$pagePurger->purgePages( $pages );
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/phpcs.xml b/bin/wiki/vendor/addwiki/mediawiki-api/phpcs.xml
new file mode 100644
index 00000000..1c5b15ce
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/phpcs.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<ruleset name="MediaWiki">
+ <rule ref="vendor/mediawiki/mediawiki-codesniffer/MediaWiki">
+ <exclude name="MediaWiki.Commenting.FunctionComment.MissingParamComment" />
+ </rule>
+ <file>.</file>
+ <arg name="extensions" value="php,php5,inc"/>
+ <arg name="encoding" value="utf8"/>
+ <exclude-pattern>build/</exclude-pattern>
+ <exclude-pattern>vendor/</exclude-pattern>
+</ruleset>
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/phpunit.xml.dist b/bin/wiki/vendor/addwiki/mediawiki-api/phpunit.xml.dist
new file mode 100755
index 00000000..f674132b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/phpunit.xml.dist
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- convertWarningsToExceptions is false as real API calls can return un expected warnings -->
+<phpunit
+ bootstrap="./vendor/autoload.php"
+ colors="true"
+ convertWarningsToExceptions="false"
+>
+ <testsuites>
+ <testsuite>
+ <directory suffix="Test.php">./tests/integration</directory>
+ </testsuite>
+ <testsuite>
+ <directory suffix="Test.php">./tests/unit</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">./src</directory>
+ </whitelist>
+ </filter>
+</phpunit>
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/CategoryLoopException.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/CategoryLoopException.php
new file mode 100644
index 00000000..9612f050
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/CategoryLoopException.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use Mediawiki\DataModel\Pages;
+
+/**
+ * Class CategoryLoopException
+ * @package Mediawiki\Api
+ */
+class CategoryLoopException extends \Exception {
+
+ /** @var Pages */
+ protected $categoryPath;
+
+ /**
+ * @param Pages $path
+ */
+ public function setCategoryPath( Pages $path ) {
+ $this->categoryPath = $path;
+ }
+
+ /**
+ * Get the path of Pages that comprise the category loop. The first item in this list is also a
+ * child page of the last item.
+ * @return Pages The set of category Pages that comprise the category loop.
+ */
+ public function getCategoryPath() {
+ return $this->categoryPath;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/AnonymousGenerator.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/AnonymousGenerator.php
new file mode 100644
index 00000000..715e3c02
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/AnonymousGenerator.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Mediawiki\Api\Generator;
+
+/**
+ * @access public
+ *
+ * @author Addshore
+ *
+ * @since 0.5.1
+ */
+class AnonymousGenerator implements ApiGenerator {
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var array
+ */
+ private $params;
+
+ /**
+ * @param string $name
+ * @param array $params including 'g' prefix keys
+ */
+ public function __construct( $name, array $params ) {
+ $this->name = $name;
+ $this->params = $params;
+ }
+
+ /**
+ * @return array
+ */
+ public function getParams() {
+ $params = $this->params;
+ $params['generator'] = $this->name;
+ return $params;
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/ApiGenerator.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/ApiGenerator.php
new file mode 100644
index 00000000..923e98cd
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/ApiGenerator.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Mediawiki\Api\Generator;
+
+/**
+ * Interface relating to Mediawiki generators
+ * @see https://www.mediawiki.org/wiki/API:Query#Generators
+ *
+ * @access public
+ *
+ * @author Addshore
+ *
+ * @since 0.5.1
+ */
+interface ApiGenerator {
+
+ /**
+ * @since 0.5.1
+ *
+ * Associative array of parameters including the 'generator' parameter.
+ * All generator param keys must have their 'g' prefixes
+ *
+ * @return string[]
+ */
+ public function getParams();
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/FluentGenerator.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/FluentGenerator.php
new file mode 100644
index 00000000..108f4999
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/FluentGenerator.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Mediawiki\Api\Generator;
+
+/**
+ * @access public
+ *
+ * @author Addshore
+ *
+ * @since 0.5.1
+ */
+class FluentGenerator implements ApiGenerator {
+
+ private $name;
+ private $params;
+
+ /**
+ * @param string $name
+ */
+ public function __construct( $name ) {
+ $this->name = $name;
+ }
+
+ /**
+ * Convenience method for using this fluidly
+ *
+ * @param string $name
+ *
+ * @return FluentGenerator
+ */
+ public static function factory( $name ) {
+ return new self( $name );
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getParams() {
+ $params = $this->params;
+ $params['generator'] = $this->name;
+ return $params;
+ }
+
+ /**
+ * @param string $key optionally with the 'g' prefix
+ * @param string $value
+ *
+ * @return $this
+ */
+ public function set( $key, $value ) {
+ $key = $this->addKeyprefixIfNeeded( $key );
+ $this->params[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return string
+ */
+ private function addKeyPrefixIfNeeded( $key ) {
+ if ( strtolower( substr( $key, 0, 1 ) ) === 'g' ) {
+ return $key;
+ }
+ return 'g' . $key;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/MediawikiFactory.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/MediawikiFactory.php
new file mode 100644
index 00000000..fc773ce5
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/MediawikiFactory.php
@@ -0,0 +1,240 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use Mediawiki\Api\Service\CategoryTraverser;
+use Mediawiki\Api\Service\FileUploader;
+use Mediawiki\Api\Service\ImageRotator;
+use Mediawiki\Api\Service\LogListGetter;
+use Mediawiki\Api\Service\NamespaceGetter;
+use Mediawiki\Api\Service\PageDeleter;
+use Mediawiki\Api\Service\PageListGetter;
+use Mediawiki\Api\Service\PageMover;
+use Mediawiki\Api\Service\PageProtector;
+use Mediawiki\Api\Service\PagePurger;
+use Mediawiki\Api\Service\PageGetter;
+use Mediawiki\Api\Service\PageRestorer;
+use Mediawiki\Api\Service\PageWatcher;
+use Mediawiki\Api\Service\Parser;
+use Mediawiki\Api\Service\RevisionDeleter;
+use Mediawiki\Api\Service\RevisionPatroller;
+use Mediawiki\Api\Service\RevisionRestorer;
+use Mediawiki\Api\Service\RevisionRollbacker;
+use Mediawiki\Api\Service\RevisionSaver;
+use Mediawiki\Api\Service\RevisionUndoer;
+use Mediawiki\Api\Service\UserBlocker;
+use Mediawiki\Api\Service\UserCreator;
+use Mediawiki\Api\Service\UserGetter;
+use Mediawiki\Api\Service\UserRightsChanger;
+
+/**
+ * @access public
+ *
+ * @author Addshore
+ */
+class MediawikiFactory {
+
+ /**
+ * @var MediawikiApi
+ */
+ private $api;
+
+ /**
+ * @param MediawikiApi $api
+ */
+ public function __construct( MediawikiApi $api ) {
+ $this->api = $api;
+ }
+
+ /**
+ * Get a new CategoryTraverser object for this API.
+ * @return \Mediawiki\Api\Service\CategoryTraverser
+ */
+ public function newCategoryTraverser() {
+ return new CategoryTraverser( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return RevisionSaver
+ */
+ public function newRevisionSaver() {
+ return new RevisionSaver( $this->api );
+ }
+
+ /**
+ * @since 0.5
+ * @return RevisionUndoer
+ */
+ public function newRevisionUndoer() {
+ return new RevisionUndoer( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageGetter
+ */
+ public function newPageGetter() {
+ return new PageGetter( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return UserGetter
+ */
+ public function newUserGetter() {
+ return new UserGetter( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageDeleter
+ */
+ public function newPageDeleter() {
+ return new PageDeleter( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageMover
+ */
+ public function newPageMover() {
+ return new PageMover( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageListGetter
+ */
+ public function newPageListGetter() {
+ return new PageListGetter( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageRestorer
+ */
+ public function newPageRestorer() {
+ return new PageRestorer( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PagePurger
+ */
+ public function newPagePurger() {
+ return new PagePurger( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return RevisionRollbacker
+ */
+ public function newRevisionRollbacker() {
+ return new RevisionRollbacker( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return RevisionPatroller
+ */
+ public function newRevisionPatroller() {
+ return new RevisionPatroller( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageProtector
+ */
+ public function newPageProtector() {
+ return new PageProtector( $this->api );
+ }
+
+ /**
+ * @since 0.5
+ * @return PageWatcher
+ */
+ public function newPageWatcher() {
+ return new PageWatcher( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return RevisionDeleter
+ */
+ public function newRevisionDeleter() {
+ return new RevisionDeleter( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return RevisionRestorer
+ */
+ public function newRevisionRestorer() {
+ return new RevisionRestorer( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return UserBlocker
+ */
+ public function newUserBlocker() {
+ return new UserBlocker( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return UserRightsChanger
+ */
+ public function newUserRightsChanger() {
+ return new UserRightsChanger( $this->api );
+ }
+
+ /**
+ * @since 0.5
+ * @return UserCreator
+ */
+ public function newUserCreator() {
+ return new UserCreator( $this->api );
+ }
+
+ /**
+ * @since 0.4
+ * @return LogListGetter
+ */
+ public function newLogListGetter() {
+ return new LogListGetter( $this->api );
+ }
+
+ /**
+ * @since 0.5
+ * @return FileUploader
+ */
+ public function newFileUploader() {
+ return new FileUploader( $this->api );
+ }
+
+ /**
+ * @since 0.5
+ * @return ImageRotator
+ */
+ public function newImageRotator() {
+ return new ImageRotator( $this->api );
+ }
+
+ /**
+ * @since 0.6
+ * @return Parser
+ */
+ public function newParser() {
+ return new Parser( $this->api );
+ }
+
+ /**
+ * @since 0.7
+ * @return NamespaceGetter
+ */
+ public function newNamespaceGetter() {
+ return new NamespaceGetter( $this->api );
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/CategoryTraverser.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/CategoryTraverser.php
new file mode 100644
index 00000000..c82b5d69
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/CategoryTraverser.php
@@ -0,0 +1,161 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\CategoryLoopException;
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\Pages;
+
+/**
+ * Category traverser.
+ *
+ * Note on spelling 'descendant' (from Wiktionary):
+ * The adjective, "descending from a biological ancestor", may be spelt either
+ * with an 'a' or with an 'e' in the final syllable. However the noun descendant,
+ * "one who is the progeny of someone", may be spelt only with an 'a'.
+ */
+class CategoryTraverser extends Service {
+
+ const CALLBACK_CATEGORY = 10;
+ const CALLBACK_PAGE = 20;
+
+ /**
+ * @var string[]
+ */
+ protected $namespaces;
+
+ /**
+ * @var callable[]
+ */
+ protected $callbacks;
+
+ /**
+ * Used to remember the previously-visited categories when traversing.
+ * @var string[]
+ */
+ protected $alreadyVisited;
+
+ /**
+ * @param MediawikiApi $api The API to connect to.
+ */
+ public function __construct( MediawikiApi $api ) {
+ parent::__construct( $api );
+ $this->callbacks = [];
+ }
+
+ /**
+ * Query the remote site for the list of namespaces in use, so that later we can tell what's a
+ * category and what's not. This populates $this->namespaces, and will not re-request on
+ * repeated invocations.
+ * @return void
+ */
+ protected function retrieveNamespaces() {
+ if ( is_array( $this->namespaces ) ) {
+ return;
+ }
+ $params = [ 'meta' => 'siteinfo', 'siprop' => 'namespaces' ];
+ $namespaces = $this->api->getRequest( new SimpleRequest( 'query', $params ) );
+ if ( isset( $namespaces['query']['namespaces'] ) ) {
+ $this->namespaces = $namespaces['query']['namespaces'];
+ }
+ }
+
+ /**
+ * Register a callback that will be called for each page or category visited during the
+ * traversal.
+ * @param int $type One of the 'CALLBACK_' constants of this class.
+ * @param callable $callback A callable that takes two \Mediawiki\DataModel\Page parameters.
+ */
+ public function addCallback( $type, $callback ) {
+ if ( !isset( $this->callbacks[$type] ) ) {
+ $this->callbacks[$type] = [];
+ }
+ $this->callbacks[$type][] = $callback;
+ }
+
+ /**
+ * Visit every descendant page of $rootCategoryName (which will be a Category
+ * page, because there are no desecendants of any other pages).
+ * @param Page $rootCat The full name of the page to start at.
+ * @param Page[] $currentPath Used only when recursing into this method, to track each path
+ * through the category hierarchy in case of loops.
+ * @return Pages All descendants of the given category.
+ * @throws CategoryLoopException If a category loop is detected.
+ */
+ public function descend( Page $rootCat, $currentPath = null ) {
+ // Make sure we know the namespace IDs.
+ $this->retrieveNamespaces();
+
+ $rootCatName = $rootCat->getPageIdentifier()->getTitle()->getText();
+ if ( is_null( $currentPath ) ) {
+ $this->alreadyVisited = [];
+ $currentPath = new Pages();
+ }
+ $this->alreadyVisited[] = $rootCatName;
+ $currentPath->addPage( $rootCat );
+
+ // Start a list of child pages.
+ $descendants = new Pages();
+ do {
+ $pageListGetter = new PageListGetter( $this->api );
+ $members = $pageListGetter->getPageListFromCategoryName( $rootCatName );
+ foreach ( $members->toArray() as $member ) {
+ /** @var Title */
+ $memberTitle = $member->getPageIdentifier()->getTitle();
+
+ // See if this page is a Category page.
+ $isCat = false;
+ if ( isset( $this->namespaces[ $memberTitle->getNs() ] ) ) {
+ $ns = $this->namespaces[ $memberTitle->getNs() ];
+ $isCat = ( isset( $ns['canonical'] ) && $ns['canonical'] === 'Category' );
+ }
+ // If it's a category, descend into it.
+ if ( $isCat ) {
+ // If this member has already been visited on this branch of the traversal,
+ // throw an Exception with information about which categories form the loop.
+ if ( $currentPath->hasPage( $member ) ) {
+ $currentPath->addPage( $member );
+ $loop = new CategoryLoopException();
+ $loop->setCategoryPath( $currentPath );
+ throw $loop;
+ }
+ // Don't go any further if we've already visited this member
+ // (does not indicate a loop, however; we've already caught that above).
+ if ( in_array( $memberTitle->getText(), $this->alreadyVisited ) ) {
+ continue;
+ }
+ // Call any registered callbacked, and carry on to the next branch.
+ $this->call( self::CALLBACK_CATEGORY, [ $member, $rootCat ] );
+ $newDescendants = $this->descend( $member, $currentPath );
+ $descendants->addPages( $newDescendants );
+ // Re-set the path.
+ $currentPath = new Pages();
+ } else {
+ // If it's a page, add it to the list and carry on.
+ $descendants->addPage( $member );
+ $this->call( self::CALLBACK_PAGE, [ $member, $rootCat ] );
+ }
+ }
+ } while ( isset( $result['continue'] ) );
+ return $descendants;
+ }
+
+ /**
+ * Call all the registered callbacks of a particular type.
+ * @param int $type The callback type; should match one of the 'CALLBACK_' constants.
+ * @param mixed[] $params The parameters to pass to the callback function.
+ */
+ protected function call( $type, $params ) {
+ if ( !isset( $this->callbacks[$type] ) ) {
+ return;
+ }
+ foreach ( $this->callbacks[$type] as $callback ) {
+ if ( is_callable( $callback ) ) {
+ call_user_func_array( $callback, $params );
+ }
+ }
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/FileUploader.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/FileUploader.php
new file mode 100644
index 00000000..5ada5739
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/FileUploader.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Exception;
+use Mediawiki\Api\MultipartRequest;
+use Mediawiki\Api\SimpleRequest;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class FileUploader extends Service {
+
+ /** @var int */
+ protected $chunkSize;
+
+ /**
+ * Set the chunk size used for chunked uploading.
+ *
+ * Chunked uploading is available in MediaWiki 1.20 and above, although prior to version 1.25,
+ * SVGs could not be uploaded via chunked uploading.
+ *
+ * @link https://www.mediawiki.org/wiki/API:Upload#Chunked_uploading
+ *
+ * @param int $chunkSize In bytes.
+ */
+ public function setChunkSize( $chunkSize ) {
+ $this->chunkSize = $chunkSize;
+ }
+
+ /**
+ * Upload a file.
+ *
+ * @param string $targetName The name to give the file on the wiki (no 'File:' prefix required).
+ * @param string $location Can be local path or remote URL.
+ * @param string $text Initial page text for new files.
+ * @param string $comment Upload comment. Also used as the initial page text for new files if
+ * text parameter not provided.
+ * @param string $watchlist Unconditionally add or remove the page from your watchlist, use
+ * preferences or do not change watch. Possible values: 'watch', 'preferences', 'nochange'.
+ * @param bool $ignoreWarnings Ignore any warnings. This must be set to upload a new version of
+ * an existing image.
+ *
+ * @return bool
+ */
+ public function upload(
+ $targetName,
+ $location,
+ $text = '',
+ $comment = '',
+ $watchlist = 'preferences',
+ $ignoreWarnings = false
+ ) {
+ $params = [
+ 'filename' => $targetName,
+ 'token' => $this->api->getToken(),
+ ];
+ // Watchlist behaviour.
+ if ( in_array( $watchlist, [ 'watch', 'nochange' ] ) ) {
+ $params['watchlist'] = $watchlist;
+ }
+ // Ignore warnings?
+ if ( $ignoreWarnings ) {
+ $params['ignorewarnings'] = '1';
+ }
+ // Page text.
+ if ( !empty( $text ) ) {
+ $params['text'] = $text;
+ }
+ // Revision comment.
+ if ( !empty( $comment ) ) {
+ $params['comment'] = $comment;
+ }
+
+ if ( is_file( $location ) ) {
+ // Normal single-request upload.
+ $params['filesize'] = filesize( $location );
+ $params['file'] = fopen( $location, 'r' );
+ if ( is_int( $this->chunkSize ) && $this->chunkSize > 0 ) {
+ // Chunked upload.
+ $params = $this->uploadByChunks( $params );
+ }
+ } else {
+ // Upload from URL.
+ $params['url'] = $location;
+ }
+
+ $response = $this->api->postRequest( new SimpleRequest( 'upload', $params ) );
+ return ( $response['upload']['result'] === 'Success' );
+ }
+
+ /**
+ * Upload a file by chunks and get the parameters for the final upload call.
+ * @param mixed[] $params The request parameters.
+ * @return mixed[]
+ * @throws Exception
+ */
+ protected function uploadByChunks( $params ) {
+ // Get the file handle for looping, but don't keep it in the request parameters.
+ $fileHandle = $params['file'];
+ unset( $params['file'] );
+ // Track the chunks and offset.
+ $chunksDone = 0;
+ $params['offset'] = 0;
+ while ( true ) {
+
+ // 1. Make the request.
+ $params['chunk'] = fread( $fileHandle, $this->chunkSize );
+ $contentDisposition = 'form-data; name="chunk"; filename="' . $params['filename'] . '"';
+ $request = MultipartRequest::factory()
+ ->setParams( $params )
+ ->setAction( 'upload' )
+ ->setMultipartParams( [
+ 'chunk' => [ 'headers' => [ 'Content-Disposition' => $contentDisposition ] ],
+ ] );
+ $response = $this->api->postRequest( $request );
+
+ // 2. Deal with the response.
+ $chunksDone++;
+ $params['offset'] = ( $chunksDone * $this->chunkSize );
+ if ( !isset( $response['upload']['filekey'] ) ) {
+ // This should never happen. Even the last response still has the filekey.
+ throw new Exception( 'Unable to get filekey for chunked upload' );
+ }
+ $params['filekey'] = $response['upload']['filekey'];
+ if ( $response['upload']['result'] === 'Continue' ) {
+ // Amend parameters for next upload POST request.
+ $params['offset'] = $response['upload']['offset'];
+ } else {
+ // The final upload POST will be done in self::upload()
+ // to commit the upload out of the stash area.
+ unset( $params['chunk'], $params['offset'] );
+ return $params;
+ }
+ }
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/ImageRotator.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/ImageRotator.php
new file mode 100644
index 00000000..ba5624dc
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/ImageRotator.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\Api\UsageException;
+use Mediawiki\DataModel\File;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class ImageRotator extends Service {
+
+ /**
+ * NOTE: This service has not been fully tested
+ *
+ * @param File $file
+ * @param int $rotation Degrees to rotate image clockwise, One value: 90, 180, 270
+ *
+ * @throws UsageException
+ * @return bool
+ */
+ public function rotate( File $file, $rotation ) {
+ $params = [
+ 'rotation' => $rotation,
+ 'token' => $this->api->getToken(),
+ ];
+
+ if ( !is_null( $file->getPageIdentifier()->getTitle() ) ) {
+ $params['titles'] = $file->getPageIdentifier()->getTitle()->getText();
+ } else {
+ $params['pageids'] = $file->getPageIdentifier()->getId();
+ }
+
+ $result = $this->api->postRequest( new SimpleRequest( 'imagerotate', $params ) );
+
+ // This module sometimes gives odd errors so deal with them..
+ if ( array_key_exists( 'imagerotate', $result ) ) {
+ $imageRotate = array_pop( $result['imagerotate'] );
+ if ( array_key_exists( 'result', $imageRotate ) &&
+ $imageRotate['result'] == 'Failure'
+ ) {
+ throw new UsageException(
+ 'imagerotate-Failure',
+ $imageRotate['errormessage'],
+ $result
+ );
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/LogListGetter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/LogListGetter.php
new file mode 100644
index 00000000..d9394919
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/LogListGetter.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Log;
+use Mediawiki\DataModel\LogList;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revisions;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @access private
+ *
+ * @author Thomas Arrow
+ */
+class LogListGetter extends Service {
+
+ /**
+ * @param array $extraParams
+ *
+ * @return LogList
+ */
+ public function getLogList( array $extraParams = [] ) {
+ $logList = new LogList();
+
+ while ( true ) {
+ $params = [
+ 'list' => 'logevents',
+ 'leprop' => 'title|ids|type|user|timestamp|comment|details'
+ ];
+
+ $newParams = array_merge( $extraParams, $params );
+ $result = $this->api->getRequest( new SimpleRequest( 'query', $newParams ) );
+
+ foreach ( $result[ 'query' ]['logevents'] as $logevent ) {
+ $logList->addLog(
+ new Log(
+ $logevent['logid'],
+ $logevent['type'],
+ $logevent['action'],
+ $logevent['timestamp'],
+ $logevent['user'],
+ new Page(
+ new PageIdentifier(
+ new Title( $logevent['title'], $logevent['ns'] ),
+ $logevent['pageid']
+ ),
+ new Revisions()
+ ),
+ $logevent['comment'],
+ $this->getLogDetailsFromEvent( $logevent )
+ )
+ );
+ }
+
+ return $logList;
+ }
+ }
+
+ /**
+ * @param array $event
+ *
+ * @return array
+ */
+ private function getLogDetailsFromEvent( $event ) {
+ $ignoreKeys = array_flip( [
+ 'logid',
+ 'ns',
+ 'title',
+ 'pageid',
+ 'logpage',
+ 'type',
+ 'action',
+ 'user',
+ 'type',
+ 'timestamp',
+ 'comment' ] );
+ return array_diff_key( $event, $ignoreKeys );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/NamespaceGetter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/NamespaceGetter.php
new file mode 100644
index 00000000..c3f00030
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/NamespaceGetter.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\NamespaceInfo;
+
+/**
+ * @access private
+ *
+ * @author gbirke
+ */
+class NamespaceGetter extends Service {
+
+ /**
+ * Find a namespace by its canonical name
+ *
+ * @param string $canonicalName
+ * @return NamespaceInfo|null
+ */
+ public function getNamespaceByCanonicalName( $canonicalName ) {
+ $result = $this->getNamespaceResult()['query'];
+ foreach ( $result['namespaces'] as $nsInfo ) {
+ if ( !empty( $nsInfo['canonical'] ) && $nsInfo['canonical'] === $canonicalName ) {
+ return $this->createNamespaceFromQuery( $nsInfo, $result['namespacealiases'] );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Find a namespace by its canonical name, local name or namespace alias
+ *
+ * @param string $name
+ * @return NamespaceInfo|null
+ */
+ public function getNamespaceByName( $name ) {
+ $result = $this->getNamespaceResult()['query'];
+ foreach ( $result['namespaces'] as $nsInfo ) {
+ if ( ( !empty( $nsInfo['canonical'] ) && $nsInfo['canonical'] === $name ) ||
+ $nsInfo['*'] === $name ) {
+ return $this->createNamespaceFromQuery( $nsInfo, $result['namespacealiases'] );
+ }
+ }
+ foreach ( $result['namespacealiases'] as $alias ) {
+ if ( $alias['*'] === $name && !empty( $result['namespaces'][$alias['id']] ) ) {
+ return $this->createNamespaceFromQuery(
+ $result['namespaces'][$alias['id']],
+ $result['namespacealiases']
+ );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return NamespaceInfo[]
+ */
+ public function getNamespaces() {
+ $namespaces = [];
+ $result = $this->getNamespaceResult()['query'];
+ foreach ( $result['namespaces'] as $nsInfo ) {
+ $namespaces[$nsInfo['id']] = $this->createNamespaceFromQuery(
+ $nsInfo, $result['namespacealiases']
+ );
+ }
+ return $namespaces;
+ }
+
+ private function createNamespaceFromQuery( $nsInfo, $namespaceAliases ) {
+ return new NamespaceInfo(
+ $nsInfo['id'],
+ empty( $nsInfo['canonical'] ) ? '' : $nsInfo['canonical'],
+ $nsInfo['*'],
+ $nsInfo['case'],
+ empty( $nsInfo['defaultcontentmodel'] ) ? null : $nsInfo['defaultcontentmodel'],
+ $this->getAliases( $nsInfo['id'], $namespaceAliases )
+ );
+ }
+
+ /**
+ * @param int $id
+ * @param array $namespaceAliases Alias list, as returned by the API
+ * @return string[]
+ */
+ private function getAliases( $id, $namespaceAliases ) {
+ $aliases = [];
+ foreach ( $namespaceAliases as $alias ) {
+ if ( $alias['id'] === $id ) {
+ $aliases[] = $alias['*'];
+ }
+ }
+ return $aliases;
+ }
+
+ /**
+ * @return array
+ */
+ private function getNamespaceResult() {
+ return $this->api->getRequest( new SimpleRequest(
+ 'query', [
+ 'meta' => 'siteinfo',
+ 'siprop' => 'namespaces|namespacealiases'
+ ]
+ ) );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageDeleter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageDeleter.php
new file mode 100644
index 00000000..60c43e30
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageDeleter.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageDeleter extends Service {
+
+ /**
+ * @since 0.2
+ *
+ * @param Page $page
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function delete( Page $page, array $extraParams = [] ) {
+ $this->api->postRequest( new SimpleRequest(
+ 'delete',
+ $this->getDeleteParams( $page->getPageIdentifier(), $extraParams )
+ ) );
+ return true;
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param Revision $revision
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function deleteFromRevision( Revision $revision, array $extraParams = [] ) {
+ $this->api->postRequest( new SimpleRequest(
+ 'delete',
+ $this->getDeleteParams( $revision->getPageIdentifier(), $extraParams )
+ ) );
+ return true;
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param int $pageid
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function deleteFromPageId( $pageid, array $extraParams = [] ) {
+ $this->api->postRequest( new SimpleRequest(
+ 'delete',
+ $this->getDeleteParams( new PageIdentifier( null, $pageid ), $extraParams )
+ ) );
+ return true;
+ }
+
+ /**
+ * @since 0.5
+ *
+ * @param Title|string $title
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function deleteFromPageTitle( $title, array $extraParams = [] ) {
+ if ( is_string( $title ) ) {
+ $title = new Title( $title );
+ }
+ $this->api->postRequest( new SimpleRequest(
+ 'delete',
+ $this->getDeleteParams( new PageIdentifier( $title ), $extraParams )
+ ) );
+ return true;
+ }
+
+ /**
+ * @param PageIdentifier $identifier
+ * @param array $extraParams
+ *
+ * @return array
+ */
+ private function getDeleteParams( PageIdentifier $identifier, $extraParams ) {
+ $params = [];
+
+ if ( !is_null( $identifier->getId() ) ) {
+ $params['pageid'] = $identifier->getId();
+ } else {
+ $params['title'] = $identifier->getTitle()->getTitle();
+ }
+
+ $params['token'] = $this->api->getToken( 'delete' );
+
+ return array_merge( $extraParams, $params );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageGetter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageGetter.php
new file mode 100644
index 00000000..6c5c54cf
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageGetter.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Content;
+use Mediawiki\DataModel\EditInfo;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Revisions;
+use Mediawiki\DataModel\Title;
+use RuntimeException;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageGetter extends Service {
+
+ /**
+ * @since 0.2
+ *
+ * @param int $id
+ * @param array $extraParams
+ *
+ * @return Page
+ */
+ public function getFromRevisionId( $id, array $extraParams = [] ) {
+ $result =
+ $this->api->getRequest(
+ new SimpleRequest(
+ 'query',
+ $this->getQuery( [ 'revids' => $id ], $extraParams )
+ )
+ );
+
+ return $this->newPageFromResult( array_shift( $result['query']['pages'] ) );
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param string|Title $title
+ * @param array $extraParams
+ *
+ * @return Page
+ */
+ public function getFromTitle( $title, array $extraParams = [] ) {
+ if ( $title instanceof Title ) {
+ $title = $title->getTitle();
+ }
+ $result =
+ $this->api->getRequest(
+ new SimpleRequest(
+ 'query',
+ $this->getQuery( [ 'titles' => $title ], $extraParams )
+ )
+ );
+
+ return $this->newPageFromResult( array_shift( $result['query']['pages'] ) );
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param int $id
+ * @param array $extraParams
+ *
+ * @return Page
+ */
+ public function getFromPageId( $id, array $extraParams = [] ) {
+ $result =
+ $this->api->getRequest(
+ new SimpleRequest(
+ 'query',
+ $this->getQuery( [ 'pageids' => $id ], $extraParams )
+ )
+ );
+
+ return $this->newPageFromResult( array_shift( $result['query']['pages'] ) );
+ }
+
+ /**
+ * @since 0.4
+ *
+ * @param PageIdentifier $pageIdentifier
+ * @param array $extraParams
+ *
+ * @throws RuntimeException
+ * @return Page
+ */
+ public function getFromPageIdentifier(
+ PageIdentifier $pageIdentifier,
+ array $extraParams = []
+ ) {
+ if ( !$pageIdentifier->identifiesPage() ) {
+ throw new RuntimeException( '$pageIdentifier does not identify a page' );
+ }
+ if ( !is_null( $pageIdentifier->getId() ) ) {
+ return $this->getFromPageId( $pageIdentifier->getId(), $extraParams );
+ } else {
+ return $this->getFromTitle( $pageIdentifier->getTitle(), $extraParams );
+ }
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param Page $page
+ * @param array $extraParams
+ *
+ * @return Page
+ */
+ public function getFromPage( Page $page, array $extraParams = [] ) {
+ $result =
+ $this->api->getRequest(
+ new SimpleRequest(
+ 'query',
+ $this->getQuery( [ 'pageids' => $page->getId() ], $extraParams )
+ )
+ );
+ $revisions = $this->getRevisionsFromResult( array_shift( $result['query']['pages'] ) );
+ $revisions->addRevisions( $page->getRevisions() );
+
+ return new Page(
+ $page->getPageIdentifier(),
+ $revisions
+ );
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param Revision $revision
+ * @param array $extraParams
+ *
+ * @return Page
+ */
+ public function getFromRevision( Revision $revision, array $extraParams = [] ) {
+ $result =
+ $this->api->getRequest(
+ new SimpleRequest(
+ 'query',
+ $this->getQuery( [ 'revids' => $revision->getId() ], $extraParams )
+ )
+ );
+ $revisions = $this->getRevisionsFromResult( array_shift( $result['query']['pages'] ) );
+ $revisions->addRevision( $revision );
+
+ return new Page(
+ new PageIdentifier(
+ new Title(
+ $result['title'],
+ $result['ns']
+ ),
+ $result['pageid']
+ ),
+ $revisions
+ );
+ }
+
+ /**
+ * @param array $additionalParams
+ *
+ * @param array $extraParams
+ *
+ * @return array
+ */
+ private function getQuery( $additionalParams, array $extraParams = [] ) {
+ $base = [
+ 'prop' => 'revisions|info|pageprops',
+ 'rvprop' => 'ids|flags|timestamp|user|size|sha1|comment|content|tags',
+ 'inprop' => 'protection',
+ ];
+
+ return array_merge( $extraParams, $base, $additionalParams );
+ }
+
+ /**
+ * @param array $array
+ *
+ * @return Revisions
+ */
+ private function getRevisionsFromResult( $array ) {
+ $revisions = new Revisions();
+ $pageid = $array['pageid'];
+ foreach ( $array['revisions'] as $revision ) {
+ $revisions->addRevision(
+ new Revision(
+ $this->getContent( $array['contentmodel'], $revision['*'] ),
+ new PageIdentifier( new Title( $array['title'], $array['ns'] ), $pageid ),
+ $revision['revid'],
+ new EditInfo(
+ $revision['comment'],
+ array_key_exists( 'minor', $revision ),
+ array_key_exists( 'bot', $revision )
+ ),
+ $revision['user'],
+ $revision['timestamp']
+ )
+ );
+ }
+
+ return $revisions;
+ }
+
+ /**
+ * @param string $model
+ * @param string $content returned from the API
+ *
+ * @throws RuntimeException
+ * @return Content
+ */
+ private function getContent( $model, $content ) {
+ return new Content( $content, $model );
+ }
+
+ /**
+ * @param array $array
+ *
+ * @return Page
+ */
+ private function newPageFromResult( $array ) {
+ if ( array_key_exists( 'pageid', $array ) ) {
+ $pageid = $array['pageid'];
+ $revisions = $this->getRevisionsFromResult( $array );
+ } else {
+ $pageid = 0;
+ $revisions = new Revisions();
+ }
+
+ return new Page(
+ new PageIdentifier(
+ new Title(
+ $array['title'],
+ $array['ns']
+ ),
+ $pageid
+ ),
+ $revisions
+ );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageListGetter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageListGetter.php
new file mode 100644
index 00000000..6b6d0007
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageListGetter.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Pages;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageListGetter extends Service {
+
+ /**
+ * Get the set of pages in a given category. Extra parameters can include:
+ * cmtype: default 'page|subcat|file'
+ * cmlimit: default 10, maximum 500 (5000 for bots)
+ *
+ * @link https://www.mediawiki.org/wiki/API:Categorymembers
+ * @since 0.3
+ *
+ * @param string $name
+ * @param array $extraParams
+ *
+ * @return Pages
+ */
+ public function getPageListFromCategoryName( $name, array $extraParams = [] ) {
+ $params = array_merge( $extraParams, [
+ 'list' => 'categorymembers',
+ 'cmtitle' => $name,
+ ] );
+ return $this->runQuery( $params, 'cmcontinue', 'categorymembers' );
+ }
+
+ /**
+ * List pages that transclude a certain page.
+ *
+ * @link https://www.mediawiki.org/wiki/API:Embeddedin
+ * @since 0.5
+ *
+ * @param string $pageName
+ * @param array $extraParams
+ *
+ * @return Pages
+ */
+ public function getPageListFromPageTransclusions( $pageName, array $extraParams = [] ) {
+ $params = array_merge( $extraParams, [
+ 'list' => 'embeddedin',
+ 'eititle' => $pageName,
+ ] );
+ return $this->runQuery( $params, 'eicontinue', 'embeddedin' );
+ }
+
+ /**
+ * Get all pages that link to the given page.
+ *
+ * @link https://www.mediawiki.org/wiki/API:Linkshere
+ * @since 0.5
+ * @uses PageListGetter::runQuery()
+ *
+ * @param string $pageName The page name
+ * @param string[] Any extra parameters to use: lhprop, lhnamespace, lhshow, lhlimit
+ *
+ * @return Pages
+ */
+ public function getFromWhatLinksHere( $pageName, $extraParams = [] ) {
+ $params = array_merge( $extraParams, [
+ 'prop' => 'info',
+ 'generator' => 'linkshere',
+ 'titles' => $pageName,
+ ] );
+ return $this->runQuery( $params, 'glhcontinue', 'pages' );
+ }
+
+ /**
+ * Get all pages that have the given prefix.
+ *
+ * @link https://www.mediawiki.org/wiki/API:Allpages
+ *
+ * @param string $prefix The page title prefix.
+ *
+ * @return Pages
+ */
+ public function getFromPrefix( $prefix ) {
+ $params = [
+ 'list' => 'allpages',
+ 'apprefix' => $prefix,
+ ];
+ return $this->runQuery( $params, 'apcontinue', 'allpages' );
+ }
+
+ /**
+ * Get up to 10 random pages.
+ *
+ * @link https://www.mediawiki.org/wiki/API:Random
+ * @uses PageListGetter::runQuery()
+ *
+ * @param array $extraParams
+ *
+ * @return Pages
+ */
+ public function getRandom( array $extraParams = [] ) {
+ $params = array_merge( $extraParams, [ 'list' => 'random' ] );
+ return $this->runQuery( $params, null, 'random', 'id', false );
+ }
+
+ /**
+ * Run a query to completion.
+ *
+ * @param string[] $params Query parameters
+ * @param string $contName Result subelement name for continue details
+ * @param string $resName Result element name for main results array
+ * @param string $pageIdName Result element name for page ID
+ * @param bool $cont Whether to continue the query, using multiple requests
+ * @return Pages
+ */
+ protected function runQuery( $params, $contName, $resName, $pageIdName = 'pageid', $cont = true ) {
+ $pages = new Pages();
+
+ do {
+ // Set up continue parameter if it's been set already.
+ if ( isset( $result['continue'][$contName] ) ) {
+ $params[$contName] = $result['continue'][$contName];
+ }
+
+ // Run the actual query.
+ $result = $this->api->getRequest( new SimpleRequest( 'query', $params ) );
+ if ( !array_key_exists( 'query', $result ) ) {
+ return $pages;
+ }
+
+ // Add the results to the output page list.
+ foreach ( $result['query'][$resName] as $member ) {
+ $pageTitle = new Title( $member['title'], $member['ns'] );
+ $page = new Page( new PageIdentifier( $pageTitle, $member[$pageIdName] ) );
+ $pages->addPage( $page );
+ }
+
+ } while ( $cont && isset( $result['continue'] ) );
+
+ return $pages;
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageMover.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageMover.php
new file mode 100644
index 00000000..f7eba2af
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageMover.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageMover extends Service {
+
+ /**
+ * @since 0.2
+ *
+ * @param Page $page
+ * @param Title $target
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function move( Page $page, Title $target, array $extraParams = [] ) {
+ $this->api->postRequest(
+ new SimpleRequest(
+ 'move', $this->getMoveParams( $page->getId(), $target, $extraParams )
+ )
+ );
+
+ return true;
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param int $pageid
+ * @param Title $target
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function moveFromPageId( $pageid, Title $target, array $extraParams = [] ) {
+ $this->api->postRequest(
+ new SimpleRequest( 'move', $this->getMoveParams( $pageid, $target, $extraParams ) )
+ );
+
+ return true;
+ }
+
+ /**
+ * @param int $pageid
+ * @param Title $target
+ * @param array $extraParams
+ *
+ * @return array
+ */
+ private function getMoveParams( $pageid, $target, $extraParams ) {
+ $params = [];
+ $params['fromid'] = $pageid;
+ $params['to'] = $target->getTitle();
+ $params['token'] = $this->api->getToken( 'move' );
+
+ return array_merge( $extraParams, $params );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageProtector.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageProtector.php
new file mode 100644
index 00000000..e3988e84
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageProtector.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use InvalidArgumentException;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageProtector extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param Page $page
+ * @param string[] $protections where the 'key' is the action and the 'value' is the group
+ * @param array $extraParams
+ *
+ * @return bool
+ * @throws InvalidArgumentException
+ */
+ public function protect( Page $page, $protections, array $extraParams = [] ) {
+ if ( !is_array( $protections ) || empty( $protections ) ) {
+ throw new InvalidArgumentException(
+ '$protections must be an array with keys and values'
+ );
+ }
+
+ $params = [
+ 'pageid' => $page->getId(),
+ 'token' => $this->api->getToken( 'protect' ),
+ ];
+ $protectionsString = '';
+ foreach ( $protections as $action => $value ) {
+ if ( !is_string( $action ) || !is_string( $value ) ) {
+ throw new InvalidArgumentException(
+ 'All keys and elements of $protections must be strings'
+ );
+ }
+ $protectionsString = $action . '=' . $value . '|';
+ }
+ $params['protections'] = rtrim( $protectionsString, '|' );
+
+ $this->api->postRequest(
+ new SimpleRequest( 'protect', array_merge( $extraParams, $params ) )
+ );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PagePurger.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PagePurger.php
new file mode 100644
index 00000000..6f9057f1
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PagePurger.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\Generator\ApiGenerator;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Pages;
+use Mediawiki\DataModel\Page;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ * @author Thomas Arrow
+ */
+class PagePurger extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @brief Purge a single page
+ *
+ * Purges a single page by submitting a
+ * 'purge' action to the mediawiki api
+ * with the parameter 'pageids' set to
+ * the singe page id
+ *
+ * @param Page $page the page that is going to be purged
+ *
+ * @return bool return true if the purge was successful
+ */
+ public function purge( Page $page ) {
+ $responseArray = $this->api->postRequest(
+ new SimpleRequest( 'purge', [ 'pageids' => $page->getId() ] )
+ );
+
+ // the purge response for the page
+ $purgeResponse = $responseArray['purge'][0];
+
+ return array_key_exists( 'purged', $purgeResponse );
+ }
+
+ /**
+ * @since 0.7
+ *
+ * @brief Purge multiple pages
+ *
+ * Purges all the pages of the Pages object
+ * by submitting a 'purge' action to the mediawiki
+ * api with the parameter 'pageids' set to be the
+ * pages ids in multiple-value seperation.
+ *
+ * @param Pages $pages the pages that are going to be purged
+ *
+ * @return Pages the pages that have been purged successfully
+ */
+ public function purgePages( Pages $pages ) {
+ $pagesArray = $pages->toArray();
+ $pagesIds = [];
+
+ foreach ( $pagesArray as $page ) {
+ array_push( $pagesIds, $page->getId() );
+ }
+
+ // convert an array to multiple-value format
+ // because the mediawiki api require multiple
+ // values to be seperated like the example
+ // ex: [111, 222, 333] => "111|222|333"
+ $pageIdsMultiple = implode( '|', $pagesIds );
+
+ $responseArray = $this->api->postRequest(
+ new SimpleRequest( 'purge', [ 'pageids' => $pageIdsMultiple ] )
+ );
+
+ // array that will hold the successfully purged pages
+ $purgedPages = new Pages();
+
+ // for every purge result
+ foreach ( $responseArray['purge'] as $purgeResponse ) {
+ // if the purge for the page was successful
+ if ( array_key_exists( 'purged', $purgeResponse ) ) {
+ // we iterate all the input pages
+ foreach ( $pagesArray as $page ) {
+ // and if the page from the input was successfully purged
+ if ( $purgeResponse['title'] === $page->getTitle()->getText() ) {
+ // add it in the purgedPages object
+ $purgedPages->addPage( $page );
+
+ break;
+ }
+
+ }
+
+ }
+
+ }
+
+ return $purgedPages;
+ }
+
+ /**
+ * @since 0.6
+ *
+ * @param ApiGenerator $generator
+ *
+ * @return bool
+ */
+ public function purgeGenerator( ApiGenerator $generator ) {
+ $this->api->postRequest(
+ new SimpleRequest( 'purge', $generator->getParams() )
+ );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageRestorer.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageRestorer.php
new file mode 100644
index 00000000..a422bd97
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageRestorer.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\Title;
+use OutOfBoundsException;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageRestorer extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param Page $page
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function restore( Page $page, array $extraParams = [] ) {
+ $this->api->postRequest(
+ new SimpleRequest(
+ 'undelete',
+ $this->getUndeleteParams( $page->getTitle(), $extraParams )
+ )
+ );
+
+ return true;
+ }
+
+ /**
+ * @param Title $title
+ * @param array $extraParams
+ *
+ * @return array
+ */
+ private function getUndeleteParams( Title $title, $extraParams ) {
+ $params = [];
+
+ $params['title'] = $title->getTitle();
+ $params['token'] = $this->getUndeleteToken( $title );
+
+ return array_merge( $extraParams, $params );
+ }
+
+ /**
+ * @param Title $title
+ *
+ * @throws OutOfBoundsException
+ * @returns string
+ */
+ private function getUndeleteToken( Title $title ) {
+ $response = $this->api->postRequest(
+ new SimpleRequest(
+ 'query', [
+ 'list' => 'deletedrevs',
+ 'titles' => $title->getTitle(),
+ 'drprop' => 'token',
+ ]
+ )
+ );
+ if ( array_key_exists( 'token', $response['query']['deletedrevs'][0] ) ) {
+ return $response['query']['deletedrevs'][0]['token'];
+ } else {
+ throw new OutOfBoundsException(
+ 'Could not get page undelete token from list=deletedrevs query'
+ );
+ }
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageWatcher.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageWatcher.php
new file mode 100644
index 00000000..e7afab17
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageWatcher.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageWatcher extends Service {
+
+ /**
+ * @param Page $page
+ *
+ * @return bool
+ */
+ public function watch( Page $page ) {
+ $params = [
+ 'token' => $this->api->getToken( 'watch' ),
+ ];
+ if ( !is_null( $page->getPageIdentifier()->getId() ) ) {
+ $params['pageids'] = $page->getPageIdentifier()->getId();
+ } elseif ( !is_null( $page->getPageIdentifier()->getTitle() ) ) {
+ $params['titles'] = $page->getPageIdentifier()->getTitle()->getTitle();
+ } elseif ( !is_null( $page->getRevisions()->getLatest() ) ) {
+ $params['revids'] = $page->getRevisions()->getLatest()->getId();
+ }
+
+ $this->api->postRequest( new SimpleRequest( 'watch', $params ) );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Parser.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Parser.php
new file mode 100644
index 00000000..da83e39e
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Parser.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use GuzzleHttp\Promise\PromiseInterface;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\PageIdentifier;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class Parser extends Service {
+
+ /**
+ * @param PageIdentifier $pageIdentifier
+ *
+ * @return array the parse result (raw from the api)
+ */
+ public function parsePage( PageIdentifier $pageIdentifier ) {
+ return $this->parsePageAsync( $pageIdentifier )->wait();
+ }
+
+ /**
+ * @param PageIdentifier $pageIdentifier
+ *
+ * @return PromiseInterface of array the parse result (raw from the api)
+ */
+ public function parsePageAsync( PageIdentifier $pageIdentifier ) {
+ $params = [];
+ if ( $pageIdentifier->getId() !== null ) {
+ $params['pageid'] = $pageIdentifier->getId();
+ } elseif ( $pageIdentifier->getTitle() !== null ) {
+ $params['page'] = $pageIdentifier->getTitle()->getText();
+ } else {
+ throw new \RuntimeException( 'No way to identify page' );
+ }
+
+ $promise = $this->api->getRequestAsync( new SimpleRequest( 'parse', $params ) );
+
+ return $promise->then( function ( $result ) {
+ return $result['parse'];
+ } );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionDeleter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionDeleter.php
new file mode 100644
index 00000000..b12a7278
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionDeleter.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Revision;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class RevisionDeleter extends Service {
+
+ /**
+ * @since 0.5
+ *
+ * @param Revision $revision
+ *
+ * @return bool
+ */
+ public function delete( Revision $revision ) {
+ $params = [
+ 'type' => 'revision',
+ 'hide' => 'content',
+ // Note: pre 1.24 this is a delete token, post it is csrf
+ 'token' => $this->api->getToken( 'delete' ),
+ 'ids' => $revision->getId(),
+ ];
+
+ $this->api->postRequest( new SimpleRequest(
+ 'revisiondelete',
+ $params
+ ) );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionPatroller.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionPatroller.php
new file mode 100644
index 00000000..e642fd96
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionPatroller.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Revision;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class RevisionPatroller extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param Revision $revision
+ *
+ * @return bool success
+ */
+ public function patrol( Revision $revision ) {
+ $this->api->postRequest( new SimpleRequest(
+ 'patrol', [
+ 'revid' => $revision->getId(),
+ 'token' => $this->getTokenForRevision( $revision ),
+ ] ) );
+ return true;
+ }
+
+ /**
+ * @param Revision $revision
+ *
+ * @returns string
+ */
+ private function getTokenForRevision( Revision $revision ) {
+ $result = $this->api->postRequest( new SimpleRequest( 'query', [
+ 'list' => 'recentchanges',
+ 'rcstart' => $revision->getTimestamp(),
+ 'rcend' => $revision->getTimestamp(),
+ 'rctoken' => 'patrol',
+ ] ) );
+ $result = array_shift( $result['query']['recentchanges'] );
+ return $result['patroltoken'];
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRestorer.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRestorer.php
new file mode 100644
index 00000000..fb82c252
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRestorer.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Revision;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class RevisionRestorer extends Service {
+
+ /**
+ * @since 0.5
+ *
+ * @param Revision $revision
+ *
+ * @return bool
+ */
+ public function restore( Revision $revision ) {
+ $params = [
+ 'type' => 'revision',
+ 'show' => 'content',
+ // Note: pre 1.24 this is a delete token, post it is csrf
+ 'token' => $this->api->getToken( 'delete' ),
+ 'ids' => $revision->getId(),
+ ];
+
+ $this->api->postRequest( new SimpleRequest(
+ 'revisiondelete',
+ $params
+ ) );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRollbacker.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRollbacker.php
new file mode 100644
index 00000000..76a2f5c7
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRollbacker.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class RevisionRollbacker extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param Revision $revision
+ * @param Title $title if using MW 1.24 of lower (https://gerrit.wikimedia.org/r/#/c/133063/)
+ *
+ * @return bool
+ */
+ public function rollback( Revision $revision, Title $title = null ) {
+ $this->api->postRequest(
+ new SimpleRequest( 'rollback', $this->getRollbackParams( $revision, $title ) )
+ );
+
+ return true;
+ }
+
+ /**
+ * @param Revision $revision
+ * @param Title|null $title
+ *
+ * @return array
+ */
+ private function getRollbackParams( Revision $revision, $title ) {
+ $params = [];
+ if ( !is_null( $title ) ) {
+ // This is needed prior to https://gerrit.wikimedia.org/r/#/c/133063/
+ $params['title'] = $title->getTitle();
+ } else {
+ // This will work after https://gerrit.wikimedia.org/r/#/c/133063/
+ $params['pageid'] = $revision->getPageId();
+ }
+ $params['user'] = $revision->getUser();
+ $params['token'] = $this->getTokenForRevision( $revision );
+
+ return $params;
+ }
+
+ /**
+ * @param Revision $revision
+ *
+ * @returns string
+ */
+ private function getTokenForRevision( Revision $revision ) {
+ $result = $this->api->postRequest(
+ new SimpleRequest(
+ 'query', [
+ 'prop' => 'revisions',
+ 'revids' => $revision->getId(),
+ 'rvtoken' => 'rollback',
+ ]
+ )
+ );
+ $result = array_shift( $result['query']['pages'] );
+
+ return $result['revisions'][0]['rollbacktoken'];
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionSaver.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionSaver.php
new file mode 100644
index 00000000..00b91ee6
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionSaver.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\EditInfo;
+use Mediawiki\DataModel\Revision;
+use RuntimeException;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ * @author DFelten (EditInfo fix)
+ */
+class RevisionSaver extends Service {
+
+ /**
+ * @since 0.2
+ *
+ * @param Revision $revision
+ * @param EditInfo $editInfo
+ *
+ * @return bool success
+ */
+ public function save( Revision $revision, EditInfo $editInfo = null ) {
+ $editInfo = $editInfo ? $editInfo : $revision->getEditInfo();
+
+ $result = $this->api->postRequest(
+ new SimpleRequest( 'edit', $this->getEditParams( $revision, $editInfo ) )
+ );
+ return ( $result['edit']['result'] == 'Success' );
+ }
+
+ /**
+ * @param Revision $revision
+ * @param EditInfo $editInfo
+ *
+ * @throws RuntimeException
+ * @returns array
+ */
+ private function getEditParams( Revision $revision, EditInfo $editInfo = null ) {
+ if ( !$revision->getPageIdentifier()->identifiesPage() ) {
+ throw new RuntimeException( '$revision PageIdentifier does not identify a page' );
+ }
+
+ $params = [];
+
+ $content = $revision->getContent();
+ $data = $content->getData();
+ if ( !is_string( $data ) ) {
+ throw new RuntimeException( 'Dont know how to save content of this model.' );
+ }
+ $params['text'] = $content->getData();
+ $params['md5'] = md5( $content->getData() );
+
+ $timestamp = $revision->getTimestamp();
+ if ( !is_null( $timestamp ) ) {
+ $params['basetimestamp'] = $timestamp;
+ }
+
+ if ( !is_null( $revision->getPageIdentifier()->getId() ) ) {
+ $params['pageid'] = $revision->getPageIdentifier()->getId();
+ } else {
+ $params['title'] = $revision->getPageIdentifier()->getTitle()->getTitle();
+ }
+
+ $params['token'] = $this->api->getToken();
+
+ if ( $this->api->isLoggedin() ) {
+ $params['assert'] = 'user';
+ }
+
+ $this->addEditInfoParams( $editInfo, $params );
+
+ return $params;
+ }
+
+ /**
+ * @param null|EditInfo $editInfo
+ * @param array &$params
+ */
+ private function addEditInfoParams( $editInfo, &$params ) {
+ if ( !is_null( $editInfo ) ) {
+ $params['summary'] = $editInfo->getSummary();
+ if ( $editInfo->getMinor() ) {
+ $params['minor'] = true;
+ }
+ if ( $editInfo->getBot() ) {
+ $params['bot'] = true;
+ $params['assert'] = 'bot';
+ }
+ }
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionUndoer.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionUndoer.php
new file mode 100644
index 00000000..e411030e
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionUndoer.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Revision;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class RevisionUndoer extends Service {
+
+ /**
+ * @param Revision $revision
+ *
+ * @return bool
+ */
+ public function undo( Revision $revision ) {
+ $this->api->postRequest( new SimpleRequest(
+ 'edit',
+ $this->getParamsFromRevision( $revision )
+ ) );
+ return true;
+ }
+
+ /**
+ * @param Revision $revision
+ *
+ * @return array
+ */
+ private function getParamsFromRevision( Revision $revision ) {
+ $params = [
+ 'undo' => $revision->getId(),
+ 'token' => $this->api->getToken(),
+ ];
+
+ if ( !is_null( $revision->getPageIdentifier()->getId() ) ) {
+ $params['pageid'] = $revision->getPageIdentifier()->getId();
+ } else {
+ $params['title'] = $revision->getPageIdentifier()->getTitle()->getTitle();
+ }
+
+ return $params;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Service.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Service.php
new file mode 100644
index 00000000..4a4b466b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Service.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace MediaWiki\Api\Service;
+
+use Mediawiki\Api\MediawikiApi;
+
+/**
+ * The base service functions that all services inherit.
+ * @since 0.7.2
+ */
+abstract class Service {
+
+ /** @var MediawikiApi */
+ protected $api;
+
+ /**
+ * @param MediawikiApi $api The API to in for this service.
+ */
+ public function __construct( MediawikiApi $api ) {
+ $this->api = $api;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserBlocker.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserBlocker.php
new file mode 100644
index 00000000..9cf9f421
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserBlocker.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use InvalidArgumentException;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\User;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class UserBlocker extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param User|string $user
+ * @param array $extraParams
+ *
+ * @throws InvalidArgumentException
+ * @return bool
+ */
+ public function block( $user, array $extraParams = [] ) {
+ if ( !$user instanceof User && !is_string( $user ) ) {
+ throw new InvalidArgumentException( '$user must be either a string or User object' );
+ }
+
+ if ( $user instanceof User ) {
+ $user = $user->getName();
+ }
+
+ $params = [
+ 'user' => $user,
+ 'token' => $this->api->getToken( 'block' ),
+ ];
+
+ $params = array_merge( $extraParams, $params );
+
+ $this->api->postRequest( new SimpleRequest( 'block', $params ) );
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserCreator.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserCreator.php
new file mode 100644
index 00000000..8d74b107
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserCreator.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use InvalidArgumentException;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\Api\UsageException;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class UserCreator extends Service {
+
+ /**
+ * @param string $username
+ * @param string $password
+ * @param string|null $email
+ *
+ * @return bool
+ */
+ public function create( $username, $password, $email = null ) {
+ if ( !is_string( $username ) ) {
+ throw new InvalidArgumentException( '$username should be a string' );
+ }
+ if ( !is_string( $password ) ) {
+ throw new InvalidArgumentException( '$password should be a string' );
+ }
+ if ( !is_string( $email ) && !is_null( $email ) ) {
+ throw new InvalidArgumentException( '$email should be a string or null' );
+ }
+
+ $params = [
+ 'createreturnurl' => $this->api->getApiUrl(),
+ 'createtoken' => $this->api->getToken( 'createaccount' ),
+ 'username' => $username,
+ 'password' => $password,
+ 'retype' => $password,
+ ];
+
+ if ( !is_null( $email ) ) {
+ $params['email'] = $email;
+ }
+
+ try {
+ $result = $this->api->postRequest( new SimpleRequest( 'createaccount', $params ) );
+ return $result['createaccount']['status'] === 'PASS';
+ } catch ( UsageException $exception ) {
+ // If the above request failed, try again in the old way.
+ if ( $exception->getApiCode() === 'noname' ) {
+ return $this->createPreOneTwentySeven( $params );
+ }
+ throw $exception;
+ }
+ }
+
+ /**
+ * Create a user in the pre 1.27 manner.
+ * @link https://www.mediawiki.org/wiki/API:Account_creation/pre-1.27
+ * @return bool
+ */
+ protected function createPreOneTwentySeven( $params ) {
+ $newParams = [
+ 'name' => $params['username'],
+ 'password' => $params['password'],
+ ];
+ if ( array_key_exists( 'email', $params ) ) {
+ $newParams['email'] = $params['email'];
+ }
+ // First get the token.
+ $tokenRequest = new SimpleRequest( 'createaccount', $newParams );
+ $result = $this->api->postRequest( $tokenRequest );
+ if ( $result['createaccount']['result'] == 'NeedToken' ) {
+ // Then send the token to create the account.
+ $newParams['token'] = $result['createaccount']['token'];
+ $request = new SimpleRequest( 'createaccount', $newParams );
+ $result = $this->api->postRequest( $request );
+ }
+ return ( $result['createaccount']['result'] === 'Success' );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserGetter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserGetter.php
new file mode 100644
index 00000000..d3bfbd5b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserGetter.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\User;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class UserGetter extends Service {
+
+ /**
+ * @param string $username
+ *
+ * @return User
+ */
+ public function getFromUsername( $username ) {
+ $result = $this->api->getRequest(
+ new SimpleRequest(
+ 'query', [
+ 'list' => 'users',
+ 'ususers' => $username,
+ 'usprop' => 'gender|emailable|registration|editcount|rights|implicitgroups|groups|blockinfo',
+ ]
+ )
+ );
+
+ return $this->newUserFromListUsersResult( array_shift( $result['query']['users'] ) );
+ }
+
+ /**
+ * @param array $array
+ *
+ * @return User
+ */
+ private function newUserFromListUsersResult( $array ) {
+ if ( array_key_exists( 'userid', $array ) ) {
+ return new User(
+ $array['name'],
+ $array['userid'],
+ $array['editcount'],
+ $array['registration'],
+ [ 'groups' => $array['groups'], 'implicitgroups' => $array['implicitgroups'] ],
+ $array['rights'],
+ $array['gender']
+ );
+ } else {
+ return new User(
+ $array['name'],
+ 0,
+ 0,
+ '',
+ [ 'groups' => [], 'implicitgroups' => [] ],
+ [],
+ ''
+ );
+ }
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserRightsChanger.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserRightsChanger.php
new file mode 100644
index 00000000..4fbea9c1
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserRightsChanger.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\User;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class UserRightsChanger extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param User $user
+ * @param string[] $add
+ * @param string[] $remove
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function change(
+ User $user,
+ $add = [],
+ $remove = [],
+ array $extraParams = []
+ ) {
+ $result = $this->api->postRequest(
+ new SimpleRequest(
+ 'query', [
+ 'list' => 'users',
+ 'ustoken' => 'userrights',
+ 'ususers' => $user->getName(),
+ ]
+ )
+ );
+
+ $params = [
+ 'user' => $user->getName(),
+ 'token' => $result['query']['users'][0]['userrightstoken'],
+ ];
+ if ( !empty( $add ) ) {
+ $params['add'] = implode( '|', $add );
+ }
+ if ( !empty( $remove ) ) {
+ $params['remove'] = implode( '|', $remove );
+ }
+
+ $this->api->postRequest(
+ new SimpleRequest( 'userrights', array_merge( $extraParams, $params ) )
+ );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/blue ℳ𝒲β™₯π“Šπ“ƒπ’Ύπ’Έβ„΄π’Ήβ„―.png b/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/blue ℳ𝒲β™₯π“Šπ“ƒπ’Ύπ’Έβ„΄π’Ήβ„―.png
new file mode 100644
index 00000000..9f8efbc8
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/blue ℳ𝒲β™₯π“Šπ“ƒπ’Ύπ’Έβ„΄π’Ήβ„―.png
Binary files differ
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/namespaces.json b/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/namespaces.json
new file mode 100644
index 00000000..2933692a
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/namespaces.json
@@ -0,0 +1,241 @@
+{
+ "batchcomplete": "",
+ "query": {
+ "namespaces": {
+ "-2": {
+ "id": -2,
+ "case": "first-letter",
+ "canonical": "Media",
+ "*": "Medium"
+ },
+ "-1": {
+ "id": -1,
+ "case": "first-letter",
+ "canonical": "Special",
+ "*": "Spezial"
+ },
+ "0": {
+ "id": 0,
+ "case": "first-letter",
+ "content": "",
+ "*": ""
+ },
+ "1": {
+ "id": 1,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Talk",
+ "*": "Diskussion"
+ },
+ "2": {
+ "id": 2,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "User",
+ "*": "Benutzer"
+ },
+ "3": {
+ "id": 3,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "User talk",
+ "*": "Benutzer Diskussion"
+ },
+ "4": {
+ "id": 4,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Project",
+ "*": "Wikipedia"
+ },
+ "5": {
+ "id": 5,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Project talk",
+ "*": "Wikipedia Diskussion"
+ },
+ "6": {
+ "id": 6,
+ "case": "first-letter",
+ "canonical": "File",
+ "*": "Datei"
+ },
+ "7": {
+ "id": 7,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "File talk",
+ "*": "Datei Diskussion"
+ },
+ "8": {
+ "id": 8,
+ "case": "first-letter",
+ "canonical": "MediaWiki",
+ "*": "MediaWiki"
+ },
+ "9": {
+ "id": 9,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "MediaWiki talk",
+ "*": "MediaWiki Diskussion"
+ },
+ "10": {
+ "id": 10,
+ "case": "first-letter",
+ "canonical": "Template",
+ "*": "Vorlage"
+ },
+ "11": {
+ "id": 11,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Template talk",
+ "*": "Vorlage Diskussion"
+ },
+ "12": {
+ "id": 12,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Help",
+ "*": "Hilfe"
+ },
+ "13": {
+ "id": 13,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Help talk",
+ "*": "Hilfe Diskussion"
+ },
+ "14": {
+ "id": 14,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Category",
+ "*": "Kategorie"
+ },
+ "15": {
+ "id": 15,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Category talk",
+ "*": "Kategorie Diskussion"
+ },
+ "100": {
+ "id": 100,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Portal",
+ "*": "Portal"
+ },
+ "101": {
+ "id": 101,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Portal Diskussion",
+ "*": "Portal Diskussion"
+ },
+ "828": {
+ "id": 828,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Module",
+ "*": "Modul"
+ },
+ "829": {
+ "id": 829,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Module talk",
+ "*": "Modul Diskussion"
+ },
+ "2300": {
+ "id": 2300,
+ "case": "first-letter",
+ "canonical": "Gadget",
+ "*": "Gadget"
+ },
+ "2301": {
+ "id": 2301,
+ "case": "first-letter",
+ "canonical": "Gadget talk",
+ "*": "Gadget Diskussion"
+ },
+ "2302": {
+ "id": 2302,
+ "case": "case-sensitive",
+ "canonical": "Gadget definition",
+ "defaultcontentmodel": "GadgetDefinition",
+ "*": "Gadget-Definition"
+ },
+ "2303": {
+ "id": 2303,
+ "case": "case-sensitive",
+ "canonical": "Gadget definition talk",
+ "*": "Gadget-Definition Diskussion"
+ },
+ "2600": {
+ "id": 2600,
+ "case": "first-letter",
+ "canonical": "Topic",
+ "defaultcontentmodel": "flow-board",
+ "*": "Thema"
+ }
+ },
+ "namespacealiases": [
+ {
+ "id": 2,
+ "*": "Benutzerin"
+ },
+ {
+ "id": 3,
+ "*": "BD"
+ },
+ {
+ "id": 3,
+ "*": "Benutzerin Diskussion"
+ },
+ {
+ "id": 4,
+ "*": "WP"
+ },
+ {
+ "id": 5,
+ "*": "WD"
+ },
+ {
+ "id": 6,
+ "*": "Bild"
+ },
+ {
+ "id": 6,
+ "*": "Image"
+ },
+ {
+ "id": 7,
+ "*": "Bild Diskussion"
+ },
+ {
+ "id": 7,
+ "*": "Image talk"
+ },
+ {
+ "id": 12,
+ "*": "H"
+ },
+ {
+ "id": 13,
+ "*": "HD"
+ },
+ {
+ "id": 100,
+ "*": "P"
+ },
+ {
+ "id": 101,
+ "*": "PD"
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/CategoryTraverserTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/CategoryTraverserTest.php
new file mode 100644
index 00000000..7161447d
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/CategoryTraverserTest.php
@@ -0,0 +1,203 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\CategoryLoopException;
+use Mediawiki\Api\Service\CategoryTraverser;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Title;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Content;
+
+class CategoryTraverserTest extends \PHPUnit_Framework_TestCase {
+
+ /** @var TestEnvironment */
+ protected $testEnvironment;
+
+ /** @var \Mediawiki\Api\MediawikiFactory */
+ protected $factory;
+
+ /** @var \Mediawiki\Api\Service\CategoryTraverser */
+ protected $traverser;
+
+ public function setUp() {
+ parent::setUp();
+ $this->testEnvironment = TestEnvironment::newDefault();
+ $this->factory = $this->testEnvironment->getFactory();
+ $this->traverser = $this->factory->newCategoryTraverser();
+ }
+
+ /**
+ * A convenience wrapper around a PageDeleter.
+ * @param string[] $titles The titles to delete.
+ */
+ public function deletePages( $titles ) {
+ $deleter = $this->factory->newPageDeleter();
+ foreach ( $titles as $t ) {
+ // @todo Properly delete?
+ // $deleter->deleteFromPageTitle( new Title( $t ) );
+ $this->savePage( $t, '' );
+ }
+ }
+
+ /**
+ * A convenience wrapper to a RevisionSaver.
+ * @param string $title The title of the new page.
+ * @param string $content The wikitext to save to the page.
+ * @return Page The saved Page.
+ */
+ protected function savePage( $title, $content ) {
+ $pageIdentifier = new PageIdentifier( new Title( $title ) );
+ $revision = new Revision( new Content( $content ), $pageIdentifier );
+ $this->factory->newRevisionSaver()->save( $revision );
+ return $this->factory->newPageGetter()->getFromPageIdentifier( $pageIdentifier );
+ }
+
+ /**
+ * Get a list of all pages in a category or any of its descendants.
+ */
+ public function testDescendants() {
+ $rootCat = $this->savePage( 'Category:Root category', '' );
+ $this->savePage( 'Category:Sub category B', '[[Category:Root category]]' );
+ $this->savePage( 'Category:Sub category C', '[[Category:Root category]]' );
+ $this->savePage( 'Test page A1', 'Testing. [[Category:Root category]]' );
+ $this->savePage( 'Test page B1', 'Testing. [[Category:Sub category B]]' );
+ $this->savePage( 'Test page B2', 'Testing. [[Category:Sub category B]]' );
+ $this->savePage( 'Test page C1', 'Testing. [[Category:Sub category C]]' );
+ $this->testEnvironment->runJobs();
+
+ $callback = function ( Page $pageInfo, Page $parentCat ) {
+ $parentCatName = $parentCat->getPageIdentifier()->getTitle()->getText();
+ $thisPageName = $pageInfo->getPageIdentifier()->getTitle()->getText();
+ if ( $parentCatName === 'Category:Root category' ) {
+ $this->assertEquals( 'Test page A1', $thisPageName );
+ }
+ if ( $parentCatName === 'Category:Sub category C' ) {
+ $this->assertEquals( 'Test page C1', $thisPageName );
+ }
+ };
+ $this->traverser->addCallback( CategoryTraverser::CALLBACK_PAGE, $callback );
+ $decendants = $this->traverser->descend( $rootCat );
+ $this->assertCount( 4, $decendants->toArray() );
+ $this->deletePages( [
+ 'Category:Root category',
+ 'Category:Sub category B',
+ 'Category:Sub category C',
+ 'Test page A1',
+ 'Test page B1',
+ 'Test page B2',
+ 'Test page C1',
+ ] );
+ }
+
+ /**
+ * Make sure there aren't duplicate results when there are multiple paths to
+ * the same page.
+ */
+ public function testDescendantsWithMultiplePaths() {
+ $grandparent = $this->savePage( 'Category:Grandparent', '' );
+ $this->savePage( 'Category:Parent 1', '[[Category:Grandparent]]' );
+ $this->savePage( 'Category:Parent 2', '[[Category:Grandparent]]' );
+ $this->savePage( 'Parent 1', '[[Category:Grandparent]]' );
+ $this->savePage( 'Child 1', '[[Category:Parent 1]]' );
+ $this->savePage( 'Child 2', '[[Category:Parent 1]]' );
+ $this->savePage( 'Child 3', '[[Category:Parent 2]]' );
+ $this->testEnvironment->runJobs();
+ $decendants = $this->traverser->descend( $grandparent );
+ $this->assertCount( 4, $decendants->toArray() );
+ $this->deletePages( [
+ 'Category:Grandparent',
+ 'Category:Parent 1',
+ 'Category:Parent 2',
+ 'Child 1',
+ 'Child 2',
+ 'Child 3',
+ ] );
+ }
+
+ /**
+ * Categories should only be traversed once. For example, in the following graph, 'C' can be
+ * reached as a child of 'A' or of 'B', but only the first arrival will proceed to 'D':
+ *
+ * A
+ * | \
+ * | B
+ * | /
+ * C
+ * |
+ * D
+ *
+ */
+ public function testDescendantsOnlyVisitCatsOnce() {
+ global $wgVisitedCats;
+ $wgVisitedCats = [];
+ $catA = $this->savePage( 'Category:A cat', '' );
+ $this->savePage( 'Category:B cat', 'Testing. [[Category:A cat]]' );
+ $this->savePage( 'Category:C cat', 'Testing. [[Category:A cat]][[Category:B cat]]' );
+ $this->savePage( 'Category:D cat', 'Testing. [[Category:C cat]]' );
+ $this->testEnvironment->runJobs();
+ $callback = function ( Page $pageInfo, Page $parentCat ) {
+ global $wgVisitedCats;
+ $wgVisitedCats[] = $parentCat->getPageIdentifier()->getTitle()->getText();
+ };
+ $this->traverser->addCallback( CategoryTraverser::CALLBACK_CATEGORY, $callback );
+ $descendants = $this->traverser->descend( $catA );
+ $this->assertCount( 0, $descendants->toArray() );
+ $this->assertCount( 3, $wgVisitedCats );
+ $this->deletePages( [
+ 'Category:A cat',
+ 'Category:B cat',
+ 'Category:C cat',
+ 'Category:D cat',
+ ] );
+ }
+
+ /**
+ * Category loops are caught on descent.
+ *
+ * E
+ * / \
+ * F G
+ * / \
+ * H I
+ * |
+ * E <-- throw an Exception when we get to this repetition
+ *
+ */
+ public function testDescendIntoLoop() {
+ $catA = $this->savePage( 'Category:E cat', '[[Category:H cat]]' );
+ $catB = $this->savePage( 'Category:F cat', '[[Category:E cat]]' );
+ $catC = $this->savePage( 'Category:G cat', '[[Category:E cat]]' );
+ $catD = $this->savePage( 'Category:H cat', '[[Category:F cat]]' );
+ $catE = $this->savePage( 'Category:I cat', '[[Category:F cat]]' );
+ $this->testEnvironment->runJobs();
+ $haveCaught = false;
+ try {
+ $this->traverser->descend( $catA );
+ } catch ( CategoryLoopException $ex ) {
+ $haveCaught = true;
+ $expectedCatLoop = [
+ 'Category:E cat',
+ 'Category:F cat',
+ 'Category:H cat',
+ ];
+ // Build a simplified representation of the thrown loop pages, to get around different
+ // revision IDs.
+ $actualCatLoop = [];
+ foreach ( $ex->getCategoryPath()->toArray() as $p ) {
+ $actualCatLoop[] = $p->getPageIdentifier()->getTitle()->getText();
+ }
+ $this->assertEquals( $expectedCatLoop, $actualCatLoop );
+ }
+ $this->assertTrue( $haveCaught );
+ $this->deletePages( [
+ 'Category:E cat',
+ 'Category:F cat',
+ 'Category:G cat',
+ 'Category:H cat',
+ 'Category:I cat',
+ ] );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/FileUploaderTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/FileUploaderTest.php
new file mode 100755
index 00000000..e805ee1d
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/FileUploaderTest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\ApiUser;
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\MediawikiFactory;
+use Mediawiki\Api\Service\FileUploader;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Title;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * Test the \Mediawiki\Api\Service\FileUploader class.
+ */
+class FileUploaderTest extends PHPUnit_Framework_TestCase {
+
+ /** @var MediawikiFactory */
+ protected $factory;
+
+ /** @var FileUploader */
+ protected $fileUploader;
+
+ /**
+ * Create a FileUploader to use in all these tests.
+ */
+ public function setup() {
+ parent::setup();
+ $testEnvironment = TestEnvironment::newDefault();
+ $this->factory = $testEnvironment->getFactory();
+ $this->fileUploader = $this->factory->newFileUploader();
+
+ // Log in as the sysop user. These credentials are referenced in docs/contributing.rst.
+ $localApiUser = new ApiUser( 'admin', 'admin123' );
+ $api = $testEnvironment->getApi();
+ $api->login( $localApiUser );
+ }
+
+ public function testUpload() {
+ $testPagename = uniqid( 'file-uploader-test-' ) . '.png';
+ $testTitle = new Title( 'File:'.$testPagename );
+
+ // Check that the file doesn't exist yet.
+ $testFile = $this->factory->newPageGetter()->getFromTitle( $testTitle );
+ $this->assertEquals( 0, $testFile->getPageIdentifier()->getId() );
+
+ // Upload a file.
+ $testFilename = dirname( __DIR__ ) . '/fixtures/blue ℳ𝒲β™₯π“Šπ“ƒπ’Ύπ’Έβ„΄π’Ήβ„―.png';
+ $uploaded = $this->fileUploader->upload( $testPagename, $testFilename, 'Testing',
+ null, null, true );
+ $this->assertTrue( $uploaded );
+
+ // Get the file again, and check that it exists this time.
+ $testFile2 = $this->factory->newPageGetter()->getFromTitle( $testTitle );
+ $this->assertGreaterThan( 0, $testFile2->getPageIdentifier()->getId() );
+ }
+
+ public function testUploadByChunks() {
+ $testPagename = uniqid( 'file-uploader-test-' ) . '.png';
+ $testTitle = new Title( 'File:'.$testPagename );
+
+ // Upload a 83725 byte file in 10k chunks.
+ $testFilename = dirname( __DIR__ ) . '/fixtures/blue ℳ𝒲β™₯π“Šπ“ƒπ’Ύπ’Έβ„΄π’Ήβ„―.png';
+ $this->fileUploader->setChunkSize( 1024 * 10 );
+ $uploaded = $this->fileUploader->upload( $testPagename, $testFilename, 'Testing',
+ null, null, true );
+ $this->assertTrue( $uploaded );
+
+ // Get the file again, and check that it exists this time.
+ $testFile2 = $this->factory->newPageGetter()->getFromTitle( $testTitle );
+ $this->assertGreaterThan( 0, $testFile2->getPageIdentifier()->getId() );
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/NamespaceGetterTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/NamespaceGetterTest.php
new file mode 100644
index 00000000..eea55f9f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/NamespaceGetterTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\Service\NamespaceGetter;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\NamespaceInfo;
+
+class NamespaceGetterTest extends \PHPUnit_Framework_TestCase {
+ public function testGetNamespaceByCanonicalNameReturnsNullIfNamespaceWasNotFound() {
+ $nsGetter = new NamespaceGetter( $this->getApi() );
+ $this->assertNull( $nsGetter->getNamespaceByCanonicalName( 'Dummy' ) );
+ }
+
+ public function testGetNamespaceByCanonicalNameReturnsNamespaceIfNamespaceWasFound() {
+ $nsGetter = new NamespaceGetter( $this->getApi() );
+ $expectedNamespace = new NamespaceInfo( 1, 'Talk', 'Diskussion', 'first-letter' );
+ $this->assertEquals( $expectedNamespace, $nsGetter->getNamespaceByCanonicalName( 'Talk' ) );
+ }
+
+ public function testGetNamespaceByNameTriesAllNames() {
+ $nsGetter = new NamespaceGetter( $this->getApi() );
+ $expectedNamespace = new NamespaceInfo( 1, 'Talk', 'Diskussion', 'first-letter' );
+ $this->assertEquals( $expectedNamespace, $nsGetter->getNamespaceByName( 'Talk' ) );
+ $this->assertEquals( $expectedNamespace, $nsGetter->getNamespaceByName( 'Diskussion' ) );
+ }
+
+ public function testGetNamespaceByNameTriesAliases() {
+ $nsGetter = new NamespaceGetter( $this->getApi() );
+ $expectedNamespace = new NamespaceInfo(
+ 3,
+ 'User talk',
+ 'Benutzer Diskussion',
+ 'first-letter',
+ null,
+ [ 'BD', 'Benutzerin Diskussion' ]
+ );
+ $this->assertEquals( $expectedNamespace, $nsGetter->getNamespaceByName(
+ 'Benutzerin Diskussion'
+ ) );
+ $this->assertEquals( $expectedNamespace, $nsGetter->getNamespaceByName( 'BD' ) );
+ }
+
+ public function testGetNamespacesReturnsAllNamespaces() {
+ $nsGetter = new NamespaceGetter( $this->getApi() );
+ $talkNamespace = new NamespaceInfo( 1, 'Talk', 'Diskussion', 'first-letter' );
+ $gadgetNamespace = new NamespaceInfo(
+ 2302,
+ 'Gadget definition',
+ 'Gadget-Definition',
+ 'case-sensitive',
+ 'GadgetDefinition'
+ );
+ $namespaces = $nsGetter->getNamespaces();
+ $this->assertCount( 27, $namespaces );
+ $this->assertArrayHasKey( 1, $namespaces );
+ $this->assertEquals( $talkNamespace, $namespaces[1] );
+ $this->assertArrayHasKey( 2302, $namespaces );
+ $this->assertEquals( $gadgetNamespace, $namespaces[2302] );
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject|MediawikiApi
+ */
+ private function getApi() {
+ $api = $this->getMockBuilder( MediawikiApi::class )->disableOriginalConstructor()->getMock();
+ $api->expects( $this->any() )
+ ->method( 'getRequest' )
+ ->with( $this->getRequest() )
+ ->willReturn( $this->getNamespaceFixture() );
+ return $api;
+ }
+
+ private function getRequest() {
+ return new SimpleRequest(
+ 'query', [
+ 'meta' => 'siteinfo',
+ 'siprop' => 'namespaces|namespacealiases'
+ ] );
+ }
+
+ private function getNamespaceFixture() {
+ return json_decode( file_get_contents( __DIR__ . '/../fixtures/namespaces.json' ), true );
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageIntegrationTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageIntegrationTest.php
new file mode 100644
index 00000000..673eac8f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageIntegrationTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\DataModel\Content;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Title;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * @author Addshore
+ */
+class PageIntegrationTest extends PHPUnit_Framework_TestCase {
+
+ /**
+ * @var PageIdentifier
+ */
+ private static $localPageIdentifier;
+
+ public static function setUpBeforeClass() {
+ parent::setUpBeforeClass();
+ $title = new Title( 'TestPage - ' . strval( time() ) );
+ self::$localPageIdentifier = new PageIdentifier( $title );
+ }
+
+ public function testCreatePage() {
+ $factory = TestEnvironment::newDefault()->getFactory();
+ $this->assertTrue(
+ $factory->newRevisionSaver()->save(
+ new Revision(
+ new Content( 'testCreatePage_content' ),
+ self::$localPageIdentifier
+ )
+ ),
+ 'Failed to Create Page ' . self::$localPageIdentifier->getTitle()->getText()
+ );
+ }
+
+ /**
+ * This is testGetPageUsingTitle as currently we only know the title
+ * @depends testCreatePage
+ */
+ public function testGetPageUsingTitle() {
+ $factory = TestEnvironment::newDefault()->getFactory();
+ $page = $factory->newPageGetter()->getFromPageIdentifier( self::$localPageIdentifier );
+ $this->assertTrue( is_int( $page->getPageIdentifier()->getId() ) );
+ $title = $page->getPageIdentifier()->getTitle();
+ $this->assertEquals( self::$localPageIdentifier->getTitle(), $title );
+ $content = $page->getRevisions()->getLatest()->getContent()->getData();
+ $this->assertEquals( 'testCreatePage_content', $content );
+ self::$localPageIdentifier = $page->getPageIdentifier();
+ }
+
+ /**
+ * @depends testGetPageUsingTitle
+ */
+ public function testGetPageUsingId() {
+ $factory = TestEnvironment::newDefault()->getFactory();
+ $page = $factory->newPageGetter()->getFromPageId( self::$localPageIdentifier->getId() );
+ $this->assertEquals( self::$localPageIdentifier->getId(), $page->getPageIdentifier()->getId() );
+ $title = $page->getPageIdentifier()->getTitle();
+ $this->assertEquals( self::$localPageIdentifier->getTitle(), $title );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageListGetterTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageListGetterTest.php
new file mode 100644
index 00000000..39a973a4
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageListGetterTest.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\DataModel\Content;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Title;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * Test the \Mediawiki\Api\Service\PageListGetter class.
+ */
+class PageListGetterTest extends PHPUnit_Framework_TestCase {
+
+ /** @var string */
+ private $emptyCatName = 'Category:Empty category';
+
+ /** @var string */
+ private $nonemptyCatName = 'Category:Test category';
+
+ /** @var \Mediawiki\Api\Service\PageListGetter */
+ private $pageListGetter;
+
+ /**
+ * Set up some test categories and pages.
+ */
+ public function setUp() {
+ $testEnvironment = TestEnvironment::newDefault();
+ $factory = $testEnvironment->getFactory();
+
+ // An empty category.
+ $emptyCat = new PageIdentifier( new Title( $this->emptyCatName ) );
+ $factory->newRevisionSaver()->save( new Revision( new Content( '' ), $emptyCat ) );
+
+ // A non-empty category.
+ $testCat = new PageIdentifier( new Title( $this->nonemptyCatName ) );
+ $factory->newRevisionSaver()->save( new Revision( new Content( '' ), $testCat ) );
+
+ // Some pages in the latter.
+ // (Count must exceed the default categorymember result set size of 10.)
+ $revisionSaver = $factory->newRevisionSaver();
+ for ( $i = 1; $i <= 35; $i++ ) {
+ $testCat = new PageIdentifier( new Title( "Test page $i" ) );
+ // Even pages link to Main Page, odd pages transclude {{test}}.
+ $mainPageLink = ( ( $i % 2 ) == 0 ) ? 'Go to [[Main Page]].' : 'This is a {{test}}.';
+ $content = new Content( "$mainPageLink\n\n[[$this->nonemptyCatName]]" );
+ $revisionSaver->save( new Revision( $content, $testCat ) );
+ }
+
+ // Run all jobs, to make sure everything is up to date.
+ $testEnvironment->runJobs();
+
+ $this->pageListGetter = $factory->newPageListGetter();
+ }
+
+ public function testGetPageListFromCategoryName() {
+ // The empty category.
+ $emptyCategory = $this->pageListGetter->getPageListFromCategoryName( $this->emptyCatName );
+ $this->assertCount( 0, $emptyCategory->toArray() );
+
+ // The nonempty category.
+ $testCategory = $this->pageListGetter->getPageListFromCategoryName( $this->nonemptyCatName );
+ $this->assertCount( 35, $testCategory->toArray() );
+ }
+
+ public function testGetPageListFromPageTransclusions() {
+ $linksHere = $this->pageListGetter->getPageListFromPageTransclusions( 'Template:Test' );
+ // Only odd-numbered test pages link to the 'Test' template.
+ $this->assertCount( 18, $linksHere->toArray() );
+ }
+
+ public function testGetFromWhatLinksHere() {
+ // Every even-numbered test page links to Main Page.
+ $mainPageLinks = $this->pageListGetter->getFromWhatLinksHere( 'Main Page' );
+ $this->assertCount( 17, $mainPageLinks->toArray() );
+
+ // Nothing links to 'Test page 4'.
+ $testPageLinks = $this->pageListGetter->getFromWhatLinksHere( 'Test page 4' );
+ $this->assertCount( 0, $testPageLinks->toArray() );
+ }
+
+ public function testGetFromPrefix() {
+ // Pages with this prefix should be test pages 1, & 10-15; i.e. 7 of them.
+ $testPages = $this->pageListGetter->getFromPrefix( 'Test page 1' );
+ $this->assertCount( 11, $testPages->toArray() );
+ }
+
+ public function testGetRandom() {
+ // Default is 1.
+ $randomPages1 = $this->pageListGetter->getRandom();
+ $this->assertCount( 1, $randomPages1->toArray() );
+
+ // 8 random pages.
+ $randomPages2 = $this->pageListGetter->getRandom( [ 'rnlimit' => 8 ] );
+ $this->assertCount( 8, $randomPages2->toArray() );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/TestEnvironment.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/TestEnvironment.php
new file mode 100644
index 00000000..f40c0cd9
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/TestEnvironment.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\Guzzle\ClientFactory;
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\MediawikiFactory;
+use Mediawiki\Api\SimpleRequest;
+use Monolog\Handler\StreamHandler;
+use Monolog\Logger;
+
+/**
+ * @author Addshore
+ */
+class TestEnvironment {
+
+ /** @var \Mediawiki\Api\MediawikiFactory */
+ private $factory;
+
+ /** @var MediawikiApi */
+ protected $api;
+
+ /**
+ * Get a new default test environment.
+ * @return TestEnvironment
+ */
+ public static function newDefault() {
+ return new self();
+ }
+
+ /**
+ * Set up the test environment by creating a new API object pointing to a
+ * MediaWiki installation on localhost (or elsewhere as specified by the
+ * MEDIAWIKI_API_URL environment variable).
+ */
+ public function __construct() {
+ $this->factory = new MediawikiFactory( $this->getApi() );
+ }
+
+ /**
+ * Get the MediawikiApi to test against, based on the MEDIAWIKI_API_URL environment variable.
+ * @return MediawikiApi
+ * @throws \Exception If the MEDIAWIKI_API_URL environment variable does not end in 'api.php'
+ */
+ public function getApi() {
+ if ( $this->api instanceof MediawikiApi ) {
+ return $this->api;
+ }
+ $apiUrl = getenv( 'MEDIAWIKI_API_URL' );
+ if ( empty( $apiUrl ) ) {
+ $apiUrl = 'http://localhost/w/api.php';
+ } elseif ( substr( $apiUrl, -7 ) !== 'api.php' ) {
+ $msg = "URL incorrect: $apiUrl"
+ . " (the MEDIAWIKI_API_URL environment variable should end in 'api.php')";
+ throw new \Exception( $msg );
+ }
+
+ // Log to a local file.
+ $logger = new Logger( 'mediawiki-api' );
+ $logFile = __DIR__ . '/../../log/mediawiki-api.log';
+ $logger->pushHandler( new StreamHandler( $logFile, Logger::DEBUG ) );
+
+ // Create and return the API object.
+ $this->api = new MediawikiApi( $apiUrl );
+ $this->api->setLogger( $logger );
+ return $this->api;
+ }
+
+ /**
+ * Get the MediaWiki factory.
+ *
+ * @return \Mediawiki\Api\MediawikiFactory The factory instance.
+ */
+ public function getFactory() {
+ return $this->factory;
+ }
+
+ /**
+ * Run all jobs in the queue. This only works if the MediaWiki installation has $wgJobRunRate
+ * set to greater than zero (for test-running, you should set it to something higher than 50).
+ * @todo This and TestEnvironment::getJobQueueLength() should probably not live here.
+ * @return void
+ */
+ public function runJobs() {
+ $reqestProps = [ 'meta' => 'siteinfo', 'siprop' => 'general' ];
+ $siteInfoRequest = new SimpleRequest( 'query', $reqestProps );
+ $out = $this->getApi()->getRequest( $siteInfoRequest );
+ $mainPageUrl = $out['query']['general']['base'];
+ $i = 0;
+ while ( $this->getJobQueueLength( $this->getApi() ) > 0 ) {
+ $i++;
+ $cf = new ClientFactory();
+ $cf->getClient()->get( $mainPageUrl );
+ if ($i == 10) {
+ // Give up if we've been looping too much. This is very arbitrary.
+ break;
+ }
+ }
+ }
+
+ /**
+ * Get the number of jobs currently in the queue.
+ * @todo This and TestEnvironment::runJobs() should probably not live here.
+ * @param MediawikiApi $api
+ * @return int
+ */
+ public function getJobQueueLength( MediawikiApi $api ) {
+ $req = new SimpleRequest( 'query', [
+ 'meta' => 'siteinfo',
+ 'siprop' => 'statistics',
+ ]
+ );
+ $out = $api->getRequest( $req );
+ return (int)$out['query']['statistics']['jobs'];
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/UserIntegrationTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/UserIntegrationTest.php
new file mode 100644
index 00000000..29221edb
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/UserIntegrationTest.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\ApiUser;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * @author Addshore
+ */
+class UserIntegrationTest extends PHPUnit_Framework_TestCase {
+
+ /**
+ * @var ApiUser
+ */
+ private static $localApiUser;
+
+ public static function setUpBeforeClass() {
+ parent::setUpBeforeClass();
+ $strTime = strval( time() );
+ self::$localApiUser = new ApiUser( 'TestUser - ' . strval( time() ), $strTime . '-pass' );
+ }
+
+ public function testCreateUser() {
+ $factory = TestEnvironment::newDefault()->getFactory();
+ $createResult = $factory->newUserCreator()->create(
+ self::$localApiUser->getUsername(),
+
+
+ self::$localApiUser->getPassword()
+ );
+ $this->assertTrue( $createResult );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/phpunit.xml.dist b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/phpunit.xml.dist
new file mode 100644
index 00000000..4a0040c3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/phpunit.xml.dist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- convertWarningsToExceptions is false as real API calls can return un expected warnings -->
+<phpunit
+ bootstrap="../../vendor/autoload.php"
+ colors="true"
+ convertWarningsToExceptions="false"
+ >
+ <testsuites>
+ <testsuite name="addwiki/mediawiki-api/integration">
+ <directory suffix="Test.php">.</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">./../../src</directory>
+ </whitelist>
+ </filter>
+</phpunit> \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/AnonymousGeneratorTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/AnonymousGeneratorTest.php
new file mode 100644
index 00000000..67d6e450
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/AnonymousGeneratorTest.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Mediawiki\Api\Test\Generator;
+
+use Mediawiki\Api\Generator\AnonymousGenerator;
+
+/**
+ * @author Addshore
+ *
+ * @covers \Mediawiki\Api\Generator\AnonymousGenerator
+ */
+class AnonymousGeneratorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testConstruction() {
+ $generator = new AnonymousGenerator( 'name', [ 'gfoo' => 'bar' ] );
+
+ $this->assertEquals(
+ [
+ 'generator' => 'name',
+ 'gfoo' => 'bar',
+ ],
+ $generator->getParams()
+ );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/FluentGeneratorTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/FluentGeneratorTest.php
new file mode 100644
index 00000000..bd7cb0bf
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/FluentGeneratorTest.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Mediawiki\Api\Test\Generator;
+
+use Mediawiki\Api\Generator\FluentGenerator;
+
+/**
+ * @author Addshore
+ *
+ * @covers \Mediawiki\Api\Generator\FluentGenerator
+ */
+class FluentGeneratorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testConstructionWithNoGPrefix() {
+ $generator = new FluentGenerator( 'name' );
+ $generator->set( 'foo', 'bar' );
+
+ $this->assertEquals(
+ [
+ 'generator' => 'name',
+ 'gfoo' => 'bar',
+ ],
+ $generator->getParams()
+ );
+ }
+
+ public function testConstructionWithGPrefix() {
+ $generator = new FluentGenerator( 'name' );
+ $generator->set( 'gfoo', 'bar' );
+
+ $this->assertEquals(
+ [
+ 'generator' => 'name',
+ 'gfoo' => 'bar',
+ ],
+ $generator->getParams()
+ );
+ }
+
+ public function testFluidity() {
+ $generator = FluentGenerator::factory( 'name' )
+ ->set( 'foo', 'bar' )
+ ->set( 'gcat', 'meow' );
+
+ $this->assertEquals(
+ [
+ 'generator' => 'name',
+ 'gfoo' => 'bar',
+ 'gcat' => 'meow',
+ ],
+ $generator->getParams()
+ );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/MediawikiFactoryTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/MediawikiFactoryTest.php
new file mode 100644
index 00000000..c4ce958f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/MediawikiFactoryTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\MediawikiFactory;
+
+/**
+ * @covers Mediawiki\Api\MediawikiFactory
+ *
+ * @author Addshore
+ */
+class MediawikiFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function getMockMediawikiApi() {
+ return $this->getMockBuilder( 'Mediawiki\Api\MediawikiApi' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function provideFactoryMethodsTest() {
+ return [
+ [ 'Mediawiki\Api\Service\RevisionSaver', 'newRevisionSaver' ],
+ [ 'Mediawiki\Api\Service\RevisionUndoer', 'newRevisionUndoer' ],
+ [ 'Mediawiki\Api\Service\PageGetter', 'newPageGetter' ],
+ [ 'Mediawiki\Api\Service\UserGetter', 'newUserGetter' ],
+ [ 'Mediawiki\Api\Service\PageDeleter', 'newPageDeleter' ],
+ [ 'Mediawiki\Api\Service\PageMover', 'newPageMover' ],
+ [ 'Mediawiki\Api\Service\PageListGetter', 'newPageListGetter' ],
+ [ 'Mediawiki\Api\Service\PageRestorer', 'newPageRestorer' ],
+ [ 'Mediawiki\Api\Service\PagePurger', 'newPagePurger' ],
+ [ 'Mediawiki\Api\Service\RevisionRollbacker', 'newRevisionRollbacker' ],
+ [ 'Mediawiki\Api\Service\RevisionPatroller', 'newRevisionPatroller' ],
+ [ 'Mediawiki\Api\Service\PageProtector', 'newPageProtector' ],
+ [ 'Mediawiki\Api\Service\PageWatcher', 'newPageWatcher' ],
+ [ 'Mediawiki\Api\Service\RevisionDeleter', 'newRevisionDeleter' ],
+ [ 'Mediawiki\Api\Service\RevisionRestorer', 'newRevisionRestorer' ],
+ [ 'Mediawiki\Api\Service\UserBlocker', 'newUserBlocker' ],
+ [ 'Mediawiki\Api\Service\UserRightsChanger', 'newUserRightsChanger' ],
+ [ 'Mediawiki\Api\Service\UserCreator', 'newUserCreator' ],
+ [ 'Mediawiki\Api\Service\LogListGetter', 'newLogListGetter' ],
+ [ 'Mediawiki\Api\Service\FileUploader', 'newFileUploader' ],
+ [ 'Mediawiki\Api\Service\ImageRotator', 'newImageRotator' ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideFactoryMethodsTest
+ */
+ public function testFactoryMethod( $class, $method ) {
+ $factory = new MediawikiFactory( $this->getMockMediawikiApi() );
+ $this->assertInstanceOf( $class, $factory->$method() );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Service/PagePurgerTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Service/PagePurgerTest.php
new file mode 100644
index 00000000..38199c26
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Service/PagePurgerTest.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace Mediawiki\Api\Test\Service;
+
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\Service\PagePurger;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\Pages;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Title;
+use PHPUnit_Framework_MockObject_MockObject;
+
+/**
+ * @author Addshore
+ * @covers Mediawiki\Api\Service\PagePurger
+ */
+class PagePurgerTest extends \PHPUnit_Framework_TestCase {
+
+ private function getMockApi() {
+ /** @var MediawikiApi|PHPUnit_Framework_MockObject_MockObject $mock */
+ $mock = $this->getMockBuilder( '\Mediawiki\Api\MediawikiApi' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ return $mock;
+ }
+
+ public function testValidConstruction() {
+ new PagePurger( $this->getMockApi() );
+ $this->assertTrue( true );
+ }
+
+ public function testPurgePage() {
+ $api = $this->getMockApi();
+ $api->expects( $this->once() )
+ ->method( 'postRequest' )
+ ->with(
+ $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' )
+ )
+ ->will( $this->returnValue(
+ [
+ "batchcomplete" => "",
+ "purge" => [ [ "ns" => 0, "title" => "Foo", "purged" => "" ] ]
+ ] ) );
+
+ $service = new PagePurger( $api );
+
+ $page = new Page(
+ new PageIdentifier(
+ new Title( 'Foo', 0 ),
+ 123
+ )
+ );
+
+ $this->assertTrue( $service->purge( $page ) );
+ }
+
+ function testIncorrectPurgePage() {
+ $api = $this->getMockApi();
+ $api->expects( $this->once() )
+ ->method( 'postRequest' )
+ ->with(
+ $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' )
+ )
+ ->will( $this->returnValue( [
+ "batchcomplete" => "",
+ "purge" =>
+ [ [
+ "ns" => 0,
+ "title" => "This page really does not exist",
+ "missing" => ""
+ ] ]
+ ] ) );
+
+ $service = new PagePurger( $api );
+
+ $page = new Page(
+ new PageIdentifier(
+ new Title( 'Foo', 0 ),
+ 123
+ )
+ );
+
+ $this->assertFalse( $service->purge( $page ) );
+ }
+
+ public function testPurgePages() {
+ $api = $this->getMockApi();
+ $api->expects( $this->once() )
+ ->method( 'postRequest' )
+ ->with(
+ $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' )
+ )
+ ->will( $this->returnValue(
+ [
+ "batchcomplete" => "",
+ "purge" => [
+ [
+ "ns" => 0,
+ "title" => "Foo",
+ "purged" => ""
+ ],
+ [
+ "ns" => 0,
+ "title" => "Bar",
+ "purged" => ""
+ ],
+ ]
+ ]
+ ) );
+
+ $service = new PagePurger( $api );
+
+ $pages = new Pages( [
+ new Page(
+ new PageIdentifier(
+ new Title( 'Foo', 0 ),
+ 100
+ )
+ ), new Page(
+ new PageIdentifier(
+ new Title( 'Bar', 1 ),
+ 101
+ )
+ ) ] );
+
+ $this->assertEquals( $service->purgePages( $pages ), $pages );
+ }
+
+ function testIncorrectPurgePages() {
+ $api = $this->getMockApi();
+ $api->expects( $this->once() )
+ ->method( 'postRequest' )
+ ->with(
+ $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' )
+ )
+ ->will( $this->returnValue(
+ [
+ "batchcomplete" => "",
+ "purge" => [
+ [
+ "ns" => 0,
+ "title" => "Foo",
+ "purged" => ""
+ ],
+ [
+ "ns" => 0,
+ "title" => "Bar",
+ "purged" => ""
+ ],
+ [
+ "ns" => 0,
+ "title" => "This page really does not exist",
+ "missing" => ""
+ ],
+ ]
+ ]
+ ) );
+
+ $service = new PagePurger( $api );
+
+ $pages = new Pages( [
+ new Page(
+ new PageIdentifier(
+ new Title( 'Foo', 0 ),
+ 100
+ )
+ ), new Page(
+ new PageIdentifier(
+ new Title( 'Bar', 1 ),
+ 101
+ )
+ ), new Page(
+ new PageIdentifier(
+ new Title( 'MissingPage', 1 ),
+ 103
+ )
+ ) ] );
+
+ // MissingPage is not in the pages that are returned by purgePages
+ $pagesArray = $pages->toArray();
+ array_pop( $pagesArray );
+ $result = new Pages( $pagesArray );
+
+ $this->assertEquals( $service->purgePages( $pages ), $result );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/phpunit.xml.dist b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/phpunit.xml.dist
new file mode 100644
index 00000000..8f4cecef
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/phpunit.xml.dist
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit bootstrap="../../vendor/autoload.php" colors="true">
+ <testsuites>
+ <testsuite name="addwiki/mediawiki-api/unit">
+ <directory suffix="Test.php">.</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">./../../src</directory>
+ </whitelist>
+ </filter>
+</phpunit> \ No newline at end of file