Mozilla.) The new 'MonoBook' skin is not compatible with PHP 5 due to bugs in
the underlying PHPTAL library. It will be automatically disabled when running
on PHP5; the older look and feel will be used instead.
+
+= MediaWiki 1.2 =
+
+== MediaWiki 1.2.6, 2004-05-24 ==
+* Spam blocker ({{wg|SpamRegex}} - refuses to save edits that match)
+* Updated documentation about {{wg|WhitelistRead}}
+* Ensure that searchindex table is created as MyISAM
+* Interwiki cache timeout (memcached)
+* Fix uploads on Windows with magic_quotes_gpc
+* Some config fixes for Windows (slashes etc)
+* Local interwiki URL redirects
+* Fixed obscure deletion problem in squid mode on corrupt entries
+* Language files updated to remove more hard-coded "Wikipedia" strings
+
+== MediaWiki 1.2.5, 2004-05-03 ==
+* Fixed install problem with blank root password
+* Fixed Special:Emailuser/Username links
+* Fixed main-page edit links on fuzzy search results
+* Fixed wikipedia-interwiki.sql
+* Fixed install with apache2filter (ugly URLs)
+* IP in 'go' search brings up contributions
+* Switch from broken & to ? on top-level wiki URL hack
+* Fix for moved pages in enhanced Recentchanges
+* Initial main page on new installs links to the online documentation
+
+== MediaWiki 1.2.4, 2004-04-13 ==
+* Fixed edit toolbar in Mozilla
+* Diff links in Contributions for 'top' edits
+* Fixed Nostalgia skin drop-down for register_globals off
+* Backported optional open proxy blocker
+* Backported {{wg|WhitelistRead}}
+* {{wg|CapitalLinks}} option to force full case sensitivity in titles
+* Cleaned up error handling when can't talk to database
+* Disabled unsafe command-line installer (remove the <code>die()</code> call to
+use)
+
+== MediaWiki 1.2.3, 2004-04-02 ==
+* Fixed an in-place install bug with non-root MySQL user
+* Fixed history diff checkboxes bug on titles with ampersands
+* Fixed printable link bug on special pages with parameters
+* Fixed bug that broke IP blocking w/o memcached
+* Turns off E_NOTICE warnings if PHP settings have them on (you can grope in
+and turn this off if you like to debug)
+
+== MediaWiki 1.2.2, 2004-03-28 ==
+* Fixed an upgrade bug introduced in 1.2.1.
+* Disabled {{wg|UseCategoryMagic}}, which feature is incomplete broken
+
+== MediaWiki 1.2.1, 2004-03-27 ==
+Installation, compatibility, security fixlets:
+* Detect use of PHP as CGI and disable <code>index.php/Title</code> URLs
+* Try to auto-create math tmp & output directories if not present
+* Disable Asksql in default install ({{wg|AllowSysopQueries}})
+* Better handling of <code>get_magic_quotes_gpc</code> (apostrophe problems)
+* French localisation no longer hard-codes "Wikipedia" name
+
+== MediaWiki 1.2.0, 2004-03-24 ==
+This is the new production release; it is more or less in sync with what is
+running on Wikipedia right now. However this software is provided with NO
+WARRANTY of fitness for any purpose; there may be some interesting bugs, it may
+eat all your data, and documentation may not be up to date. New features in 1.2:
+* In-place web-based installation [experimental!] Note that maintenance
+functions are not yet available through the web install script.
+* Image resizing/thumbnail generation
+* Stricter upload file extension blacklist and whitelist options
+* More flexible blocking system; time period may be set
+* Handier sysop account management. An account marked "bureaucrat" may assign
+sysop access to other accounts via Special:Makesysop. (The exact details of
+this may change in the future)
+* Support for a squid cache with explicit purging of cached anon pages
+* Optional compression of old revision text (requires zlib support)
+* Fuzzy title search (experimental, requires memcached)
+* Page rendering cache (experimental)
+* Editing toolbar to demonstrate wiki syntax to newbies (off by default in user
+preferences)
+* Support for authenticated SMTP outgoing e-mail (experimental)
+* It's now possible to assign sysop accounts from within the wiki. An account
+with this ability must be labeled with the "bureaucrat" privilege, such as the
+'Developer' account created by the install. Fixes and tweaks:
+* Now works with register_globals off!
+* Should work out of the box on MySQL 3.2.x again. On 4.x set
+{{wg|EnablePersistentLC}}<code> = true;</code> to turn on the link cache table
+for a slight rendering speed boost.
+* Should work on PHP 5.0 beta (not thoroughly tested)
+* Works with short tags disabled.
+* rebuildMessages.php can now selectively update new messages, or overwrite
+everything.
+* Some layout fixes for RTL languages.
+* Now includes arrow icons for enhanced recent changes.
+* Various bug fixes.
+
+=== Behavior changes ===
+* wiki.phtml and redirect.phtml are now renamed to index.php and redirect.php
+The old names are provided too for compatibility, but make sure they don't
+conflict if you've been putting other files in your wiki.
+* Uploaded filenames are more strictly checked than before. See bits in
+DefaultSettings.php to tweak this behavior to your needs.
+* Database messages are now enabled by default, so the interface messages can
+be tweaked through the wiki with a sysop account. Disable this if you don't
+want the performance hit.
+
+=== Database changes ===
+An index was added to recentchanges table to speed up Newpages
+(patch-rc-newindex.sql for manual updaters). Expiration date field has been
+added to ipblocks table ({{manual|patch-ipb_expiry.sql}} for manual updaters).
+The links tables have slightly stricter indexes. ('links' and 'brokenlinks' are
+not changed on existing installations.)
+
+=== Known problems ===
+The version 1.1.0 LocalSettings.sample file included the setting
+{{wg|CategoryMagic}}<code> = true;</code> this setting is for an experimental
+feature that _does not work correctly_. If you have it left over, turn it off
+or you'll see mysterious problems with vanishing links. There may be problems
+with session handling on some systems. Checking the "remember my password" box
+may help as a temporary workaround. If you receive "Cannot load input file"
+errors when trying to get at the wiki after installation, make the following
+changes:
+ in LocalSettings.php change the line something like this:
+ {{wg|ArticlePath}} = "/wiki/index.php/$1";
+ to:
+ {{wg|ArticlePath}} = "/wiki/index.php?title=$1";
+ in index.php, remove these lines:
+ if( isset( $_SERVER['PATH_INFO'] ) ) {
+ $title = substr( $_SERVER['PATH_INFO'], 1 );
+ } else {
+ $title = $_REQUEST['title'];
+ }
+
+= MediaWiki 1.1 =
+
+== MediaWiki 1.1.0, 2003-12-08 ==
+
+This is the new production release. Any following 1.1.x releases are expected
+to contain only bug fixes; developments of new features will go towards a 1.2.0
+release.
+New features in 1.1:
+* New wiki table syntax:
+http://meta.wikipedia.org/wiki/MediaWiki_User%27s_Guide:_Using_tables
+* User-editable interface messages:
+http://meta.wikipedia.org/wiki/MediaWiki_namespace
+* XML-wrapped page source export with optional history:
+http://meta.wikipedia.org/wiki/XML_import_and_export (There is not yet an
+import function!)
+* "Magic words" Fixes and tweaks:
+* linkscc table caches link data for rendering; faster
+{{manual|rebuildlinks.php}}
+* Numerous bugs in [[skin:Cologne Blue|Cologne Blue]] skin fixed
+* Login gives warning about missing cookies
+* Block log, protection log added; deletion log now includes undeletions
+* Deletion & upload logs now escape comment text properly
+* Problems with <nowiki><nowiki></nowiki> segments in section titles etc
+mitigated
+* Contributions offset and minor edit bugs fixed
+* Whatlinkshere now sorted alphabetically
+* Various exciting new profiling options.
+* Debug log is off by default.
+* Various small bugs fixed. Internal changes:
+* wfQuery has had a second parameter inserted, DB_READ or DB_WRITE. This value
+is not actually used so far.
+* Partial code for categories and Smarty template-based skins is in the tree
+but disabled.
+* Parts of Article.php have been moved to {{manual|EditPage.php}} and
+{{manual|ImagePage.php}}. New translations:
+* fi - Finnish
+* ia - Interlingua
+* no - Norwegian
+* sk - Slovak
+* ta - Tamil
+
+=== Database changes ===
+"linkscc" table added. If upgrading manually (rather than with
+{{manual|update.php}}), run maintenance/archives/patch-linkscc.sql to create
+the table. Older releases were dated snapshots from the old 'stable' branch:
+
+= pre-MediaWiki 1.1.0 =
+
+== Mediawiki-20031118 ==
+* Image deletion fixed.
+* Deletion of image old revisions now restricted to sysops (this is an
+irreversible action and not well logged)
+* Fixed maintenance scripts broken by last release's security fix
+* Many errors in {{manual|rebuildlinks.php|rebuildlinks}} script fixed.
+
+== Mediawiki-20031117 ==
+* SECURITY FIX: stricter checking of include path
+* Fixed user contributions next/prev bug
+* Login cookies now have the database name prefixed to allow wikis to coexist
+in the same domain. This will invalidate any old saved password cookies.
+* Update cache timestamp when talk pages are created
+* Saving the login form in Mozilla no longer blanks password in prefs.
+* Check existence of source page before performing a move.
+* Detect invalid titles in Special:Allpages
+* Q-encode headers on outgoing inter-user e-mail
+* Updates to some translations.
+* Added table of contents border/bg to Cologne Blue, Nostalgia skins
+* Protected pages no longer appear unprotected when visited via redirect
+* Swapped old Wikipedia logo for the MediaWiki sunflower logo
+* install.php, update.php print warning on old PHP versions, added
+compatibility functions that might or might not help No database changes since
+20031107; upgrading should be clean.
+
+== Mediawiki-20031107 ==
+* Fixed various bugs!
+* Some speed improvements from tweaks to the table indexes
+* Limited support for memcached (see below)
+* New translations (see below)
+* Interwiki link data now kept in database for flexibility
+* Friendlier read-only source view if asked to edit a page when the db is
+locked or the page is protected.
+* Normal IP blocks auto-expire after 24 hours
+* Optional support for blocking usernames
+* Uploads disabled by default (see below)
+
+== Mediawiki-20030829 ==
+First release under MediaWiki name.
+
+=== Security note ===
+Uploads are now disabled by default. If you've set up a secure configuration
+you can reenable uploads by putting: $wgDisableUploads = false;
+into LocalSettings.php. Earlier versions of MediaWiki included a bug that
+potentially allows logged- in users to delete arbitrary files in directories
+writable by the web server user by manually feeding false form data; this is
+now fixed. As a reminder, disable PHP script execution in the upload directory!
+You may also wish to serve HTML pages as plaintext to prevent cookie- stealing
+JavaScript attacks. Example Apache config fragment:
+<pre>
+<Directory "/Library/MediaWiki/web/upload">
+ # Ignore .htaccess files
+ AllowOverride None
+
+ # Serve HTML as plaintext
+ AddType text/plain .html .htm .shtml
+
+ # Don't run arbitrary PHP code.
+ php_admin_flag engine off
+
+ # If you've other scripting languages, disable them too.
+</Directory>
+</pre>
+
+=== Database updates ===
+If you're using {{manual|update.php}}, the necessary database changes should be
+made automatically. To manually upgrade your database from the 2003-08-29
+release, run the following SQL scripts from the maintenance subdirectory:
+archives/patch-ipblocks.sql archives/patch-interwiki.sql
+archives/patch-indexes.sql interwiki.sql To copy in the Wikipedia
+language-prefix interwikis as well, add: wikipedia-interwiki.sql
+
+=== Translations ===
+New interface localization files are included for:
+*fy - Frisian
+*ro - Romanian
+*sl - Slovene
+*sq - Albanian
+*sr - Serbian
+
+=== Memcached ===
+Memcached is a distributed cache system. See http://www.danga.com/memcached/
+MediaWiki can optionally use memcached to store some data between calls to
+reduce load on the database. Currently this is limited to user and talk page
+notification data, interwiki prefix/URL matches, and the UTF-8 conversion
+tables. MediaWiki includes version 1.0.10 of the (GPL'd) PHP memcached client
+by Ryan Gilfether; if memcached is disabled it acts as a dummy object with
+minimal overhead. To use memcached you'll need PHP installed with sockets
+support (this is not in the default configure options). See docs/memcached for
+some more details. Additionally, you can store login session data in memcached
+instead of the local filesystem, which can help to enable load-balancing by
+letting login sessions transparently work on multiple front-end web servers.
+(The primary other issue is with uploads, which requires some care in
+handling.) To enable this, set $wgSessionsInMemcached = true; and set
+$wgCookieDomain appropriately if exposing multiple hostnames. This system is
+new and may be volatile; login sessions will fail dramatically if memcached is
+unavailable when this option is turned on.
+
+=== Online documentation ===
+Documentation for both end-users and site administrators is currently being
+built up on Meta-Wikipedia, and is covered under the GNU Free Documentation
+License: http://meta.wikipedia.org/wiki/MediaWiki_User%27s_Guide
+
+=== Mailing list ===
+A MediaWiki-l mailing list has been set up distinct from the Wikipedia
+wikitech-l list: http://mail.wikipedia.org/mailman/listinfo/mediawiki-l
+
+=== UseModWiki import script ===
+A stripped-down UseModWiki import script is available in the maintenance
+subdirectory. It is incomplete and requires a lot of manual clean-up, but does
+function for the brave and pure of heart.
+
+=== Test suite removed ===
+The unmaintained Java-based test suite has been removed from the tarball
+release. If you really want it you can check it out from CVS.
'WikiTextStructure' => __DIR__ . '/includes/content/WikiTextStructure.php',
'Wikimedia\\Http\\HttpAcceptNegotiator' => __DIR__ . '/includes/libs/http/HttpAcceptNegotiator.php',
'Wikimedia\\Http\\HttpAcceptParser' => __DIR__ . '/includes/libs/http/HttpAcceptParser.php',
- 'Wikimedia\\Rdbms\\AtomicSectionIdentifier' => __DIR__ . '/includes/libs/rdbms/database/AtomicSectionIdentifier.php',
+ 'Wikimedia\\Rdbms\\AtomicSectionIdentifier' => __DIR__ . '/includes/libs/rdbms/database/utils/AtomicSectionIdentifier.php',
'Wikimedia\\Rdbms\\Blob' => __DIR__ . '/includes/libs/rdbms/encasing/Blob.php',
'Wikimedia\\Rdbms\\ChronologyProtector' => __DIR__ . '/includes/libs/rdbms/ChronologyProtector.php',
'Wikimedia\\Rdbms\\ConnectionManager' => __DIR__ . '/includes/libs/rdbms/connectionmanager/ConnectionManager.php',
'Wikimedia\\Rdbms\\DBTransactionStateError' => __DIR__ . '/includes/libs/rdbms/exception/DBTransactionStateError.php',
'Wikimedia\\Rdbms\\DBUnexpectedError' => __DIR__ . '/includes/libs/rdbms/exception/DBUnexpectedError.php',
'Wikimedia\\Rdbms\\Database' => __DIR__ . '/includes/libs/rdbms/database/Database.php',
- 'Wikimedia\\Rdbms\\DatabaseDomain' => __DIR__ . '/includes/libs/rdbms/database/DatabaseDomain.php',
+ 'Wikimedia\\Rdbms\\DatabaseDomain' => __DIR__ . '/includes/libs/rdbms/database/domain/DatabaseDomain.php',
'Wikimedia\\Rdbms\\DatabaseMssql' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMssql.php',
'Wikimedia\\Rdbms\\DatabaseMysqlBase' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysqlBase.php',
'Wikimedia\\Rdbms\\DatabaseMysqli' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysqli.php',
}
}
+ /**
+ * @param WebRequest $request
+ * @return string
+ */
+ private function getTimestampFromRequest( WebRequest $request ) {
+ // Backwards compatibility checks for URIs with only year and/or month.
+ $year = $request->getInt( 'year' );
+ $month = $request->getInt( 'month' );
+ $day = null;
+ if ( $year !== 0 || $month !== 0 ) {
+ if ( $year === 0 ) {
+ $year = MWTimestamp::getLocalInstance()->format( 'Y' );
+ }
+ if ( $month < 1 || $month > 12 ) {
+ // month is invalid so treat as December (all months)
+ $month = 12;
+ }
+ // month is valid so check day
+ $day = cal_days_in_month( CAL_GREGORIAN, $month, $year );
+
+ // Left pad the months and days
+ $month = str_pad( $month, 2, "0", STR_PAD_LEFT );
+ $day = str_pad( $day, 2, "0", STR_PAD_LEFT );
+ }
+
+ $before = $request->getVal( 'date-range-to' );
+ if ( $before ) {
+ $parts = explode( '-', $before );
+ $year = $parts[0];
+ // check date input is valid
+ if ( count( $parts ) === 3 ) {
+ $month = $parts[1];
+ $day = $parts[2];
+ }
+ }
+ return $year && $month && $day ? $year . '-' . $month . '-' . $day : '';
+ }
/**
* Print the history page for an article.
*/
return;
}
- /**
- * Add date selector to quickly get to a certain time
- */
- $year = $request->getInt( 'year' );
- $month = $request->getInt( 'month' );
+ $ts = $this->getTimestampFromRequest( $request );
$tagFilter = $request->getVal( 'tagfilter' );
- $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter, false, $this->getContext() );
/**
* Option to show only revisions that have been (partially) hidden via RevisionDelete
} else {
$conds = [];
}
+
+ // Add the general form.
+ $action = htmlspecialchars( wfScript() );
+ $fields = [
+ [
+ 'name' => 'title',
+ 'type' => 'hidden',
+ 'default' => $this->getTitle()->getPrefixedDBkey(),
+ ],
+ [
+ 'name' => 'action',
+ 'type' => 'hidden',
+ 'default' => 'history',
+ ],
+ [
+ 'type' => 'date',
+ 'default' => $ts,
+ 'label' => $this->msg( 'date-range-to' )->text(),
+ 'name' => 'date-range-to',
+ ],
+ [
+ 'label-raw' => $this->msg( 'tag-filter' )->parse(),
+ 'type' => 'tagfilter',
+ 'id' => 'tagfilter',
+ 'name' => 'tagfilter',
+ 'value' => $tagFilter,
+ ]
+ ];
if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) {
- $checkDeleted = Xml::checkLabel( $this->msg( 'history-show-deleted' )->text(),
- 'deleted', 'mw-show-deleted-only', $request->getBool( 'deleted' ) ) . "\n";
- } else {
- $checkDeleted = '';
+ $fields[] = [
+ 'type' => 'check',
+ 'label' => $this->msg( 'history-show-deleted' )->text(),
+ 'default' => $request->getBool( 'deleted' ),
+ 'name' => 'deleted',
+ ];
}
- // Add the general form
- $action = htmlspecialchars( wfScript() );
- $content = Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . "\n";
- $content .= Html::hidden( 'action', 'history' ) . "\n";
- $content .= Xml::dateMenu(
- ( $year == null ? MWTimestamp::getLocalInstance()->format( 'Y' ) : $year ),
- $month
- ) . "\u{00A0}";
- $content .= $tagSelector ? ( implode( "\u{00A0}", $tagSelector ) . "\u{00A0}" ) : '';
- $content .= $checkDeleted . Html::submitButton(
- $this->msg( 'historyaction-submit' )->text(),
- [],
- [ 'mw-ui-progressive' ]
- );
- $out->addHTML(
- "<form action=\"$action\" method=\"get\" id=\"mw-history-searchform\">" .
- Xml::fieldset(
- $this->msg( 'history-fieldset-title' )->text(),
- $content,
- [ 'id' => 'mw-history-search' ]
- ) .
- '</form>'
- );
+ $out->enableOOUI();
+ $htmlForm = HTMLForm::factory( 'ooui', $fields, $this->getContext() );
+ $htmlForm
+ ->setMethod( 'get' )
+ ->setAction( $action )
+ ->setId( 'mw-history-searchform' )
+ ->setSubmitText( $this->msg( 'historyaction-submit' )->text() )
+ ->setWrapperLegend( $this->msg( 'history-fieldset-title' )->text() );
+ $htmlForm->loadData();
+
+ $out->addHTML( $htmlForm->getHTML( false ) );
Hooks::run( 'PageHistoryBeforeList', [ &$this->page, $this->getContext() ] );
// Create and output the list.
- $pager = new HistoryPager( $this, $year, $month, $tagFilter, $conds );
+ $dateComponents = explode( '-', $ts );
+ if ( count( $dateComponents ) > 1 ) {
+ $y = $dateComponents[0];
+ $m = $dateComponents[1];
+ $d = $dateComponents[2];
+ } else {
+ $y = '';
+ $m = '';
+ $d = '';
+ }
+ $pager = new HistoryPager( $this, $y, $m, $tagFilter, $conds, $d );
$out->addHTML(
$pager->getNavigationBar() .
$pager->getBody() .
* @param string $month
* @param string $tagFilter
* @param array $conds
+ * @param string $day
*/
public function __construct(
HistoryAction $historyPage,
$year = '',
$month = '',
$tagFilter = '',
- array $conds = []
+ array $conds = [],
+ $day = ''
) {
parent::__construct( $historyPage->getContext() );
$this->historyPage = $historyPage;
$this->tagFilter = $tagFilter;
- $this->getDateCond( $year, $month );
+ $this->getDateCond( $year, $month, $day );
$this->conds = $conds;
$this->showTagEditUI = ChangeTags::showTagEditingUI( $this->getUser() );
}
+++ /dev/null
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Database
- */
-namespace Wikimedia\Rdbms;
-
-/**
- * Class used for token representing identifiers for atomic sections from IDatabase instances
- */
-class AtomicSectionIdentifier {
-}
+++ /dev/null
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Database
- */
-namespace Wikimedia\Rdbms;
-
-use InvalidArgumentException;
-
-/**
- * Class to handle database/prefix specification for IDatabase domains
- */
-class DatabaseDomain {
- /** @var string|null */
- private $database;
- /** @var string|null */
- private $schema;
- /** @var string */
- private $prefix;
-
- /** @var string Cache of convertToString() */
- private $equivalentString;
-
- /**
- * @param string|null $database Database name
- * @param string|null $schema Schema name
- * @param string $prefix Table prefix
- */
- public function __construct( $database, $schema, $prefix ) {
- if ( $database !== null && ( !is_string( $database ) || $database === '' ) ) {
- throw new InvalidArgumentException( 'Database must be null or a non-empty string.' );
- }
- $this->database = $database;
- if ( $schema !== null && ( !is_string( $schema ) || $schema === '' ) ) {
- throw new InvalidArgumentException( 'Schema must be null or a non-empty string.' );
- }
- $this->schema = $schema;
- if ( !is_string( $prefix ) ) {
- throw new InvalidArgumentException( 'Prefix must be a string.' );
- } elseif ( $prefix !== '' && substr( $prefix, -1, 1 ) !== '_' ) {
- throw new InvalidArgumentException( 'A non-empty prefix must end with "_".' );
- }
- $this->prefix = $prefix;
- }
-
- /**
- * @param DatabaseDomain|string $domain Result of DatabaseDomain::toString()
- * @return DatabaseDomain
- */
- public static function newFromId( $domain ) {
- if ( $domain instanceof self ) {
- return $domain;
- }
-
- $parts = array_map( [ __CLASS__, 'decode' ], explode( '-', $domain ) );
-
- $schema = null;
- $prefix = '';
-
- if ( count( $parts ) == 1 ) {
- $database = $parts[0];
- } elseif ( count( $parts ) == 2 ) {
- list( $database, $prefix ) = $parts;
- } elseif ( count( $parts ) == 3 ) {
- list( $database, $schema, $prefix ) = $parts;
- } else {
- throw new InvalidArgumentException( "Domain '$domain' has too few or too many parts." );
- }
-
- if ( $database === '' ) {
- $database = null;
- }
-
- if ( $schema === '' ) {
- $schema = null;
- }
-
- return new self( $database, $schema, $prefix );
- }
-
- /**
- * @return DatabaseDomain
- */
- public static function newUnspecified() {
- return new self( null, null, '' );
- }
-
- /**
- * @param DatabaseDomain|string $other
- * @return bool Whether the domain instances are the same by value
- */
- public function equals( $other ) {
- if ( $other instanceof self ) {
- return (
- $this->database === $other->database &&
- $this->schema === $other->schema &&
- $this->prefix === $other->prefix
- );
- }
-
- return ( $this->getId() === $other );
- }
-
- /**
- * Check whether the domain $other meets the specifications of this domain
- *
- * If this instance has a null database specifier, then $other can have any database
- * specified, including the null, and likewise if the schema specifier is null. This
- * is not transitive like equals() since a domain that explicitly wants a certain
- * database or schema cannot be satisfied by one of another (nor null). If the prefix
- * is empty and the DB and schema are both null, then the entire domain is considered
- * unspecified, and any prefix of $other is considered compatible.
- *
- * @param DatabaseDomain|string $other
- * @return bool
- * @since 1.32
- */
- public function isCompatible( $other ) {
- if ( $this->isUnspecified() ) {
- return true; // even the prefix doesn't matter
- }
-
- $other = self::newFromId( $other );
-
- return (
- ( $this->database === $other->database || $this->database === null ) &&
- ( $this->schema === $other->schema || $this->schema === null ) &&
- $this->prefix === $other->prefix
- );
- }
-
- /**
- * @return bool
- * @since 1.32
- */
- public function isUnspecified() {
- return (
- $this->database === null && $this->schema === null && $this->prefix === ''
- );
- }
-
- /**
- * @return string|null Database name
- */
- public function getDatabase() {
- return $this->database;
- }
-
- /**
- * @return string|null Database schema
- */
- public function getSchema() {
- return $this->schema;
- }
-
- /**
- * @return string Table prefix
- */
- public function getTablePrefix() {
- return $this->prefix;
- }
-
- /**
- * @return string
- */
- public function getId() {
- if ( $this->equivalentString === null ) {
- $this->equivalentString = $this->convertToString();
- }
-
- return $this->equivalentString;
- }
-
- /**
- * @return string
- */
- private function convertToString() {
- $parts = [ (string)$this->database ];
- if ( $this->schema !== null ) {
- $parts[] = $this->schema;
- }
- if ( $this->prefix != '' || $this->schema !== null ) {
- // If there is a schema, then we need the prefix to disambiguate.
- // For engines like Postgres that use schemas, this awkwardness is hopefully
- // avoided since it is easy to have one DB per server (to avoid having many users)
- // and use schema/prefix to have wiki farms. For example, a domain schemes could be
- // wiki-<project>-<language>, e.g. "wiki-fitness-es"/"wiki-sports-fr"/"wiki-news-en".
- $parts[] = $this->prefix;
- }
-
- return implode( '-', array_map( [ __CLASS__, 'encode' ], $parts ) );
- }
-
- private static function encode( $decoded ) {
- $encoded = '';
-
- $length = strlen( $decoded );
- for ( $i = 0; $i < $length; ++$i ) {
- $char = $decoded[$i];
- if ( $char === '-' ) {
- $encoded .= '?h';
- } elseif ( $char === '?' ) {
- $encoded .= '??';
- } else {
- $encoded .= $char;
- }
- }
-
- return $encoded;
- }
-
- private static function decode( $encoded ) {
- $decoded = '';
-
- $length = strlen( $encoded );
- for ( $i = 0; $i < $length; ++$i ) {
- $char = $encoded[$i];
- if ( $char === '?' ) {
- $nextChar = $encoded[$i + 1] ?? null;
- if ( $nextChar === 'h' ) {
- $decoded .= '-';
- ++$i;
- } elseif ( $nextChar === '?' ) {
- $decoded .= '?';
- ++$i;
- } else {
- $decoded .= $char;
- }
- } else {
- $decoded .= $char;
- }
- }
-
- return $decoded;
- }
-
- /**
- * @return string
- */
- function __toString() {
- return $this->getId();
- }
-}
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Database
+ */
+namespace Wikimedia\Rdbms;
+
+use InvalidArgumentException;
+
+/**
+ * Class to handle database/prefix specification for IDatabase domains
+ */
+class DatabaseDomain {
+ /** @var string|null */
+ private $database;
+ /** @var string|null */
+ private $schema;
+ /** @var string */
+ private $prefix;
+
+ /** @var string Cache of convertToString() */
+ private $equivalentString;
+
+ /**
+ * @param string|null $database Database name
+ * @param string|null $schema Schema name
+ * @param string $prefix Table prefix
+ */
+ public function __construct( $database, $schema, $prefix ) {
+ if ( $database !== null && ( !is_string( $database ) || $database === '' ) ) {
+ throw new InvalidArgumentException( 'Database must be null or a non-empty string.' );
+ }
+ $this->database = $database;
+ if ( $schema !== null && ( !is_string( $schema ) || $schema === '' ) ) {
+ throw new InvalidArgumentException( 'Schema must be null or a non-empty string.' );
+ }
+ $this->schema = $schema;
+ if ( !is_string( $prefix ) ) {
+ throw new InvalidArgumentException( 'Prefix must be a string.' );
+ } elseif ( $prefix !== '' && substr( $prefix, -1, 1 ) !== '_' ) {
+ throw new InvalidArgumentException( 'A non-empty prefix must end with "_".' );
+ }
+ $this->prefix = $prefix;
+ }
+
+ /**
+ * @param DatabaseDomain|string $domain Result of DatabaseDomain::toString()
+ * @return DatabaseDomain
+ */
+ public static function newFromId( $domain ) {
+ if ( $domain instanceof self ) {
+ return $domain;
+ }
+
+ $parts = array_map( [ __CLASS__, 'decode' ], explode( '-', $domain ) );
+
+ $schema = null;
+ $prefix = '';
+
+ if ( count( $parts ) == 1 ) {
+ $database = $parts[0];
+ } elseif ( count( $parts ) == 2 ) {
+ list( $database, $prefix ) = $parts;
+ } elseif ( count( $parts ) == 3 ) {
+ list( $database, $schema, $prefix ) = $parts;
+ } else {
+ throw new InvalidArgumentException( "Domain '$domain' has too few or too many parts." );
+ }
+
+ if ( $database === '' ) {
+ $database = null;
+ }
+
+ if ( $schema === '' ) {
+ $schema = null;
+ }
+
+ return new self( $database, $schema, $prefix );
+ }
+
+ /**
+ * @return DatabaseDomain
+ */
+ public static function newUnspecified() {
+ return new self( null, null, '' );
+ }
+
+ /**
+ * @param DatabaseDomain|string $other
+ * @return bool Whether the domain instances are the same by value
+ */
+ public function equals( $other ) {
+ if ( $other instanceof self ) {
+ return (
+ $this->database === $other->database &&
+ $this->schema === $other->schema &&
+ $this->prefix === $other->prefix
+ );
+ }
+
+ return ( $this->getId() === $other );
+ }
+
+ /**
+ * Check whether the domain $other meets the specifications of this domain
+ *
+ * If this instance has a null database specifier, then $other can have any database
+ * specified, including the null, and likewise if the schema specifier is null. This
+ * is not transitive like equals() since a domain that explicitly wants a certain
+ * database or schema cannot be satisfied by one of another (nor null). If the prefix
+ * is empty and the DB and schema are both null, then the entire domain is considered
+ * unspecified, and any prefix of $other is considered compatible.
+ *
+ * @param DatabaseDomain|string $other
+ * @return bool
+ * @since 1.32
+ */
+ public function isCompatible( $other ) {
+ if ( $this->isUnspecified() ) {
+ return true; // even the prefix doesn't matter
+ }
+
+ $other = self::newFromId( $other );
+
+ return (
+ ( $this->database === $other->database || $this->database === null ) &&
+ ( $this->schema === $other->schema || $this->schema === null ) &&
+ $this->prefix === $other->prefix
+ );
+ }
+
+ /**
+ * @return bool
+ * @since 1.32
+ */
+ public function isUnspecified() {
+ return (
+ $this->database === null && $this->schema === null && $this->prefix === ''
+ );
+ }
+
+ /**
+ * @return string|null Database name
+ */
+ public function getDatabase() {
+ return $this->database;
+ }
+
+ /**
+ * @return string|null Database schema
+ */
+ public function getSchema() {
+ return $this->schema;
+ }
+
+ /**
+ * @return string Table prefix
+ */
+ public function getTablePrefix() {
+ return $this->prefix;
+ }
+
+ /**
+ * @return string
+ */
+ public function getId() {
+ if ( $this->equivalentString === null ) {
+ $this->equivalentString = $this->convertToString();
+ }
+
+ return $this->equivalentString;
+ }
+
+ /**
+ * @return string
+ */
+ private function convertToString() {
+ $parts = [ (string)$this->database ];
+ if ( $this->schema !== null ) {
+ $parts[] = $this->schema;
+ }
+ if ( $this->prefix != '' || $this->schema !== null ) {
+ // If there is a schema, then we need the prefix to disambiguate.
+ // For engines like Postgres that use schemas, this awkwardness is hopefully
+ // avoided since it is easy to have one DB per server (to avoid having many users)
+ // and use schema/prefix to have wiki farms. For example, a domain schemes could be
+ // wiki-<project>-<language>, e.g. "wiki-fitness-es"/"wiki-sports-fr"/"wiki-news-en".
+ $parts[] = $this->prefix;
+ }
+
+ return implode( '-', array_map( [ __CLASS__, 'encode' ], $parts ) );
+ }
+
+ private static function encode( $decoded ) {
+ $encoded = '';
+
+ $length = strlen( $decoded );
+ for ( $i = 0; $i < $length; ++$i ) {
+ $char = $decoded[$i];
+ if ( $char === '-' ) {
+ $encoded .= '?h';
+ } elseif ( $char === '?' ) {
+ $encoded .= '??';
+ } else {
+ $encoded .= $char;
+ }
+ }
+
+ return $encoded;
+ }
+
+ private static function decode( $encoded ) {
+ $decoded = '';
+
+ $length = strlen( $encoded );
+ for ( $i = 0; $i < $length; ++$i ) {
+ $char = $encoded[$i];
+ if ( $char === '?' ) {
+ $nextChar = $encoded[$i + 1] ?? null;
+ if ( $nextChar === 'h' ) {
+ $decoded .= '-';
+ ++$i;
+ } elseif ( $nextChar === '?' ) {
+ $decoded .= '?';
+ ++$i;
+ } else {
+ $decoded .= $char;
+ }
+ } else {
+ $decoded .= $char;
+ }
+ }
+
+ return $decoded;
+ }
+
+ /**
+ * @return string
+ */
+ function __toString() {
+ return $this->getId();
+ }
+}
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Database
+ */
+namespace Wikimedia\Rdbms;
+
+/**
+ * Class used for token representing identifiers for atomic sections from IDatabase instances
+ */
+class AtomicSectionIdentifier {
+}
use MediaWiki\Auth\AuthenticationRequest;
use MediaWiki\User\UserIdentity;
use MediaWiki\Logger\LoggerFactory;
+use Wikimedia\Assert\Assert;
use Wikimedia\IPSet;
use Wikimedia\ScopedCallback;
use Wikimedia\Rdbms\Database;
}
}
+ /** @var array|null */
+ private static $defOpt = null;
+ /** @var string|null */
+ private static $defOptLang = null;
+
+ /**
+ * Reset the process cache of default user options. This is only necessary
+ * if the wiki configuration has changed since defaults were calculated,
+ * and as such should only be performed inside the testing suite that
+ * regularly changes wiki configuration.
+ */
+ public static function resetGetDefaultOptionsForTestsOnly() {
+ Assert::invariant( defined( 'MW_PHPUNIT_TEST' ), 'Unit tests only' );
+ self::$defOpt = null;
+ self::$defOptLang = null;
+ }
+
/**
* Combine the language default options with any site-specific options
* and add the default language variants.
public static function getDefaultOptions() {
global $wgNamespacesToBeSearchedDefault, $wgDefaultUserOptions, $wgDefaultSkin;
- static $defOpt = null;
- static $defOptLang = null;
-
$contLang = MediaWikiServices::getInstance()->getContentLanguage();
- if ( $defOpt !== null && $defOptLang === $contLang->getCode() ) {
+ if ( self::$defOpt !== null && self::$defOptLang === $contLang->getCode() ) {
// The content language does not change (and should not change) mid-request, but the
// unit tests change it anyway, and expect this method to return values relevant to the
// current content language.
- return $defOpt;
+ return self::$defOpt;
}
- $defOpt = $wgDefaultUserOptions;
+ self::$defOpt = $wgDefaultUserOptions;
// Default language setting
- $defOptLang = $contLang->getCode();
- $defOpt['language'] = $defOptLang;
+ self::$defOptLang = $contLang->getCode();
+ self::$defOpt['language'] = self::$defOptLang;
foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
if ( $langCode === $contLang->getCode() ) {
- $defOpt['variant'] = $langCode;
+ self::$defOpt['variant'] = $langCode;
} else {
- $defOpt["variant-$langCode"] = $langCode;
+ self::$defOpt["variant-$langCode"] = $langCode;
}
}
// since extensions may change the set of searchable namespaces depending
// on user groups/permissions.
foreach ( $wgNamespacesToBeSearchedDefault as $nsnum => $val ) {
- $defOpt['searchNs' . $nsnum] = (bool)$val;
+ self::$defOpt['searchNs' . $nsnum] = (bool)$val;
}
- $defOpt['skin'] = Skin::normalizeKey( $wgDefaultSkin );
+ self::$defOpt['skin'] = Skin::normalizeKey( $wgDefaultSkin );
- Hooks::run( 'UserGetDefaultOptions', [ &$defOpt ] );
+ Hooks::run( 'UserGetDefaultOptions', [ &self::$defOpt ] );
- return $defOpt;
+ return self::$defOpt;
}
/**
'targets' => [ 'desktop', 'mobile' ],
],
'mediawiki.checkboxtoggle' => [
+ 'targets' => [ 'desktop', 'mobile' ],
'scripts' => 'resources/src/mediawiki.checkboxtoggle.js',
],
'mediawiki.checkboxtoggle.styles' => [
+ 'targets' => [ 'desktop', 'mobile' ],
'styles' => 'resources/src/mediawiki.checkboxtoggle.styles.css',
],
'mediawiki.cookie' => [
public static function resetNonServiceCaches() {
global $wgRequest, $wgJobClasses;
+ User::resetGetDefaultOptionsForTestsOnly();
foreach ( $wgJobClasses as $type => $class ) {
JobQueueGroup::singleton()->get( $type )->delete();
}
/**
* @dataProvider provideFetchOptionsFromRequest
*/
- public function testFetchOptionsFromRequest( $expectedValues, $preferences, $inputParams ) {
+ public function testFetchOptionsFromRequest(
+ $expectedValuesDefaults, $expectedValues, $preferences, $inputParams
+ ) {
+ // $defaults and $allFalse are just to make the expected values below
+ // shorter by hiding the background.
+
+ $page = TestingAccessWrapper::newFromObject(
+ $this->newSpecialPage()
+ );
+
+ $page->registerFilters();
+
+ // Does not consider $preferences, just wiki's defaults
+ $wikiDefaults = $page->getDefaultOptions()->getAllValues();
+
+ switch ( $expectedValuesDefaults ) {
+ case 'allFalse':
+ $allFalse = $wikiDefaults;
+
+ foreach ( $allFalse as $key => $value ) {
+ if ( $value === true ) {
+ $allFalse[$key] = false;
+ }
+ }
+
+ // This is not exposed on the form (only in preferences) so it
+ // respects the preference.
+ $allFalse['extended'] = true;
+
+ $expectedValues += $allFalse;
+ break;
+ case 'wikiDefaults':
+ $expectedValues += $wikiDefaults;
+ break;
+ default:
+ $this->fail( "Unknown \$expectedValuesDefaults: $expectedValuesDefaults" );
+ }
+
$page = TestingAccessWrapper::newFromObject(
$this->newSpecialPage()
);
}
public function provideFetchOptionsFromRequest() {
- // $defaults and $allFalse are just to make the expected values below
- // shorter by hiding the background.
-
- $page = TestingAccessWrapper::newFromObject(
- $this->newSpecialPage()
- );
-
- $page->registerFilters();
-
- // Does not consider $preferences, just wiki's defaults
- $wikiDefaults = $page->getDefaultOptions()->getAllValues();
-
- $allFalse = $wikiDefaults;
-
- foreach ( $allFalse as $key => &$value ) {
- if ( $value === true ) {
- $value = false;
- }
- }
-
- // This is not exposed on the form (only in preferences) so it
- // respects the preference.
- $allFalse['extended'] = true;
-
return [
- [
- [
+ 'ignores casing' => [
+ 'expectedValuesDefaults' => 'wikiDefaults',
+ 'expectedValues' => [
'hideminor' => true,
- ] + $wikiDefaults,
- [],
- [
+ ],
+ 'preferences' => [],
+ 'inputParams' => [
'hideMinor' => 1,
],
],
- [
- [
+ 'first two same as prefs, second two overriden' => [
+ 'expectedValuesDefaults' => 'wikiDefaults',
+ 'expectedValues' => [
// First two same as prefs
'hideminor' => true,
'hidebots' => false,
'hideanons' => false,
'hideliu' => true,
'userExpLevel' => 'registered'
- ] + $wikiDefaults,
- [
+ ],
+ 'preferences' => [
'watchlisthideminor' => 1,
'watchlisthidebots' => 0,
'watchlisthideanons' => 1,
'watchlisthideliu' => 0,
],
- [
+ 'inputParams' => [
'hideanons' => 0,
'hideliu' => 1,
],
],
- // Defaults/preferences for form elements are entirely ignored for
- // action=submit and omitted elements become false
- [
- [
+ 'Defaults/preferences for form elements are entirely ignored for '
+ . 'action=submit and omitted elements become false' => [
+ 'expectedValuesDefaults' => 'allFalse',
+ 'expectedValues' => [
'hideminor' => false,
'hidebots' => true,
'hideanons' => false,
'hideliu' => true,
'userExpLevel' => 'unregistered'
- ] + $allFalse,
- [
+ ],
+ 'preferences' => [
'watchlisthideminor' => 0,
'watchlisthidebots' => 1,
'watchlisthideanons' => 0,
'watchlisthideliu' => 1,
],
- [
+ 'inputParams' => [
'hidebots' => 1,
'hideliu' => 1,
'action' => 'submit',