From: jenkins-bot Date: Tue, 26 Mar 2019 23:45:24 +0000 (+0000) Subject: Merge "Replace spinner.gif with CSS solution" X-Git-Tag: 1.34.0-rc.0~2327 X-Git-Url: https://git.cyclocoop.org/%7B%24admin_url%7Dmembres/supprimer.php?a=commitdiff_plain;h=619eea9cda234e09bd1cf8539a090508afb46061;hp=425c38ec5132655c25caba4f61208a9518951636;p=lhc%2Fweb%2Fwiklou.git Merge "Replace spinner.gif with CSS solution" --- diff --git a/.gitignore b/.gitignore index def5a08eca..8cacb1ee30 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ sftp-config.json # Building & testing npm-debug.log node_modules/ +/resources/lib/.foreign /tests/phpunit/phpunit.phar /tests/selenium/log .eslintcache diff --git a/.phpcs.xml b/.phpcs.xml index 69346483b8..d1e54a706c 100644 --- a/.phpcs.xml +++ b/.phpcs.xml @@ -228,7 +228,6 @@ */includes/parser/Preprocessor_Hash\.php */includes/parser/Preprocessor\.php */includes/PathRouter\.php - */includes/poolcounter/PoolCounter\.php */includes/PrefixSearch\.php */includes/profiler/SectionProfiler\.php */includes/search/SearchEngine\.php diff --git a/HISTORY b/HISTORY index a9260699fb..d00da92372 100644 --- a/HISTORY +++ b/HISTORY @@ -2,6 +2,25 @@ Change notes from older releases. For current info see RELEASE-NOTES-1.33. = MediaWiki 1.32 = +== MediaWiki 1.32.1 == + +=== Changes since MediaWiki 1.32.0 === +* (T213577) rdbms: avoid transaction status errors from ping() in rollback(). +* rdbms: Pass required parameter. +* rdbms: do not treat SAVEPOINT and RELEASE SAVEPOINT as write queries. +* (T204531) rdbms: reduce LoadBalancer replication log spam. +* (T213489) Avoid session double-start in Setup.php. +* (T213717) Correct namespace 'Template' for gom-deva +* (T198054) Fix login page crash caused by unknown language via ?uselang +* (T215324) (T210937) list=users mistakenly reports user as missing. +* (T209483) Add ILBFactory::redefineLocalDomain method. This is intended for +use with scripts like addWiki.php to avoid mismatched domain errors. +* (T208871) The hard-coded Google search form on the database error page was +removed. +* (T204800) Fix Title::getFragmentForURL for bad interwiki prefix +* (T215566) Fix installer being unable to determine if the database exists +during a fresh installation. + == MediaWiki 1.32.0 == === Changes since MediaWiki 1.32.0-rc.2 === @@ -4785,6 +4804,11 @@ of files that are no longer available follows. = MediaWiki 1.23 = +== MediaWiki 1.23.17 == + +=== Changes since 1.23.16 === +* Fix syntax errors introduced in 1.23.16 when running PHP 5.3. + == MediaWiki 1.23.16 == This is a security and maintenance release of the MediaWiki 1.23 branch. @@ -7044,6 +7068,52 @@ changes to languages because of Bugzilla reports. == MediaWiki 1.19 == +== MediaWiki 1.19.24 == + +This is a security and maintenance release of the MediaWiki 1.19 branch. + +=== Changes since 1.19.23 === + +* ({{bug|T85848}}, {{bug|T71210}}) SECURITY: Don't parse XMP blocks that +contain XML entities, to prevent various DoS attacks. +* ({{bug|T88310}}) SECURITY: Always expand xml entities when checking SVG's. +* ({{bug|T73394}}) SECURITY: Escape > in Html::expandAttributes to prevent XSS. +* ({{bug|T85855}}) SECURITY: Don't execute another user's CSS or JS on preview. +* ({{bug|T85349}}, {{bug|T85850}}, {{bug|T86711}}) SECURITY: Multiple issues +fixed in SVG filtering to prevent XSS and protect viewer's privacy. + +== MediaWiki 1.19.23 == + +This is a security and maintenance release of the MediaWiki 1.19 branch. + +=== Changes since 1.19.22 === + +* (bug T76686) [SECURITY] thumb.php outputs wikitext message as raw HTML, which +could lead to xss. Permission to edit MediaWiki namespace is required to +exploit this. +* (bug T74222) The original patch for T74222 was reverted as unnecessary. +* Add missing $ in front of variable in OutputPage.php + +== MediaWiki 1.19.22 == + +This is a security and maintenance release of the MediaWiki 1.19 branch. + +=== Changes since 1.19.21 === + +* ({{bug|66776}}, {{bug|71478}}) SECURITY: User PleaseStand reported a way to +inject code into API clients that used format=php to process pages that +underwent flash policy mangling. This was fixed along with improving how the +mangling was done for format=json, and allowing sites to disable the mangling +using $wgMangleFlashPolicy. +* ({{bug|72222}}) SECURITY: Do not show log action when the entry is revdeleted +with DELETED_ACTION. NOTICE: this may be reverted in a future release pending a +public RFC about the desired functionality. This issue was reported by user +Bawolff. +* ({{bug|71621}}) Make allowing site-wide styles on restricted special pages a +config option. +* $wgMangleFlashPolicy was added to make MediaWiki's mangling of anything that +might be a flash policy directive configurable. + == MediaWiki 1.19.21 == This is a maintenance release of the MediaWiki 1.19 branch. @@ -7618,6 +7688,20 @@ changes to languages because of Bugzilla reports. == MediaWiki 1.18 == +== MediaWiki 1.18.6 == +2012-11-29 + +This is a maintenance and security release of the MediaWiki 1.18 branch + +=== Changes since 1.18.5 === +* ([[bugzilla:40995|bug 40995]]) Prevent session fixation in Special:UserLogin +(CVE-2012-5391) +* ([[bugzilla:41400|bug 41400]]) Prevent linker regex from exceeding PCRE +backtrack limit +* Localisation updates +* Increase permitted runtime for testParserTest +* ([[bugzilla:36179|bug 36179]]) Unquote 'null' for PostgreSQL. + == MediaWiki 1.18.5 == 2012-08-30 @@ -11341,6 +11425,43 @@ regularly. Below only new and removed languages are listed. == MediaWiki 1.13 == +== MediaWiki 1.13.5 == + +February 22, 2009 + +This is a maintenance update to the Summer 2008 snapshot release of MediaWiki. + +MediaWiki is now using a "continuous integration" development model with +quarterly snapshot releases. The latest development code is always kept +"ready to run", and in fact runs our own sites on Wikipedia. + +Release branches will continue to receive security updates for about a year +from first release, but nonessential bugfixes and feature developments +will be made on the development trunk and appear in the next quarterly release. + +Those wishing to use the latest code instead of a branch release can obtain +it from source control: http://www.mediawiki.org/wiki/Download_from_SVN + +== Changes since 1.13.4 == + +* (bug 17449) Fixed PostgreSQL installation +* (bug 17527) Fixed missing MySQL-specific options in installer + +== Changes since 1.13.3 == + +A number of cross-site scripting (XSS) security vulnerabilities were discovered +in the web-based installer (config/index.php). These vulnerabilities all +require a live installer -- once the installer has been used to install a wiki, +it is deactivated. + +Note that cross-site scripting vulnerabilities can be used to attack any website +in the same cookie domain. So if you have an uninstalled copy of MediaWiki on +the same site as an active web service, MediaWiki could be used to attack the +active service. + +If you are hosting an old copy of MediaWiki that you have never installed, you +are advised to remove it from the web. + == Changes since 1.13.2 == David Remahl of Apple's Product Security team has identified a number of @@ -11983,9 +12104,143 @@ Other changes in this release: the page * list=exturlusage in "list all links" mode can now filter by protocol +== MediaWiki 1.12 == +== MediaWiki 1.12.4 == -== MediaWiki 1.12 == +February 7, 2009 + +A number of cross-site scripting (XSS) security vulnerabilities were discovered +in the web-based installer (config/index.php). These vulnerabilities all +require a live installer -- once the installer has been used to install a wiki, +it is deactivated. + +Note that cross-site scripting vulnerabilities can be used to attack any +website in the same cookie domain. So if you have an uninstalled copy of +MediaWiki on the same site as an active web service, MediaWiki could be used to +attack the active service. + +If you are hosting an old copy of MediaWiki that you have never installed, you +are advised to remove it from the web. + +== MediaWiki 1.12.3 == + +* Fixed packaging/distribution error. Many files were missing from the +distributed tarball. + +== MediaWiki 1.12.2 == + +David Remahl of Apple's Product Security team has identified a number of +security issues in previous releases of MediaWiki. Subsequent analysis by the +MediaWiki development team expanded the scope of these vulnerabilities. The +issues with a significant impact are as follows: + +* A local script injection vulnerability affecting Internet Explorer clients +for all MediaWiki installations with uploads enabled. [CVE-2008-5250] +* A local script injection vulnerability affecting clients with SVG scripting +capability (such as Firefox 1.5+), for all MediaWiki installations with SVG +uploads enabled. [CVE-2008-5250] +* A CSRF vulnerability affecting the Special:Import feature, for all MediaWiki +installations since the feature was introduced in 1.3.0. [CVE-2008-5252] + +A local script injection vulnerability allows an attacker with a wiki account +to steal another user's login session, and to act as that user on the wiki. The +attacker uploads a malicious script file, and tricks the victim into executing +it. + +CSRF vulnerabilities allow an attacker to act as an authorised user on the +wiki, but unlike an XSS vulnerability, the attacker can only act as the user in +a specific and restricted way. The present CSRF vulnerability allows pages to +be edited, with forged revision histories. Like an XSS vulnerability, the +authorised user must visit the malicious web page to activate the attack. + +These three vulnerabilities are all fixed in this release. + +David Remahl also reminded us of some security-related configuration issues: + +* By default, MediaWiki stores a backup of deleted images in the images/deleted +directory. If you do not want these images to be publically accessible, make +sure this directory is not accessible from the web. MediaWiki takes some steps +to avoid leaking these images, but these measures are not perfect. +* Set display_errors=off in your php.ini to avoid path disclosure via PHP fatal +errors. This is the default on most shared web hosts. +* Enabling MediaWiki's debugging features, such as $wgShowExceptionDetails, may +lead to path disclosure. + +Other changes in this release: + +* Avoid fatal error in profileinfo.php when not configured. +* Add a .htaccess to deleted images directory for additional protection against +exposure of deleted files with known SHA-1 hashes on default installations. +* Avoid streaming uploaded files to the user via index.php. This allows +security-conscious users to serve uploaded files via a different domain, and +thus client-side scripts executed from that domain cannot access the login +cookies. Affects Special:Undelete, img_auth.php and thumb.php. +* When streaming files via index.php, use the MIME type detected from the file +extension, not from the data. This reduces the XSS attack surface. +* Blacklist redirects via Special:Filepath. Such redirects exacerbate any XSS +vulnerabilities involving uploads of files containing scripts. +* Internationalisation updates. + +== MediaWiki 1.12.1 == + +Changes since 1.12.0: +* (bug [[bugzilla:13522|13522]]) Fix fatal error in Parser::extractTagsAndParams +* (bug [[bugzilla:12077|12077]]) Fix HTML nesting for TOC +* (bug [[bugzilla:13532|13532]]) Use proper timestamp call when reverting images +* (bug [[bugzilla:13649|13649]], [[bugzilla:14084|14084]]) Bad call to +wfTimestamp() +* (bug [[bugzilla:13770|13770]]) Use Preprocessor_Hash by default to avoid +missing DOM module errors +* (bug [[bugzilla:13442|13442]]) API: Missing pages in prop=langlinks and +prop=extlinks are now handled properly. +* (bug [[bugzilla:13482|13482]]) API: Disabled search types handled properly +* (bug [[bugzilla:13836|13836]]) API: Fixed fatal errors resulting from +combining iiprop=metadata with format=xml +* (bug [[bugzilla:11633|11633]]) API: Explicitly convert redirect titles to +strings due to PHP's very weak typing on array keys. +* API: Fixing main page display in meta=siteinfo +* (bug [[bugzilla:11719|11719]]) API: Remove trailing blanks in YAML output. +* (bug [[bugzilla:13718|13718]]) API: Return the proper continue parameter for +cmsort=timestamp +* Security: Work around misconfiguration by requiring strict comparisons for +in_array in User::isAllowed(). +* Security: Fixed XSS vulnerability in useskin parameter. + +== MediaWiki 1.12.0 == + +This is the quarterly branch release of [[MediaWiki]] for Winter 2008. + +MediaWiki is now using a "continuous integration" development model with +quarterly snapshot releases. The latest development code is always kept "ready +to run", and in fact runs our own sites on [[wikipedia:|Wikipedia]]. + +Release branches will continue to receive security updates for about a year +from first release, but nonessential bugfixes and feature developments will be +made on the development trunk and appear in the next quarterly release. + +Those wishing to use the latest code instead of a branch release can obtain it +from source control: [[Download from SVN]]. + +Changes since 1.12.0rc1: +*(bug [[bugzilla:13359|13359]]) Double-escaping in [[Special:Allpages]]. +*Localization updates. + +== MediaWiki 1.12.0rc1 == + +This is a release candidate of the Winter 2008 quarterly snapshot release of +[[MediaWiki]]. + +MediaWiki is now using a "continuous integration" development model with +quarterly snapshot releases. The latest development code is always kept "ready +to run", and in fact runs our own sites on [[wikipedia:|Wikipedia]]. + +Release branches will continue to receive security updates for about a year +from first release, but nonessential bugfixes and feature developments will be +made on the development trunk and appear in the next quarterly release. + +Those wishing to use the latest code instead of a branch release can obtain it +from source control: [[Download from SVN]]. This is the Winter 2007 quarterly release. @@ -12539,6 +12794,76 @@ Full API documentation is available at https://www.mediawiki.org/wiki/API == MediaWiki 1.11 == +== MediaWiki 1.11.2 == + +March 2, 2008 + +This is a security release of the Fall 2007 snapshot release of MediaWiki. +Possible cross-site information leaks using the callback parameter for +JSON-formatted results in the API are prevented by dropping user credentials. + +MediaWiki release versions prior to 1.11 are not vulnerable, as they do not +include the callback feature which allows client-side JavaScript on other sites +to reach API data. + +Changes in this release: + +* User credentials are dropped for API JSON requests using a callback +* Edit tokens are not reported for API JSON requests using a callback + +== MediaWiki 1.11.1 == + +January 23, 2008 + +This is a security and bugfix release of the Fall 2007 snapshot release of + MediaWiki. A potential XSS injection vector affecting api.php only for + Microsoft Internet Explorer users has been closed. + +Changes in this release: +* (bug [[bugzilla:11450|11450]]) Fix creation of objectcache table on upgrade +* (bug [[bugzilla:11462|11462]]) Fix typo in LanguageGetSpecialPageAliases hook +name +* Fix regression in LinkBatch.php breaking PHP 5.0 +* Security fix for API on MSIE + +To work around the vulnerability without upgrading, you may disable the API if +you don't need it: +:[[Manual:$wgEnableAPI|$wgEnableAPI]] = false; + +Not vulnerable versions: +* 1.12 or later +* 1.11 >= 1.11.1 +* 1.10 >= 1.10.3 +* 1.9 >= 1.9.5 +* 1.8 any version (if $wgEnableAPI has been left off) + +Vulnerable versions: +* 1.11 <= 1.11.0rc1 +* 1.10 <= 1.10.2 +* 1.9 <= 1.9.4 +* 1.8 any version (if $wgEnableAPI has been switched on) + +MediaWiki 1.7 and below are not affected as they do not include the API +functionality, however the BotQuery extension is similarly vulnerable unless +updated to the latest SVN version. + +== MediaWiki 1.11.0 == + +September 10, 2007 + +This is the Fall 2007 snapshot release of MediaWiki. + +MediaWiki is now using a "continuous integration" development model with +quarterly snapshot releases. The latest development code is always kept "ready +to run", and in fact runs our own sites on Wikipedia. + +Release branches will continue to receive security updates for about a year +from first release, but nonessential bugfixes and feature developments will be +made on the development trunk and appear in the next quarterly release. + +Those wishing to use the latest code instead of a branch release can obtain it +from source control: [[Download from SVN]] + This is the Summer 2007 branch release of MediaWiki. MediaWiki is now using a "continuous integration" development model with @@ -12552,6 +12877,33 @@ will be made on the development trunk and appear in the next quarterly release. Those wishing to use the latest code instead of a branch release can obtain it from source control: https://www.mediawiki.org/wiki/Download_from_SVN +== Changes since 1.11.0rc1 == + +A possible HTML/XSS injection vector in the API pretty-printing mode has been +found and fixed. + +The vulnerability may be worked around in an unfixed version by simply +disabling the API interface if it is not in use, by adding this to +[[Manual:LocalSettings.php|LocalSettings.php]]:
+[[Manual:$wgEnableAPI|$wgEnableAPI]] = false;
+(This is the default setting in 1.8.x.) + +Not vulnerable versions: +* 1.11 >= 1.11.0 +* 1.10 >= 1.10.2 +* 1.9 >= 1.9.4 +* 1.8 >= 1.8.5 + +Vulnerable versions: +* 1.11 <= 1.11.0rc1 +* 1.10 <= 1.10.1 +* 1.9 <= 1.9.3 +* 1.8 <= 1.8.4 (if [[Manual:$wgEnableAPI|$wgEnableAPI]] has been switched on) + +MediaWiki 1.7 and below are not affected as they do not include the faulty +function, however the [[Extension:BotQuery|BotQuery extension]] is similarly +vulnerable unless updated to the latest SVN version. + == Configuration changes since 1.10 == * $wgThumbUpright - Adjust width of upright images when parameter 'upright' is @@ -12560,7 +12912,8 @@ it from source control: https://www.mediawiki.org/wiki/Download_from_SVN usergroups * $wgEnotifImpersonal, $wgEnotifUseJobQ - Bulk mail options for large sites * $wgShowHostnames - Expose server host names through the API and HTML comments -* $wgSaveDeletedFiles has been removed, the feature is now enabled unconditionally +* $wgSaveDeletedFiles has been removed, the feature is now enabled +unconditionally == New features since 1.10 == @@ -13127,6 +13480,121 @@ Full API documentation is available at https://www.mediawiki.org/wiki/API == MediaWiki 1.10 == +== MediaWiki 1.10.4 == + +March 2, 2008 + +* Correction for API path fix, broken in 1.10.3 + +== MediaWiki 1.10.3 == + +January 23, 2008 + +This is a security update to the Winter 2007 quarterly release. A potential +XSS injection vector affecting api.php only for Microsoft Internet Explorer +users has been closed. + + +To work around the vulnerability without upgrading, you may disable the API if +you don't need it: + +:[[Manual:$wgEnableAPI|$wgEnableAPI]] = false; + +Not vulnerable versions: +* 1.12 or later +* 1.11 >= 1.11.1 +* 1.10 >= 1.10.3 +* 1.9 >= 1.9.5 +* 1.8 any version (if $wgEnableAPI has been left off) + +Vulnerable versions: +* 1.11 <= 1.11.0rc1 +* 1.10 <= 1.10.2 +* 1.9 <= 1.9.4 +* 1.8 any version (if $wgEnableAPI has been switched on) + +MediaWiki 1.7 and below are not affected as they do not include the API +functionality, however the BotQuery extension is similarly vulnerable unless +updated to the latest SVN version. + +== MediaWiki 1.10.2 == +September 10, 2007 + +This is a security fix update to the Spring 2007 quarterly release snapshot. A +possible HTML/XSS injection vector in the API pretty-printing mode has been +found and fixed. + +The vulnerability may be worked around in an unfixed version by simply +disabling the API interface if it is not in use, by adding this to +LocalSettings.php: +:[[Manual:$wgEnableAPI|$wgEnableAPI]] = false; + +Not vulnerable versions: +* 1.11 >= 1.11.0 +* 1.10 >= 1.10.2 +* 1.9 >= 1.9.4 +* 1.8 >= 1.8.5 + +Vulnerable versions: +* 1.11 <= 1.11.0rc1 +* 1.10 <= 1.10.1 +* 1.9 <= 1.9.3 +* 1.8 <= 1.8.4 (if $wgEnableAPI has been switched on) + +MediaWiki 1.7 and below are not affected as they do not include the faulty +function, however the BotQuery extension is similarly vulnerable unless updated +to the latest SVN version. + +== MediaWiki 1.10.1 == +July 13, 2007 + +This is a bugfix update to the Spring 2007 quarterly release snapshot. A number +of fixes to improve compatibility with PostgreSQL, some versions of MySQL, and +some PHP configurations are included. + +Changes since 1.10.0: + +* (bug [[bugzilla:9417|9417]]) Uploading new versions of images when using +Postgres no longer throws warnings. +* (bug [[bugzilla:9908|9908]]) Using tsearch2 with Postgres 8.1 no longer gives +an error. +* (bug [[bugzilla:9973|9973]]) Changed size was shown in advanced recentchanges +collapsible items with $wgRCShowChangedSized = false. +* Fixed installation on MyISAM or old InnoDB with charset=utf8, was giving +overlong key errors. +* Fixed zero-padding issues with MySQL 5 binary schema +* (bug [[bugzilla:9820|9820]]) session.save_path check no longer halts +installation, but warns of possible bad values +* (bug [[bugzilla:9978|9978]]) Fixed session.save_path validation when using +extended configuration format, e.g. "5;/tmp" + +== MediaWiki 1.10.0 == +May 9, 2007 + +This is the quarterly release snapshot for Spring 2007. See below for a full +list of changes since the 1.9.x series. + +Changes since 1.10.0rc2: + +* (bug [[bugzilla:9808|9808]]) Fix regression that ignored user 'rclimit' +option for Special:Contributions + +== MediaWiki 1.10.0rc2 == +May 4, 2007 + +THIS IS A RELEASE CANDIDATE MADE AVAILABLE FOR TESTING! +A FINAL 1.10.0 RELEASE WILL APPEAR WITHIN A FEW DAYS. + +Changes since 1.10.0rc1: +* Various l10n fixes and updates +* Fix for upgrade of page_restrictions table +* (bug [[bugzilla:9780|9780]]) Fix normalization of titles with initial colon +followed by whitespace +* Fix for regression in upload: wrong size info saved into image table +* Avoid cyclic stub problems when authorization hooks do funny things with the +user and the database at load time + +== MediaWiki 1.10.0rc1 == This is the Spring 2007 branch release of MediaWiki. MediaWiki is now using a "continuous integration" development model with @@ -13616,10 +14084,159 @@ break. Don't forget to always back up your database before upgrading! See the file UPGRADE for more detailed upgrade instructions. = MediaWiki release notes = - Security reminder: MediaWiki does not require PHP's register_globals setting since version 1.2.0. If you have it on, turn it *off* if you can. += MediaWiki 1.9 = + +== MediaWiki 1.9.6 == + +March 2, 2008 + +* Correction for API path fix, broken in 1.9.5 + +== MediaWiki 1.9.5 == + +January 23, 2008 + +This is a security update to the Winter 2007 quarterly release. A potential XSS +injection vector affecting api.php only for Microsoft Internet Explorer users +has been closed. + + +To work around the vulnerability without upgrading, you may disable the API if +you don't need it: + +:[[Manual:$wgEnableAPI|$wgEnableAPI]] = false; + +Not vulnerable versions: +* 1.12 or later +* 1.11 >= 1.11.1 +* 1.10 >= 1.10.3 +* 1.9 >= 1.9.5 +* 1.8 any version (if $wgEnableAPI has been left off) + +Vulnerable versions: +* 1.11 <= 1.11.0rc1 +* 1.10 <= 1.10.2 +* 1.9 <= 1.9.4 +* 1.8 any version (if $wgEnableAPI has been switched on) + +MediaWiki 1.7 and below are not affected as they do not include the API +functionality, however the BotQuery extension is similarly vulnerable unless +updated to the latest SVN version. + +== MediaWiki 1.9.4 == + +September 10, 2007 + +This is a security and bug fix update to the Winter 2007 quarterly release. +Minor compatibility fixes for IIS 5 are included. + +* (bug [[bugzilla:8847|8847]]) Strip spurious #fragments from request URI to +fix redirect loops on some server configurations +* A possible HTML/XSS injection vector in the API pretty-printing mode has been +found and fixed. + +The vulnerability may be worked around in an unfixed version by simply +disabling the API interface if it is not in use, by adding this to +LocalSettings.php: + +:[[Manual:$wgEnableAPI|$wgEnableAPI]] = false; + +Not vulnerable versions: +* 1.11 >= 1.11.0 +* 1.10 >= 1.10.2 +* 1.9 >= 1.9.4 +* 1.8 >= 1.8.5 + +Vulnerable versions: +* 1.11 <= 1.11.0rc1 +* 1.10 <= 1.10.1 +* 1.9 <= 1.9.3 +* 1.8 <= 1.8.4 (if $wgEnableAPI has been switched on) + +MediaWiki 1.7 and below are not affected as they do not include the faulty +function, however the BotQuery extension is similarly vulnerable unless updated +to the latest SVN version. + +== MediaWiki 1.9.3 == + +February 20, 2007 + +This is a security and bug-fix update to the Winter 2007 quarterly release. +Minor compatibility fixes for IIS and PostgreSQL are included. + +An XSS injection vulnerability based on Microsoft Internet Explorer's UTF-7 +charset autodetection was located in the AJAX support module, affecting MSIE +users on MediaWiki 1.6.x and up when the optional setting $wgUseAjax is enabled. + +If you are using an extension based on the optional Ajax module, either disable +it or upgrade to a version containing the fix: + +* 1.9: fixed in 1.9.3 +* 1.8: fixed in 1.8.4 +* 1.7: fixed in 1.7.3 +* 1.6: fixed in 1.6.10 + +There is no known danger in the default configuration, with ''$wgUseAjax'' off. + +* ([[mediazilla:8992|8992]]) Fix a remaining raw use of REQUEST_URI in history +* ([[mediazilla:8984|8984]]) Fix a database error in +Special:Recentchangeslinked when using the PostgreSQL database. +* Add ''charset'' to Content-Type headers on various HTTP error responses to +forestall additional UTF-7-autodetect XSS issues. PHP sends only ''text/html'' +by default when the script didn't specify more details, which some +inconsiderate browsers consider a license to autodetect the deadly, +hard-to-escape UTF-7. This fixes an issue with the Ajax interface error message +on MSIE when ''$wgUseAjax'' is enabled (not default configuration); this UTF-7 +variant on a previously fixed attack vector was discovered by Moshe BA from +BugSec: [http://www.bugsec.com/articles.php?Security=24 +http://www.bugsec.com/articles.php?Security=24] +* Trackback responses now specify XML content type + +== MediaWiki 1.9.2 == + +February 4, 2007 + +This is a bug-fix update that fixes some installation and other minor issues +with the 1.9.1 release as well as a security issue which was introduced in the +1.9 branch. + +JavaScript code which regenerated the "sortable tables" feature did not +properly sanitize input, leading to an HTML injection vulnerability. + +* ([[mediazilla:8774|8774]]) Fix path for GNU FDL rights icon on new installs +* ([[mediazilla:8819|8819]]) Fix full path disclosure with skins dependencies +* ([[mediazilla:8819|8819]]) Fixed data-loss bug in compressOld batch text +compression affecting pages which had null edits (move, protect, etc) as second +edit in a batch group. Isolated and patched by Travis Derouin. +* Security fix for sortable tables JavaScript + +== MediaWiki 1.9.1 == + +January 24, 2007 + +This is a bug-fix update that fixes some installation and upgrade issues with +the original 1.9.0 release. + +* ([[mediazilla:3000|3000]]) Fall back to SCRIPT_NAME plus QUERY_STRING when +REQUEST_URI is not available, as on IIS with PHP-CGI +* Security fix for DjVu images. (Only affects servers where .djvu file uploads +are enabled and ''$wgDjvuToXML'' is set.) +* ([[mediazilla:8638|8638]]) Fix update from 1.4 and earlier +* ([[mediazilla:8641|8641]]) Fix order of updates to ipblocks table for updates +from <=1.7 +* ([[mediazilla:8673|8673]]) Minor fix for web service API content-type header +* Fix API revision list on PHP 5.2.1; bad reference assignment +* Fixed up the AjaxSearch +* Exclude settings files when generating documentation. That could expose the +database user and password to remote users. +* ar: fix the 'create a new page' on search page when no exact match found +* Correct tooltip accesskey hint for Opera on the Macintosh (uses Shift-Esc-, +not Ctrl-). +* ([[mediazilla:8719|8719]]) Firefox release notes lie! Fix tooltips for +Firefox 2 on x11; accesskeys default settings appear to be same as Windows. == Changes since 1.8 == diff --git a/RELEASE-NOTES-1.33 b/RELEASE-NOTES-1.33 index 93d3253762..72a468b0a0 100644 --- a/RELEASE-NOTES-1.33 +++ b/RELEASE-NOTES-1.33 @@ -84,6 +84,8 @@ For notes on 1.32.x and older releases, see HISTORY. is no longer a problem, because the code now ensures the timestamp is always higher than the previous one. The writes are guarded with CAS logic (check and set), which prevents updates that would overlap. +* $wgDBmysql5 (T196185) - This experimental setting, deprecated in 1.31, has + been removed. === New user-facing features in 1.33 === * (T96041) __EXPECTUNUSEDCATEGORY__ on a category page causes the category diff --git a/autoload.php b/autoload.php index bbfe251283..528b7fe372 100644 --- a/autoload.php +++ b/autoload.php @@ -851,7 +851,7 @@ $wgAutoloadLocalClasses = [ 'Maintenance' => __DIR__ . '/maintenance/Maintenance.php', 'MakeTestEdits' => __DIR__ . '/maintenance/makeTestEdits.php', 'MalformedTitleException' => __DIR__ . '/includes/title/MalformedTitleException.php', - 'ManageForeignResources' => __DIR__ . '/maintenance/resources/manageForeignResources.php', + 'ManageForeignResources' => __DIR__ . '/maintenance/manageForeignResources.php', 'ManageJobs' => __DIR__ . '/maintenance/manageJobs.php', 'ManualLogEntry' => __DIR__ . '/includes/logging/LogEntry.php', 'MapCacheLRU' => __DIR__ . '/includes/libs/MapCacheLRU.php', @@ -1108,10 +1108,10 @@ $wgAutoloadLocalClasses = [ 'PhpXmlBugTester' => __DIR__ . '/includes/installer/PhpBugTests.php', 'Pingback' => __DIR__ . '/includes/Pingback.php', 'PoolCounter' => __DIR__ . '/includes/poolcounter/PoolCounter.php', + 'PoolCounterNull' => __DIR__ . '/includes/poolcounter/PoolCounterNull.php', 'PoolCounterRedis' => __DIR__ . '/includes/poolcounter/PoolCounterRedis.php', 'PoolCounterWork' => __DIR__ . '/includes/poolcounter/PoolCounterWork.php', 'PoolCounterWorkViaCallback' => __DIR__ . '/includes/poolcounter/PoolCounterWorkViaCallback.php', - 'PoolCounter_Stub' => __DIR__ . '/includes/poolcounter/PoolCounter.php', 'PoolWorkArticleView' => __DIR__ . '/includes/poolcounter/PoolWorkArticleView.php', 'PopulateArchiveRevId' => __DIR__ . '/maintenance/populateArchiveRevId.php', 'PopulateBacklinkNamespace' => __DIR__ . '/maintenance/populateBacklinkNamespace.php', diff --git a/includes/Block.php b/includes/Block.php index 7e32f7ec0d..060eebd703 100644 --- a/includes/Block.php +++ b/includes/Block.php @@ -166,7 +166,7 @@ class Block { } $this->setReason( $options['reason'] ); - $this->mTimestamp = wfTimestamp( TS_MW, $options['timestamp'] ); + $this->setTimestamp( wfTimestamp( TS_MW, $options['timestamp'] ) ); $this->setExpiry( wfGetDB( DB_REPLICA )->decodeExpiry( $options['expiry'] ) ); # Boolean settings @@ -471,7 +471,7 @@ class Block { $row->ipb_by, $row->ipb_by_text, $row->ipb_by_actor ?? null ) ); - $this->mTimestamp = wfTimestamp( TS_MW, $row->ipb_timestamp ); + $this->setTimestamp( wfTimestamp( TS_MW, $row->ipb_timestamp ) ); $this->mAuto = $row->ipb_auto; $this->setHideName( $row->ipb_deleted ); $this->mId = (int)$row->ipb_id; @@ -904,7 +904,7 @@ class Block { ->inContentLanguage()->plain() ); $timestamp = wfTimestampNow(); - $autoblock->mTimestamp = $timestamp; + $autoblock->setTimestamp( $timestamp ); $autoblock->mAuto = 1; $autoblock->isCreateAccountBlocked( $this->isCreateAccountBlocked() ); # Continue suppressing the name if needed @@ -975,7 +975,7 @@ class Block { */ public function updateTimestamp() { if ( $this->mAuto ) { - $this->mTimestamp = wfTimestamp(); + $this->setTimestamp( wfTimestamp() ); $this->setExpiry( self::getAutoblockExpiry( $this->getTimestamp() ) ); $dbw = wfGetDB( DB_MASTER ); @@ -1036,10 +1036,7 @@ class Block { * @return int (0 for foreign users) */ public function getBy() { - $blocker = $this->getBlocker(); - return ( $blocker instanceof User ) - ? $blocker->getId() - : 0; + return $this->getBlocker()->getId(); } /** @@ -1048,10 +1045,7 @@ class Block { * @return string */ public function getByName() { - $blocker = $this->getBlocker(); - return ( $blocker instanceof User ) - ? $blocker->getName() - : (string)$blocker; // username + return $this->getBlocker()->getName(); } /** diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 3afa593254..d173d355ef 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -1935,7 +1935,8 @@ $wgSearchType = null; $wgSearchTypeAlternatives = null; /** - * Table name prefix; this should be alphanumeric and not contain spaces nor hyphens + * Table name prefix. + * This should be alphanumeric, contain neither spaces nor hyphens, and end in "_" */ $wgDBprefix = ''; @@ -2114,26 +2115,6 @@ $wgDBerrorLog = false; */ $wgDBerrorLogTZ = false; -/** - * Set to true to engage MySQL 4.1/5.0 charset-related features; - * for now will just cause sending of 'SET NAMES=utf8' on connect. - * - * @warning THIS IS EXPERIMENTAL! - * - * May break if you're not using the table defs from mysql5/tables.sql. - * May break if you're upgrading an existing wiki if set differently. - * Broken symptoms likely to include incorrect behavior with page titles, - * usernames, comments etc containing non-ASCII characters. - * Might also cause failures on the object cache and other things. - * - * Even correct usage may cause failures with Unicode supplementary - * characters (those not in the Basic Multilingual Plane) unless MySQL - * has enhanced their Unicode support. - * - * @deprecated since 1.31 - */ -$wgDBmysql5 = false; - /** * Set true to enable Oracle DCRP (supported from 11gR1 onward) * diff --git a/includes/ForeignResourceManager.php b/includes/ForeignResourceManager.php index e0d088a916..9fd1e4fe45 100644 --- a/includes/ForeignResourceManager.php +++ b/includes/ForeignResourceManager.php @@ -30,10 +30,12 @@ class ForeignResourceManager { private $registryFile; private $libDir; private $tmpParentDir; + private $cacheDir; private $infoPrinter; private $errorPrinter; private $verbosePrinter; private $action; + private $registry; /** * @param string $registryFile Path to YAML file @@ -60,8 +62,11 @@ class ForeignResourceManager { // Use a temporary directory under the destination directory instead // of wfTempDir() because PHP's rename() does not work across file - // systems, as the user's /tmp and $IP may be on different filesystems. - $this->tmpParentDir = "{$this->libDir}/.tmp"; + // systems, and the user's /tmp and $IP may be on different filesystems. + $this->tmpParentDir = "{$this->libDir}/.foreign/tmp"; + + $cacheHome = getenv( 'XDG_CACHE_HOME' ) ? realpath( getenv( 'XDG_CACHE_HOME' ) ) : false; + $this->cacheDir = $cacheHome ? "$cacheHome/mw-foreign" : "{$this->libDir}/.foreign/cache"; } /** @@ -69,18 +74,24 @@ class ForeignResourceManager { * @throws Exception */ public function run( $action, $module ) { - if ( !in_array( $action, [ 'update', 'verify', 'make-sri' ] ) ) { - throw new Exception( 'Invalid action parameter.' ); + $actions = [ 'update', 'verify', 'make-sri' ]; + if ( !in_array( $action, $actions ) ) { + $this->error( "Invalid action.\n\nMust be one of " . implode( ', ', $actions ) . '.' ); + return false; } $this->action = $action; - $registry = $this->parseBasicYaml( file_get_contents( $this->registryFile ) ); + $this->registry = $this->parseBasicYaml( file_get_contents( $this->registryFile ) ); if ( $module === 'all' ) { - $modules = $registry; - } elseif ( isset( $registry[ $module ] ) ) { - $modules = [ $module => $registry[ $module ] ]; + $modules = $this->registry; + } elseif ( isset( $this->registry[ $module ] ) ) { + $modules = [ $module => $this->registry[ $module ] ]; } else { - throw new Exception( 'Unknown module name.' ); + $this->error( "Unknown module name.\n\nMust be one of:\n" . + wordwrap( implode( ', ', array_keys( $this->registry ) ), 80 ) . + '.' + ); + return false; } foreach ( $modules as $moduleName => $info ) { @@ -121,8 +132,8 @@ class ForeignResourceManager { } } - $this->cleanUp(); $this->output( "\nDone!\n" ); + $this->cleanUp(); if ( $this->hasErrors ) { // The verify mode should check all modules/files and fail after, not during. return false; @@ -131,7 +142,29 @@ class ForeignResourceManager { return true; } + private function cacheKey( $src, $integrity ) { + $key = basename( $src ) . '_' . substr( $integrity, -12 ); + $key = preg_replace( '/[.\/+?=_-]+/', '_', $key ); + return rtrim( $key, '_' ); + } + + /** @return string|false */ + private function cacheGet( $key ) { + return Wikimedia\quietCall( 'file_get_contents', "{$this->cacheDir}/$key.data" ); + } + + private function cacheSet( $key, $data ) { + wfMkdirParents( $this->cacheDir ); + file_put_contents( "{$this->cacheDir}/$key.data", $data, LOCK_EX ); + } + private function fetch( $src, $integrity ) { + $key = $this->cacheKey( $src, $integrity ); + $data = $this->cacheGet( $key ); + if ( $data ) { + return $data; + } + $req = MWHttpRequest::factory( $src, [ 'method' => 'GET', 'followRedirects' => false ] ); if ( !$req->execute()->isOK() ) { throw new Exception( "Failed to download resource at {$src}" ); @@ -144,6 +177,7 @@ class ForeignResourceManager { $actualIntegrity = $algo . '-' . base64_encode( hash( $algo, $data, true ) ); if ( $integrity === $actualIntegrity ) { $this->verbose( "... passed integrity check for {$src}\n" ); + $this->cacheSet( $key, $data ); } else { if ( $this->action === 'make-sri' ) { $this->output( "Integrity for {$src}\n\tintegrity: ${actualIntegrity}\n" ); @@ -271,6 +305,23 @@ class ForeignResourceManager { private function cleanUp() { wfRecursiveRemoveDir( $this->tmpParentDir ); + + // Prune the cache of files we don't recognise. + $knownKeys = []; + foreach ( $this->registry as $info ) { + if ( $info['type'] === 'file' || $info['type'] === 'tar' ) { + $knownKeys[] = $this->cacheKey( $info['src'], $info['integrity'] ); + } elseif ( $info['type'] === 'multi-file' ) { + foreach ( $info['files'] as $file ) { + $knownKeys[] = $this->cacheKey( $file['src'], $file['integrity'] ); + } + } + } + foreach ( glob( "{$this->cacheDir}/*" ) as $cacheFile ) { + if ( !in_array( basename( $cacheFile, '.data' ), $knownKeys ) ) { + unlink( $cacheFile ); + } + } } /** diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 319bf6397e..55b78acf53 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -334,6 +334,7 @@ function wfUrlencode( $s ) { static $needle; if ( is_null( $s ) ) { + // Reset $needle for testing. $needle = null; return ''; } diff --git a/includes/Linker.php b/includes/Linker.php index decc13cb41..df9955609c 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -1077,21 +1077,23 @@ class Linker { * @since 1.16.3 * @param Revision $rev * @param bool $isPublic Show only if all users can see it + * @param bool $useParentheses (optional) Wrap comments in parentheses where needed * @return string HTML */ - public static function revUserTools( $rev, $isPublic = false ) { + public static function revUserTools( $rev, $isPublic = false, $useParentheses = true ) { if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) { $link = wfMessage( 'rev-deleted-user' )->escaped(); } elseif ( $rev->userCan( Revision::DELETED_USER ) ) { $userId = $rev->getUser( Revision::FOR_THIS_USER ); $userText = $rev->getUserText( Revision::FOR_THIS_USER ); $link = self::userLink( $userId, $userText ) - . self::userToolLinks( $userId, $userText ); + . self::userToolLinks( $userId, $userText, false, 0, null, + $useParentheses ); } else { $link = wfMessage( 'rev-deleted-user' )->escaped(); } if ( $rev->isDeleted( Revision::DELETED_USER ) ) { - return ' ' . $link . ''; + return ' ' . $link . ''; } return $link; } @@ -1517,7 +1519,7 @@ class Linker { $block = " " . wfMessage( 'rev-deleted-comment' )->escaped() . ""; } if ( $rev->isDeleted( Revision::DELETED_COMMENT ) ) { - return " $block"; + return " $block"; } return $block; } @@ -1532,9 +1534,8 @@ class Linker { $stxt = wfMessage( 'historyempty' )->escaped(); } else { $stxt = wfMessage( 'nbytes' )->numParams( $size )->escaped(); - $stxt = wfMessage( 'parentheses' )->rawParams( $stxt )->escaped(); } - return "$stxt"; + return "$stxt"; } /** diff --git a/includes/OutputPage.php b/includes/OutputPage.php index cb90ccf7b7..8a19c51f7a 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -2984,7 +2984,7 @@ class OutputPage extends ContextSource { */ public function showFileRenameError( $old, $new ) { wfDeprecated( __METHOD__, '1.32' ); - $this->showFatalError( $this->msg( 'filerenameerror', $old, $new )->escpaed() ); + $this->showFatalError( $this->msg( 'filerenameerror', $old, $new )->escaped() ); } /** diff --git a/includes/Title.php b/includes/Title.php index d8aeb6293c..0f45839577 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -3727,6 +3727,7 @@ class Title implements LinkTarget, IDBAccessObject { // @todo: get rid of secureAndSplit, refactor parsing code. // @note: getTitleParser() returns a TitleParser implementation which does not have a // splitTitleString method, but the only implementation (MediaWikiTitleCodec) does + /** @var MediaWikiTitleCodec $titleCodec */ $titleCodec = MediaWikiServices::getInstance()->getTitleParser(); // MalformedTitleException can be thrown here $parts = $titleCodec->splitTitleString( $this->mDbkeyform, $this->mDefaultNamespace ); diff --git a/includes/actions/pagers/HistoryPager.php b/includes/actions/pagers/HistoryPager.php index d3a32d0e27..b3333726a0 100644 --- a/includes/actions/pagers/HistoryPager.php +++ b/includes/actions/pagers/HistoryPager.php @@ -310,11 +310,12 @@ class HistoryPager extends ReverseChronologicalPager { $curlink = $this->curLink( $rev, $latest ); $lastlink = $this->lastLink( $rev, $next ); - $curLastlinks = $curlink . $this->historyPage->message['pipe-separator'] . $lastlink; + $curLastlinks = Html::rawElement( 'span', [], $curlink ) . + Html::rawElement( 'span', [], $lastlink ); $histLinks = Html::rawElement( 'span', - [ 'class' => 'mw-history-histlinks' ], - $this->msg( 'parentheses' )->rawParams( $curLastlinks )->escaped() + [ 'class' => 'mw-history-histlinks mw-changeslist-links' ], + $curLastlinks ); $diffButtons = $this->diffButtons( $rev, $firstInList ); @@ -362,7 +363,7 @@ class HistoryPager extends ReverseChronologicalPager { $s .= " $link"; $s .= $dirmark; $s .= " " . - Linker::revUserTools( $rev, true ) . ""; + Linker::revUserTools( $rev, true, false ) . ""; $s .= $dirmark; if ( $rev->isMinor() ) { @@ -374,12 +375,12 @@ class HistoryPager extends ReverseChronologicalPager { # Size is always public data $prevSize = $this->parentLens[$row->rev_parent_id] ?? 0; $sDiff = ChangesList::showCharacterDifference( $prevSize, $rev->getSize() ); - $fSize = Linker::formatRevisionSize( $rev->getSize() ); - $s .= ' . . ' . "$fSize $sDiff"; + $fSize = Linker::formatRevisionSize( $rev->getSize(), false ); + $s .= ' ' . "$fSize $sDiff"; } # Text following the character difference is added just before running hooks - $s2 = Linker::revComment( $rev, false, true ); + $s2 = Linker::revComment( $rev, false, true, false ); if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) { $s2 .= ' ' . $this->msg( 'updatedmarker' )->escaped() . ''; @@ -427,7 +428,11 @@ class HistoryPager extends ReverseChronologicalPager { Hooks::run( 'HistoryRevisionTools', [ $rev, &$tools, $prevRev, $user ] ); if ( $tools ) { - $s2 .= ' ' . $this->msg( 'parentheses' )->rawParams( $lang->pipeList( $tools ) )->escaped(); + $s2 .= ' ' . Html::openElement( 'span', [ 'class' => 'mw-changeslist-links' ] ); + foreach ( $tools as $tool ) { + $s2 .= Html::rawElement( 'span', [], $tool ); + } + $s2 .= Html::closeElement( 'span' ); } # Tags @@ -443,7 +448,7 @@ class HistoryPager extends ReverseChronologicalPager { # Include separator between character difference and following text if ( $s2 !== '' ) { - $s .= ' . . ' . $s2; + $s .= ' ' . $s2; } $attribs = [ 'data-mw-revid' => $rev->getId() ]; @@ -480,7 +485,7 @@ class HistoryPager extends ReverseChronologicalPager { $link = htmlspecialchars( $date ); } if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) { - $link = "$link"; + $link = "$link"; } return $link; diff --git a/includes/api/ApiQueryRevisionsBase.php b/includes/api/ApiQueryRevisionsBase.php index 51f4d41299..565e615edd 100644 --- a/includes/api/ApiQueryRevisionsBase.php +++ b/includes/api/ApiQueryRevisionsBase.php @@ -20,6 +20,7 @@ * @file */ +use MediaWiki\Logger\LoggerFactory; use MediaWiki\Revision\RevisionAccessException; use MediaWiki\Revision\RevisionRecord; use MediaWiki\Revision\SlotRecord; @@ -292,69 +293,27 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase { } } - if ( $this->fld_roles ) { - $vals['roles'] = $revision->getSlotRoles(); - } - - if ( $this->needSlots ) { - $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_TEXT ); - if ( ( $this->fld_slotsha1 || $this->fetchContent ) && ( $revDel & self::IS_DELETED ) ) { - $anyHidden = true; + try { + if ( $this->fld_roles ) { + $vals['roles'] = $revision->getSlotRoles(); } - if ( $this->slotRoles === null ) { - try { - $slot = $revision->getSlot( SlotRecord::MAIN, RevisionRecord::RAW ); - } catch ( RevisionAccessException $e ) { - // Back compat: If there's no slot, there's no content, so set 'textmissing' - // @todo: Gergő says to mention T198099 as a "todo" here. - $vals['textmissing'] = true; - $slot = null; - } - if ( $slot ) { - $content = null; - $vals += $this->extractSlotInfo( $slot, $revDel, $content ); - if ( !empty( $vals['nosuchsection'] ) ) { - $this->dieWithError( - [ - 'apierror-nosuchsection-what', - wfEscapeWikiText( $this->section ), - $this->msg( 'revid', $revision->getId() ) - ], - 'nosuchsection' - ); - } - if ( $content ) { - $vals += $this->extractDeprecatedContent( $content, $revision ); - } - } - } else { - $roles = array_intersect( $this->slotRoles, $revision->getSlotRoles() ); - $vals['slots'] = [ - ApiResult::META_KVP_MERGE => true, - ]; - foreach ( $roles as $role ) { - try { - $slot = $revision->getSlot( $role, RevisionRecord::RAW ); - } catch ( RevisionAccessException $e ) { - // Don't error out here so the client can still process other slots/revisions. - // @todo: Gergő says to mention T198099 as a "todo" here. - $vals['slots'][$role]['missing'] = true; - continue; - } - $content = null; - $vals['slots'][$role] = $this->extractSlotInfo( $slot, $revDel, $content ); - // @todo Move this into extractSlotInfo() (and remove its $content parameter) - // when extractDeprecatedContent() is no more. - if ( $content ) { - $vals['slots'][$role]['contentmodel'] = $content->getModel(); - $vals['slots'][$role]['contentformat'] = $content->getDefaultFormat(); - ApiResult::setContentValue( $vals['slots'][$role], 'content', $content->serialize() ); - } + if ( $this->needSlots ) { + $revDel = $this->checkRevDel( $revision, RevisionRecord::DELETED_TEXT ); + if ( ( $this->fld_slotsha1 || $this->fetchContent ) && ( $revDel & self::IS_DELETED ) ) { + $anyHidden = true; } - ApiResult::setArrayType( $vals['slots'], 'kvp', 'role' ); - ApiResult::setIndexedTagName( $vals['slots'], 'slot' ); + $vals = array_merge( $vals, $this->extractAllSlotInfo( $revision, $revDel ) ); } + } catch ( RevisionAccessException $ex ) { + // This is here so T212428 doesn't spam the log. + // TODO: find out why T212428 happens in the first place! + $vals['slotsmissing'] = true; + + LoggerFactory::getInstance( 'api-warning' )->error( + 'Failed to access revision slots', + [ 'revision' => $revision->getId(), 'exception' => $ex, ] + ); } if ( $this->fld_comment || $this->fld_parsedcomment ) { @@ -396,6 +355,79 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase { return $vals; } + /** + * Extracts information about all relevant slots. + * + * @param RevisionRecord $revision + * @param int $revDel + * + * @return array + * @throws ApiUsageException + */ + private function extractAllSlotInfo( RevisionRecord $revision, $revDel ): array { + $vals = []; + + if ( $this->slotRoles === null ) { + try { + $slot = $revision->getSlot( SlotRecord::MAIN, RevisionRecord::RAW ); + } catch ( RevisionAccessException $e ) { + // Back compat: If there's no slot, there's no content, so set 'textmissing' + // @todo: Gergő says to mention T198099 as a "todo" here. + $vals['textmissing'] = true; + $slot = null; + } + + if ( $slot ) { + $content = null; + $vals += $this->extractSlotInfo( $slot, $revDel, $content ); + if ( !empty( $vals['nosuchsection'] ) ) { + $this->dieWithError( + [ + 'apierror-nosuchsection-what', + wfEscapeWikiText( $this->section ), + $this->msg( 'revid', $revision->getId() ) + ], + 'nosuchsection' + ); + } + if ( $content ) { + $vals += $this->extractDeprecatedContent( $content, $revision ); + } + } + } else { + $roles = array_intersect( $this->slotRoles, $revision->getSlotRoles() ); + $vals['slots'] = [ + ApiResult::META_KVP_MERGE => true, + ]; + foreach ( $roles as $role ) { + try { + $slot = $revision->getSlot( $role, RevisionRecord::RAW ); + } catch ( RevisionAccessException $e ) { + // Don't error out here so the client can still process other slots/revisions. + // @todo: Gergő says to mention T198099 as a "todo" here. + $vals['slots'][$role]['missing'] = true; + continue; + } + $content = null; + $vals['slots'][$role] = $this->extractSlotInfo( $slot, $revDel, $content ); + // @todo Move this into extractSlotInfo() (and remove its $content parameter) + // when extractDeprecatedContent() is no more. + if ( $content ) { + $vals['slots'][$role]['contentmodel'] = $content->getModel(); + $vals['slots'][$role]['contentformat'] = $content->getDefaultFormat(); + ApiResult::setContentValue( + $vals['slots'][$role], + 'content', + $content->serialize() + ); + } + } + ApiResult::setArrayType( $vals['slots'], 'kvp', 'role' ); + ApiResult::setIndexedTagName( $vals['slots'], 'slot' ); + } + return $vals; + } + /** * Extract information from the SlotRecord * diff --git a/includes/api/ApiStashEdit.php b/includes/api/ApiStashEdit.php index 455ff458f1..fe5f6c4a44 100644 --- a/includes/api/ApiStashEdit.php +++ b/includes/api/ApiStashEdit.php @@ -46,6 +46,8 @@ class ApiStashEdit extends ApiBase { const MAX_CACHE_TTL = 300; // 5 minutes const MAX_SIGNATURE_TTL = 60; + const MAX_CACHE_RECENT = 2; + public function execute() { $user = $this->getUser(); $params = $this->extractRequestParams(); @@ -461,9 +463,32 @@ class ApiStashEdit extends ApiBase { ); } + if ( $ok ) { + // These blobs can waste slots in low cardinality memcached slabs + self::pruneExcessStashedEntries( $cache, $user, $key ); + } + return $ok ? true : 'store_error'; } + /** + * @param BagOStuff $cache + * @param User $user + * @param string $newKey + */ + private static function pruneExcessStashedEntries( BagOStuff $cache, User $user, $newKey ) { + $key = $cache->makeKey( 'stash-edit-recent', $user->getId() ); + + $keyList = $cache->get( $key ) ?: []; + if ( count( $keyList ) >= self::MAX_CACHE_RECENT ) { + $oldestKey = array_shift( $keyList ); + $cache->delete( $oldestKey ); + } + + $keyList[] = $newKey; + $cache->set( $key, $keyList, 2 * self::MAX_CACHE_TTL ); + } + public function getAllowedParams() { return [ 'title' => [ diff --git a/includes/api/i18n/he.json b/includes/api/i18n/he.json index 04efe36fa8..3b13904b0a 100644 --- a/includes/api/i18n/he.json +++ b/includes/api/i18n/he.json @@ -352,7 +352,7 @@ "apihelp-parse-paramvalue-prop-revid": "הוספת מזהה הגרסה של הדף המפוענח.", "apihelp-parse-paramvalue-prop-displaytitle": "הוספת הכותרת של קוד הוויקי המפוענח.", "apihelp-parse-paramvalue-prop-headitems": "נותן פריטים לשים ב־<head> של הדף.", - "apihelp-parse-paramvalue-prop-headhtml": "נותן את ה־<head> המפוענח של הדף.", + "apihelp-parse-paramvalue-prop-headhtml": "נותן doctype מפוענח, תג <html> פותח, רכיב <head>, ותג <body> פותח של הדף.", "apihelp-parse-paramvalue-prop-modules": "מתן יחידות ResourceLoader שמשמשות בדף. כדי לטעון, יש להשתמש בmw.loader.using(). יש לבקש את jsconfigvars או את encodedjsconfigvars יחד עם modules.", "apihelp-parse-paramvalue-prop-jsconfigvars": "נותן משתני הגדרות של JavaScript שייחודיים לדף הזה. כדי להחיל, יש להשתמש בmw.config.set().", "apihelp-parse-paramvalue-prop-encodedjsconfigvars": "נותן משתני הגדרות של JavaScript שייחודיים לדף הזה בתור מחרוזת JSON.", diff --git a/includes/api/i18n/ja.json b/includes/api/i18n/ja.json index 6629309c92..9e28c3993e 100644 --- a/includes/api/i18n/ja.json +++ b/includes/api/i18n/ja.json @@ -1045,6 +1045,7 @@ "apierror-mustbeloggedin": "$1にログインしている必要があります。", "apierror-noimageredirect": "画像のリダイレクトを作成する権限がありません。", "apierror-nosuchpageid": "ID $1のページはありません。", + "apierror-pagelang-disabled": "このウィキではページの言語は変更できません。", "apierror-permissiondenied": "$1に必要な権限がありません。", "apierror-permissiondenied-generic": "アクセスが拒否されました。", "apierror-readonly": "ウィキは現在読み取り専用モードです。", diff --git a/includes/block/BlockRestriction.php b/includes/block/BlockRestriction.php index b92cda80f5..cbd30c2c6f 100644 --- a/includes/block/BlockRestriction.php +++ b/includes/block/BlockRestriction.php @@ -51,7 +51,7 @@ class BlockRestriction { return []; } - $db = $db ?: wfGetDb( DB_REPLICA ); + $db = $db ?: wfGetDB( DB_REPLICA ); $result = $db->select( [ 'ipblocks_restrictions', 'page' ], @@ -182,7 +182,7 @@ class BlockRestriction { $parentBlockId = (int)$parentBlockId; - $db = wfGetDb( DB_MASTER ); + $db = wfGetDB( DB_MASTER ); $db->startAtomic( __METHOD__ ); diff --git a/includes/changetags/ChangeTags.php b/includes/changetags/ChangeTags.php index 00eed14919..3a93e57611 100644 --- a/includes/changetags/ChangeTags.php +++ b/includes/changetags/ChangeTags.php @@ -766,7 +766,7 @@ class ChangeTags { // Return nothing. $conds[] = '0'; break; - }; + } } if ( $filterTagIds !== [] ) { diff --git a/includes/context/RequestContext.php b/includes/context/RequestContext.php index 2cbe67c525..a4225a1436 100644 --- a/includes/context/RequestContext.php +++ b/includes/context/RequestContext.php @@ -345,12 +345,8 @@ class RequestContext implements IContextSource, MutableContext { $obj = Language::factory( $code ); $this->lang = $obj; } - - unset( $this->recursion ); - } - catch ( Exception $ex ) { + } finally { unset( $this->recursion ); - throw $ex; } } diff --git a/includes/db/DatabaseOracle.php b/includes/db/DatabaseOracle.php index bc3873d26e..3d80bbd011 100644 --- a/includes/db/DatabaseOracle.php +++ b/includes/db/DatabaseOracle.php @@ -53,11 +53,6 @@ class DatabaseOracle extends Database { private $mFieldInfoCache = []; function __construct( array $p ) { - global $wgDBprefix; - - if ( $p['tablePrefix'] == 'get from global' ) { - $p['tablePrefix'] = $wgDBprefix; - } $p['tablePrefix'] = strtoupper( $p['tablePrefix'] ); parent::__construct( $p ); Hooks::run( 'DatabaseOraclePostInit', [ $this ] ); @@ -961,7 +956,7 @@ class DatabaseOracle extends Database { // Defines must comply with ^define\s*([^\s=]*)\s*=\s?'\{\$([^\}]*)\}'; while ( !feof( $fp ) ) { if ( $lineCallback ) { - call_user_func( $lineCallback ); + $lineCallback(); } $line = trim( fgets( $fp, 1024 ) ); $sl = strlen( $line ) - 1; @@ -1007,7 +1002,7 @@ class DatabaseOracle extends Database { $cmd = $this->replaceVars( $cmd ); if ( $inputCallback ) { - call_user_func( $inputCallback, $cmd ); + $inputCallback( $cmd ); } $res = $this->doQuery( $cmd ); if ( $resultCallback ) { diff --git a/includes/db/MWLBFactory.php b/includes/db/MWLBFactory.php index 6ed693ecb3..fc5b18abca 100644 --- a/includes/db/MWLBFactory.php +++ b/includes/db/MWLBFactory.php @@ -77,6 +77,7 @@ abstract class MWLBFactory { 'defaultGroup' => $mainConfig->get( 'DBDefaultGroup' ), ]; + $serversCheck = []; // When making changes here, remember to also specify MediaWiki-specific options // for Database classes in the relevant Installer subclass. // Such as MysqlInstaller::openConnection and PostgresInstaller::openConnectionWithParams. @@ -84,6 +85,7 @@ abstract class MWLBFactory { if ( isset( $lbConf['servers'] ) ) { // Server array is already explicitly configured; leave alone } elseif ( is_array( $mainConfig->get( 'DBservers' ) ) ) { + $lbConf['servers'] = []; foreach ( $mainConfig->get( 'DBservers' ) as $i => $server ) { if ( $server['type'] === 'sqlite' ) { $server += [ 'dbDirectory' => $mainConfig->get( 'SQLiteDataDir' ) ]; @@ -98,20 +100,6 @@ abstract class MWLBFactory { 'port' => $mainConfig->get( 'DBport' ), 'useWindowsAuth' => $mainConfig->get( 'DBWindowsAuthentication' ) ]; - } elseif ( $server['type'] === 'mysql' ) { - // A DB name is not needed to connect to mysql; 'dbname' is useless. - // This field only defines the DB to use for unspecified DB domains. - $ldDB = $mainConfig->get( 'DBname' ); // local domain DB - $srvDB = $server['dbname'] ?? null; // server DB - if ( $srvDB !== null && $srvDB !== $ldDB ) { - self::reportMismatchedDBs( $srvDB, $ldDB ); - } - } - - $ldTP = $mainConfig->get( 'DBprefix' ); // local domain prefix - $srvTP = $server['tablePrefix'] ?? ''; // server table prefix - if ( $srvTP !== '' && $srvTP !== $ldTP ) { - self::reportMismatchedPrefixes( $srvTP, $ldTP ); } if ( in_array( $server['type'], $typesWithSchema, true ) ) { @@ -122,7 +110,6 @@ abstract class MWLBFactory { 'tablePrefix' => $mainConfig->get( 'DBprefix' ), 'flags' => DBO_DEFAULT, 'sqlMode' => $mainConfig->get( 'SQLMode' ), - 'utf8Mode' => $mainConfig->get( 'DBmysql5' ) ]; $lbConf['servers'][$i] = $server; @@ -142,7 +129,6 @@ abstract class MWLBFactory { 'load' => 1, 'flags' => $flags, 'sqlMode' => $mainConfig->get( 'SQLMode' ), - 'utf8Mode' => $mainConfig->get( 'DBmysql5' ) ]; if ( in_array( $server['type'], $typesWithSchema, true ) ) { $server += [ 'schema' => $mainConfig->get( 'DBmwschema' ) ]; @@ -162,16 +148,19 @@ abstract class MWLBFactory { if ( !isset( $lbConf['externalClusters'] ) ) { $lbConf['externalClusters'] = $mainConfig->get( 'ExternalServers' ); } + + $serversCheck = $lbConf['servers']; } elseif ( $lbConf['class'] === Wikimedia\Rdbms\LBFactoryMulti::class ) { if ( isset( $lbConf['serverTemplate'] ) ) { if ( in_array( $lbConf['serverTemplate']['type'], $typesWithSchema, true ) ) { $lbConf['serverTemplate']['schema'] = $mainConfig->get( 'DBmwschema' ); } $lbConf['serverTemplate']['sqlMode'] = $mainConfig->get( 'SQLMode' ); - $lbConf['serverTemplate']['utf8Mode'] = $mainConfig->get( 'DBmysql5' ); } + $serversCheck = $lbConf['serverTemplate'] ?? []; } + self::sanityCheckServerConfig( $serversCheck, $mainConfig ); $lbConf = self::applyDefaultCaching( $lbConf, $srvCace, $mainStash, $wanCache ); return $lbConf; @@ -201,6 +190,50 @@ abstract class MWLBFactory { return $lbConf; } + /** + * @param array $servers + * @param Config $mainConfig + */ + private static function sanityCheckServerConfig( array $servers, Config $mainConfig ) { + $ldDB = $mainConfig->get( 'DBname' ); // local domain DB + $ldTP = $mainConfig->get( 'DBprefix' ); // local domain prefix + + foreach ( $servers as $server ) { + $type = $server['type'] ?? null; + $srvDB = $server['dbname'] ?? null; // server DB + $srvTP = $server['tablePrefix'] ?? ''; // server table prefix + + if ( $type === 'mysql' ) { + // A DB name is not needed to connect to mysql; 'dbname' is useless. + // This field only defines the DB to use for unspecified DB domains. + if ( $srvDB !== null && $srvDB !== $ldDB ) { + self::reportMismatchedDBs( $srvDB, $ldDB ); + } + } elseif ( $type === 'postgres' ) { + if ( $srvTP !== '' ) { + self::reportIfPrefixSet( $srvTP, $type ); + } + } + + if ( $srvTP !== '' && $srvTP !== $ldTP ) { + self::reportMismatchedPrefixes( $srvTP, $ldTP ); + } + } + } + + /** + * @param string $prefix Table prefix + * @param string $dbType Database type + */ + private static function reportIfPrefixSet( $prefix, $dbType ) { + $e = new UnexpectedValueException( + "\$wgDBprefix is set to '$prefix' but the database type is '$dbType'. " . + "MediaWiki does not support using a table prefix with this RDBMS type." + ); + MWExceptionRenderer::output( $e, MWExceptionRenderer::AS_PRETTY ); + exit; + } + /** * @param string $srvDB Server config database * @param string $ldDB Local DB domain database diff --git a/includes/htmlform/HTMLForm.php b/includes/htmlform/HTMLForm.php index 82cbb40d0c..ee0da7b64f 100644 --- a/includes/htmlform/HTMLForm.php +++ b/includes/htmlform/HTMLForm.php @@ -605,7 +605,7 @@ class HTMLForm extends ContextSource { $valid = true; $hoistedErrors = Status::newGood(); if ( $this->mValidationErrorMessage ) { - foreach ( (array)$this->mValidationErrorMessage as $error ) { + foreach ( $this->mValidationErrorMessage as $error ) { $hoistedErrors->fatal( ...$error ); } } else { @@ -700,8 +700,8 @@ class HTMLForm extends ContextSource { /** * Set a message to display on a validation error. * - * @param string|array $msg String or Array of valid inputs to wfMessage() - * (so each entry can be either a String or Array) + * @param array $msg Array of valid inputs to wfMessage() + * (so each entry must itself be an array of arguments) * * @return HTMLForm $this for chaining calls (since 1.20) */ diff --git a/includes/htmlform/HTMLFormField.php b/includes/htmlform/HTMLFormField.php index 818474d47e..16dc46591b 100644 --- a/includes/htmlform/HTMLFormField.php +++ b/includes/htmlform/HTMLFormField.php @@ -586,7 +586,7 @@ abstract class HTMLFormField { // It might look weird, but it'll work OK. return $this->getFieldLayoutOOUI( new OOUI\Widget( [ 'content' => new OOUI\HtmlSnippet( $this->getDiv( $value ) ) ] ), - [ 'infusable' => false, 'align' => 'top' ] + [ 'align' => 'top' ] ); } diff --git a/includes/htmlform/OOUIHTMLForm.php b/includes/htmlform/OOUIHTMLForm.php index 49cbdee6e0..e7e3ff6333 100644 --- a/includes/htmlform/OOUIHTMLForm.php +++ b/includes/htmlform/OOUIHTMLForm.php @@ -151,13 +151,11 @@ class OOUIHTMLForm extends HTMLForm { 'expanded' => false, 'padded' => true, 'framed' => true, - 'infusable' => false, ] ); $layout->appendContent( new OOUI\FieldsetLayout( [ 'label' => $legend, - 'infusable' => false, 'items' => [ new OOUI\Widget( [ 'content' => new OOUI\HtmlSnippet( $section ) diff --git a/includes/installer/i18n/pms.json b/includes/installer/i18n/pms.json index f928e20bae..9d417680af 100644 --- a/includes/installer/i18n/pms.json +++ b/includes/installer/i18n/pms.json @@ -77,7 +77,7 @@ "config-uploads-not-safe": "'''Avis:''' Sò dossié stàndard për carié $1 a l'é vulneràbil a l'esecussion ëd qualsëssìa senari.\nBele che MediaWiki a contròla j'aspet ëd sicurëssa ëd tùit j'archivi carià, a l'é motobin arcomandà ëd [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security saré ës përtus ëd sicurëssa] prima d'abilité ij cariament.", "config-no-cli-uploads-check": "'''Avis:''' Toa cartela predefinìa për j-amportassion ($1) a l'é nen controlà a propòsit ëd la vulnerabilità\nd'esecussion ëd senari arbitrari durant l'istalassion CLI.", "config-brokenlibxml": "Sò sistema a l'ha na combinassion ëd version PHP e libxml2 che a l'ha dij bigat e a peul provoché la corussion ëd dat ëstërmà an MediaWiki e d'àutre aplicassion për l'aragnà.\nCh'a agiorna a PHP 5.2.9 o pi neuv e libxml2 2.7.3 o pi neuv ([https://bugs.php.net/bug.php?id=45996 bigat archivià con PHP]).\nAnstalassion abortìa.", - "config-suhosin-max-value-length": "Suhosin a l'é instalà e a lìmita la longheur dël paràmetr GET a $1 byte. Ël component ResourceLoader ëd MediaWiki a travajerà an rispetand ës lìmit, ma sòn a degraderà le prestassion. Se possìbil, a dovrìa amposté suhosin.get.max_value_length a 1024 o pi àut an php.ini, e amposté $wgResourceLoaderMaxQueryLength al midem valor an LocalSettings.php .", + "config-suhosin-max-value-length": "Suhosin a l'é instalà e a lìmita la longheur dël paràmetr GET a $1 byte. Ël component ResourceLoader ëd MediaWiki a travajerà an rispetand ës lìmit, ma sòn a degraderà le prestassion. Se possìbil, a dovrìa amposté suhosin.get.max_value_length a 1024 o pi àut an php.ini, e amposté $wgResourceLoaderMaxQueryLength al midem valor an LocalSettings.php.", "config-db-type": "Sòrt ëd base ëd dàit:", "config-db-host": "Ospitant ëd la base ëd dàit:", "config-db-host-help": "Se sò servent ëd base ëd dàit a l'é su un servent diferent, ch'a anserissa ambelessì ël nòm dl'ospitant o l'adrëssa IP.\n\nS'a deuvra n'ospitalità partagià, sò fornidor d'ospitalità a dovrìa deje ël nòm dl'ospitant giust ant soa documentassion.\n\nSe a anstala su un servent Windows e a deuvra MySQL, dovré «localhost» a podrìa funsioné nen com nòm dël servent. S'a marcia nen, ch'a preuva «127.0.0.1» com adrëssa IP local.\n\nS'a deuvra PostgresSQL, ch'a lassa sto camp bianch për coleghesse a travers un socket UNIX.", diff --git a/includes/libs/objectcache/BagOStuff.php b/includes/libs/objectcache/BagOStuff.php index 4fe64f2641..bdfed82f05 100644 --- a/includes/libs/objectcache/BagOStuff.php +++ b/includes/libs/objectcache/BagOStuff.php @@ -260,6 +260,17 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface { */ abstract public function delete( $key, $flags = 0 ); + /** + * Insert an item if it does not already exist + * + * @param string $key + * @param mixed $value + * @param int $exptime + * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33) + * @return bool Success + */ + abstract public function add( $key, $value, $exptime = 0, $flags = 0 ); + /** * Merge changes into the existing cache value (possibly creating a new one) * @@ -587,16 +598,6 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface { return $res; } - /** - * Insertion - * @param string $key - * @param mixed $value - * @param int $exptime - * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33) - * @return bool Success - */ - abstract public function add( $key, $value, $exptime = 0, $flags = 0 ); - /** * Increase stored value of $key by $value while preserving its TTL * @param string $key Key to increase @@ -703,13 +704,21 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface { } } + /** + * @param int $exptime + * @return bool + */ + protected function expiryIsRelative( $exptime ) { + return ( $exptime != 0 && $exptime < ( 10 * self::TTL_YEAR ) ); + } + /** * Convert an optionally relative time to an absolute time * @param int $exptime * @return int */ - protected function convertExpiry( $exptime ) { - if ( $exptime != 0 && $exptime < ( 10 * self::TTL_YEAR ) ) { + protected function convertToExpiry( $exptime ) { + if ( $this->expiryIsRelative( $exptime ) ) { return (int)$this->getCurrentTime() + $exptime; } else { return $exptime; diff --git a/includes/libs/objectcache/HashBagOStuff.php b/includes/libs/objectcache/HashBagOStuff.php index 3c6520e1b3..eaea2d15b7 100644 --- a/includes/libs/objectcache/HashBagOStuff.php +++ b/includes/libs/objectcache/HashBagOStuff.php @@ -91,7 +91,7 @@ class HashBagOStuff extends BagOStuff { unset( $this->bag[$key] ); $this->bag[$key] = [ self::KEY_VAL => $value, - self::KEY_EXP => $this->convertExpiry( $exptime ), + self::KEY_EXP => $this->convertToExpiry( $exptime ), self::KEY_CAS => $this->token . ':' . ++self::$casCounter ]; diff --git a/includes/libs/objectcache/IExpiringStore.php b/includes/libs/objectcache/IExpiringStore.php index c1edff794d..61a4c618cf 100644 --- a/includes/libs/objectcache/IExpiringStore.php +++ b/includes/libs/objectcache/IExpiringStore.php @@ -21,7 +21,7 @@ */ /** - * Generic base class for storage interfaces. + * Generic interface for lightweight expiring object stores. * * Provides convenient TTL constants. * @@ -44,16 +44,19 @@ interface IExpiringStore { const TTL_INDEFINITE = 0; - // Attribute and QoS constants; higher QOS values with the same prefix rank higher... - // Medium attributes constants related to emulation or media type + // Emulation/fallback medium attribute (e.g. SQLBagOStuff) const ATTR_EMULATION = 1; + // Quality of service constants for ATTR_EMULATION (higher means faster) const QOS_EMULATION_SQL = 1; - // Medium attributes constants related to replica consistency - const ATTR_SYNCWRITES = 2; // SYNC_WRITES flag support + + // Replica synchronization/consistency attribute of medium when SYNC_WRITES is used + const ATTR_SYNCWRITES = 2; + // Quality of service constants for ATTR_SYNCWRITES (higher means more consistent) const QOS_SYNCWRITES_NONE = 1; // replication only supports eventual consistency or less const QOS_SYNCWRITES_BE = 2; // best effort synchronous with limited retries const QOS_SYNCWRITES_QC = 3; // write quorum applied directly to state machines where R+W > N const QOS_SYNCWRITES_SS = 4; // strict-serializable, nodes refuse reads if possible stale + // Generic "unknown" value that is useful for comparisons (e.g. always good enough) const QOS_UNKNOWN = INF; diff --git a/includes/libs/rdbms/database/DBConnRef.php b/includes/libs/rdbms/database/DBConnRef.php index 70b05835c1..cf582b7e99 100644 --- a/includes/libs/rdbms/database/DBConnRef.php +++ b/includes/libs/rdbms/database/DBConnRef.php @@ -254,7 +254,7 @@ class DBConnRef implements IDatabase { throw new DBUnexpectedError( $this->conn, 'Cannot close shared connection.' ); } - public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) { + public function query( $sql, $fname = __METHOD__, $flags = 0 ) { return $this->__call( __FUNCTION__, func_get_args() ); } diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index 9ce3086c38..dea7aab27d 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -280,6 +280,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware /** @var int No transaction is active */ const STATUS_TRX_NONE = 3; + /** @var int Writes to this temporary table do not affect lastDoneWrites() */ + const TEMP_NORMAL = 1; + /** @var int Writes to this temporary table effect lastDoneWrites() */ + const TEMP_PSEUDO_PERMANENT = 2; + /** * @note exceptions for missing libraries/drivers should be thrown in initConnection() * @param array $params Parameters passed from Database::factory() @@ -1135,47 +1140,55 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware /** * @param string $sql A SQL query - * @return bool Whether $sql is SQL for TEMPORARY table operation + * @param bool $pseudoPermanent Treat any table from CREATE TEMPORARY as pseudo-permanent + * @return int|null A self::TEMP_* constant for temp table operations or null otherwise */ - protected function registerTempTableOperation( $sql ) { + protected function registerTempTableWrite( $sql, $pseudoPermanent ) { + static $qt = '[`"\']?(\w+)[`"\']?'; // quoted table + if ( preg_match( - '/^CREATE\s+TEMPORARY\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i', + '/^CREATE\s+TEMPORARY\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?' . $qt . '/i', $sql, $matches ) ) { - $this->sessionTempTables[$matches[1]] = 1; + $type = $pseudoPermanent ? self::TEMP_PSEUDO_PERMANENT : self::TEMP_NORMAL; + $this->sessionTempTables[$matches[1]] = $type; - return true; + return $type; } elseif ( preg_match( - '/^DROP\s+(?:TEMPORARY\s+)?TABLE\s+(?:IF\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i', + '/^DROP\s+(?:TEMPORARY\s+)?TABLE\s+(?:IF\s+EXISTS\s+)?' . $qt . '/i', $sql, $matches ) ) { - $isTemp = isset( $this->sessionTempTables[$matches[1]] ); + $type = $this->sessionTempTables[$matches[1]] ?? null; unset( $this->sessionTempTables[$matches[1]] ); - return $isTemp; + return $type; } elseif ( preg_match( - '/^TRUNCATE\s+(?:TEMPORARY\s+)?TABLE\s+(?:IF\s+EXISTS\s+)?[`"\']?(\w+)[`"\']?/i', + '/^TRUNCATE\s+(?:TEMPORARY\s+)?TABLE\s+(?:IF\s+EXISTS\s+)?' . $qt . '/i', $sql, $matches ) ) { - return isset( $this->sessionTempTables[$matches[1]] ); + return $this->sessionTempTables[$matches[1]] ?? null; } elseif ( preg_match( - '/^(?:INSERT\s+(?:\w+\s+)?INTO|UPDATE|DELETE\s+FROM)\s+[`"\']?(\w+)[`"\']?/i', + '/^(?:(?:INSERT|REPLACE)\s+(?:\w+\s+)?INTO|UPDATE|DELETE\s+FROM)\s+' . $qt . '/i', $sql, $matches ) ) { - return isset( $this->sessionTempTables[$matches[1]] ); + return $this->sessionTempTables[$matches[1]] ?? null; } - return false; + return null; } - public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) { + public function query( $sql, $fname = __METHOD__, $flags = 0 ) { $this->assertTransactionStatus( $sql, $fname ); $this->assertHasConnectionHandle(); + $flags = (int)$flags; // b/c; this field used to be a bool + $ignoreErrors = $this->hasFlags( $flags, self::QUERY_SILENCE_ERRORS ); + $pseudoPermanent = $this->hasFlags( $flags, self::QUERY_PSEUDO_PERMANENT ); + $priorTransaction = $this->trxLevel; $priorWritesPending = $this->writesOrCallbacksPending(); $this->lastQuery = $sql; @@ -1184,8 +1197,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware # In theory, non-persistent writes are allowed in read-only mode, but due to things # like https://bugs.mysql.com/bug.php?id=33669 that might not work anyway... $this->assertIsWritableMaster(); - # Avoid treating temporary table operations as meaningful "writes" - $isEffectiveWrite = !$this->registerTempTableOperation( $sql ); + # Do not treat temporary table writes as "meaningful writes" that need committing. + # Profile them as reads. Integration tests can override this behavior via $flags. + $tableType = $this->registerTempTableWrite( $sql, $pseudoPermanent ); + $isEffectiveWrite = ( $tableType !== self::TEMP_NORMAL ); } else { $isEffectiveWrite = false; } @@ -1240,12 +1255,12 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->trxStatus = self::STATUS_TRX_ERROR; $this->trxStatusCause = $this->getQueryExceptionAndLog( $lastError, $lastErrno, $sql, $fname ); - $tempIgnore = false; // cannot recover + $ignoreErrors = false; // cannot recover $this->trxStatusIgnoredCause = null; } } - $this->reportQueryError( $lastError, $lastErrno, $sql, $fname, $tempIgnore ); + $this->reportQueryError( $lastError, $lastErrno, $sql, $fname, $ignoreErrors ); } return $this->resultObject( $ret ); @@ -1514,17 +1529,17 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware /** * Report a query error. Log the error, and if neither the object ignore - * flag nor the $tempIgnore flag is set, throw a DBQueryError. + * flag nor the $ignoreErrors flag is set, throw a DBQueryError. * * @param string $error * @param int $errno * @param string $sql * @param string $fname - * @param bool $tempIgnore + * @param bool $ignoreErrors * @throws DBQueryError */ - public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) { - if ( $tempIgnore ) { + public function reportQueryError( $error, $errno, $sql, $fname, $ignoreErrors = false ) { + if ( $ignoreErrors ) { $this->queryLogger->debug( "SQL ERROR (ignored): $error\n" ); } else { $exception = $this->getQueryExceptionAndLog( $error, $errno, $sql, $fname ); @@ -4684,6 +4699,15 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware $this->indexAliases = $aliases; } + /** + * @param int $field + * @param int $flags + * @return bool + */ + protected function hasFlags( $field, $flags ) { + return ( ( $field & $flags ) === $flags ); + } + /** * Get the underlying binding connection handle * diff --git a/includes/libs/rdbms/database/DatabaseDomain.php b/includes/libs/rdbms/database/DatabaseDomain.php index 7559b06c3b..8d8285426a 100644 --- a/includes/libs/rdbms/database/DatabaseDomain.php +++ b/includes/libs/rdbms/database/DatabaseDomain.php @@ -43,15 +43,17 @@ class DatabaseDomain { */ public function __construct( $database, $schema, $prefix ) { if ( $database !== null && ( !is_string( $database ) || !strlen( $database ) ) ) { - throw new InvalidArgumentException( "Database must be null or a non-empty string." ); + throw new InvalidArgumentException( 'Database must be null or a non-empty string.' ); } $this->database = $database; if ( $schema !== null && ( !is_string( $schema ) || !strlen( $schema ) ) ) { - throw new InvalidArgumentException( "Schema must be null or a non-empty string." ); + 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." ); + 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; } @@ -133,7 +135,7 @@ class DatabaseDomain { return true; // even the prefix doesn't matter } - $other = ( $other instanceof self ) ? $other : self::newFromId( $other ); + $other = self::newFromId( $other ); return ( ( $this->database === $other->database || $this->database === null ) && @@ -188,7 +190,7 @@ class DatabaseDomain { * @return string */ private function convertToString() { - $parts = [ $this->database ]; + $parts = [ (string)$this->database ]; if ( $this->schema !== null ) { $parts[] = $this->schema; } diff --git a/includes/libs/rdbms/database/DatabaseMssql.php b/includes/libs/rdbms/database/DatabaseMssql.php index ad5cf98f5c..57412c22f4 100644 --- a/includes/libs/rdbms/database/DatabaseMssql.php +++ b/includes/libs/rdbms/database/DatabaseMssql.php @@ -1328,8 +1328,9 @@ class DatabaseMssql extends Database { /** * @param string $name - * @param string $format - * @return string + * @param string $format One of "quoted" (default), "raw", or "split". + * @return string|array When the requested $format is "split", a list of database, schema, and + * table name is returned. Database and schema can be `false`. */ function tableName( $name, $format = 'quoted' ) { # Replace reserved words with better ones @@ -1344,18 +1345,17 @@ class DatabaseMssql extends Database { /** * call this instead of tableName() in the updater when renaming tables * @param string $name - * @param string $format One of quoted, raw, or split - * @return string + * @param string $format One of "quoted" (default), "raw", or "split". + * @return string|array When the requested $format is "split", a list of database, schema, and + * table name is returned. Database and schema can be `false`. + * @private */ function realTableName( $name, $format = 'quoted' ) { $table = parent::tableName( $name, $format ); if ( $format == 'split' ) { // Used internally, we want the schema split off from the table name and returned // as a list with 3 elements (database, schema, table) - $table = explode( '.', $table ); - while ( count( $table ) < 3 ) { - array_unshift( $table, false ); - } + return array_pad( explode( '.', $table, 3 ), -3, false ); } return $table; } diff --git a/includes/libs/rdbms/database/DatabaseMysqlBase.php b/includes/libs/rdbms/database/DatabaseMysqlBase.php index 25d78da18d..7fccd57f45 100644 --- a/includes/libs/rdbms/database/DatabaseMysqlBase.php +++ b/includes/libs/rdbms/database/DatabaseMysqlBase.php @@ -225,6 +225,39 @@ abstract class DatabaseMysqlBase extends Database { } } + protected function doSelectDomain( DatabaseDomain $domain ) { + if ( $domain->getSchema() !== null ) { + throw new DBExpectedError( $this, __CLASS__ . ": domain schemas are not supported." ); + } + + $database = $domain->getDatabase(); + // A null database means "don't care" so leave it as is and update the table prefix + if ( $database === null ) { + $this->currentDomain = new DatabaseDomain( + $this->currentDomain->getDatabase(), + null, + $domain->getTablePrefix() + ); + + return true; + } + + if ( $database !== $this->getDBname() ) { + $sql = 'USE ' . $this->addIdentifierQuotes( $database ); + $ret = $this->doQuery( $sql ); + if ( $ret === false ) { + $error = $this->lastError(); + $errno = $this->lastErrno(); + $this->reportQueryError( $error, $errno, $sql, __METHOD__ ); + } + } + + // Update that domain fields on success (no exception thrown) + $this->currentDomain = $domain; + + return true; + } + /** * Open a connection to a MySQL server * @@ -1427,7 +1460,7 @@ abstract class DatabaseMysqlBase extends Database { $oldName = $this->addIdentifierQuotes( $oldName ); $query = "CREATE $tmp TABLE $newName (LIKE $oldName)"; - return $this->query( $query, $fname ); + return $this->query( $query, $fname, $this::QUERY_PSEUDO_PERMANENT ); } /** diff --git a/includes/libs/rdbms/database/DatabaseMysqli.php b/includes/libs/rdbms/database/DatabaseMysqli.php index 15eeccf02c..1a5cdab78d 100644 --- a/includes/libs/rdbms/database/DatabaseMysqli.php +++ b/includes/libs/rdbms/database/DatabaseMysqli.php @@ -178,25 +178,6 @@ class DatabaseMysqli extends DatabaseMysqlBase { return $conn->affected_rows; } - function doSelectDomain( DatabaseDomain $domain ) { - if ( $domain->getSchema() !== null ) { - throw new DBExpectedError( $this, __CLASS__ . ": domain schemas are not supported." ); - } - - $database = $domain->getDatabase(); - if ( $database !== $this->getDBname() ) { - $conn = $this->getBindingHandle(); - if ( !$conn->select_db( $database ) ) { - throw new DBExpectedError( $this, "Could not select database '$database'." ); - } - } - - // Update that domain fields on success (no exception thrown) - $this->currentDomain = $domain; - - return true; - } - /** * @param mysqli_result $res * @return bool diff --git a/includes/libs/rdbms/database/DatabasePostgres.php b/includes/libs/rdbms/database/DatabasePostgres.php index 481dd9a6be..a5dc1713dc 100644 --- a/includes/libs/rdbms/database/DatabasePostgres.php +++ b/includes/libs/rdbms/database/DatabasePostgres.php @@ -116,7 +116,7 @@ class DatabasePostgres extends Database { $connectVars['port'] = (int)$this->port; } if ( $this->flags & self::DBO_SSL ) { - $connectVars['sslmode'] = 1; + $connectVars['sslmode'] = 'require'; } $this->connectString = $this->makeConnectionString( $connectVars ); @@ -819,8 +819,12 @@ __INDEXATTR__; $temporary = $temporary ? 'TEMPORARY' : ''; - $ret = $this->query( "CREATE $temporary TABLE $newNameE " . - "(LIKE $oldNameE INCLUDING DEFAULTS INCLUDING INDEXES)", $fname ); + $ret = $this->query( + "CREATE $temporary TABLE $newNameE " . + "(LIKE $oldNameE INCLUDING DEFAULTS INCLUDING INDEXES)", + $fname, + $this::QUERY_PSEUDO_PERMANENT + ); if ( !$ret ) { return $ret; } @@ -842,7 +846,10 @@ __INDEXATTR__; $fieldE = $this->addIdentifierQuotes( $field ); $newSeqE = $this->addIdentifierQuotes( $newSeq ); $newSeqQ = $this->addQuotes( $newSeq ); - $this->query( "CREATE $temporary SEQUENCE $newSeqE OWNED BY $newNameE.$fieldE", $fname ); + $this->query( + "CREATE $temporary SEQUENCE $newSeqE OWNED BY $newNameE.$fieldE", + $fname + ); $this->query( "ALTER TABLE $newNameE ALTER COLUMN $fieldE SET DEFAULT nextval({$newSeqQ}::regclass)", $fname diff --git a/includes/libs/rdbms/database/DatabaseSqlite.php b/includes/libs/rdbms/database/DatabaseSqlite.php index f2bc01d5b6..82a7e35a31 100644 --- a/includes/libs/rdbms/database/DatabaseSqlite.php +++ b/includes/libs/rdbms/database/DatabaseSqlite.php @@ -1018,7 +1018,7 @@ class DatabaseSqlite extends Database { } } - $res = $this->query( $sql, $fname ); + $res = $this->query( $sql, $fname, self::QUERY_PSEUDO_PERMANENT ); // Take over indexes $indexList = $this->query( 'PRAGMA INDEX_LIST(' . $this->addQuotes( $oldName ) . ')' ); diff --git a/includes/libs/rdbms/database/IDatabase.php b/includes/libs/rdbms/database/IDatabase.php index 6f58cc66a3..eac9baed0e 100644 --- a/includes/libs/rdbms/database/IDatabase.php +++ b/includes/libs/rdbms/database/IDatabase.php @@ -106,6 +106,14 @@ interface IDatabase { /** @var int Enable compression in connection protocol */ const DBO_COMPRESS = 512; + /** @var int Ignore query errors and return false when they happen */ + const QUERY_SILENCE_ERRORS = 1; // b/c for 1.32 query() argument; note that (int)true = 1 + /** + * @var int Treat the TEMPORARY table from the given CREATE query as if it is + * permanent as far as write tracking is concerned. This is useful for testing. + */ + const QUERY_PSEUDO_PERMANENT = 2; + /** * A string describing the current software version, and possibly * other details in a user-friendly way. Will be listed on Special:Version, etc. @@ -527,13 +535,13 @@ interface IDatabase { * @param string $sql SQL query * @param string $fname Name of the calling function, for profiling/SHOW PROCESSLIST * comment (you can use __METHOD__ or add some extra info) - * @param bool $tempIgnore Whether to avoid throwing an exception on errors... - * maybe best to catch the exception instead? + * @param int $flags Bitfield of IDatabase::QUERY_* constants. Note that suppression + * of errors is best handled by try/catch rather than using one of these flags. * @return bool|IResultWrapper True for a successful write query, IResultWrapper object - * for a successful read query, or false on failure if $tempIgnore set + * for a successful read query, or false on failure if QUERY_SILENCE_ERRORS is set. * @throws DBError */ - public function query( $sql, $fname = __METHOD__, $tempIgnore = false ); + public function query( $sql, $fname = __METHOD__, $flags = 0 ); /** * Free a result object returned by query() or select(). It's usually not diff --git a/includes/libs/rdbms/database/resultwrapper/ResultWrapper.php b/includes/libs/rdbms/database/resultwrapper/ResultWrapper.php index 1355e2263e..a9befc2bae 100644 --- a/includes/libs/rdbms/database/resultwrapper/ResultWrapper.php +++ b/includes/libs/rdbms/database/resultwrapper/ResultWrapper.php @@ -69,7 +69,6 @@ class ResultWrapper implements IResultWrapper { public function free() { if ( $this->db ) { - $this->db->freeResult( $this ); $this->db = null; } $this->result = null; diff --git a/includes/libs/rdbms/exception/DBQueryError.php b/includes/libs/rdbms/exception/DBQueryError.php index b1353b793e..9b664fc3d0 100644 --- a/includes/libs/rdbms/exception/DBQueryError.php +++ b/includes/libs/rdbms/exception/DBQueryError.php @@ -45,7 +45,7 @@ class DBQueryError extends DBExpectedError { public function __construct( IDatabase $db, $error, $errno, $sql, $fname, $message = null ) { if ( $message === null ) { if ( $db instanceof Database && $db->wasConnectionError( $errno ) ) { - $message = "A connection error occurred. \n" . + $message = "A connection error occurred during a query. \n" . "Query: $sql\n" . "Function: $fname\n" . "Error: $errno $error\n"; diff --git a/includes/libs/rdbms/lbfactory/LBFactory.php b/includes/libs/rdbms/lbfactory/LBFactory.php index 007ac20662..3a8f2e1fcd 100644 --- a/includes/libs/rdbms/lbfactory/LBFactory.php +++ b/includes/libs/rdbms/lbfactory/LBFactory.php @@ -654,7 +654,7 @@ abstract class LBFactory implements ILBFactory { } public function closeAll() { - $this->forEachLBCallMethod( 'closeAll', [] ); + $this->forEachLBCallMethod( 'closeAll' ); } public function setAgentName( $agent ) { diff --git a/includes/libs/rdbms/lbfactory/LBFactoryMulti.php b/includes/libs/rdbms/lbfactory/LBFactoryMulti.php index 189ceee56d..aec99f4ec7 100644 --- a/includes/libs/rdbms/lbfactory/LBFactoryMulti.php +++ b/includes/libs/rdbms/lbfactory/LBFactoryMulti.php @@ -89,9 +89,6 @@ class LBFactoryMulti extends LBFactory { */ private $readOnlyBySection = []; - /** @var array Load balancer factory configuration */ - private $conf; - /** @var LoadBalancer[] */ private $mainLBs = []; @@ -166,7 +163,6 @@ class LBFactoryMulti extends LBFactory { public function __construct( array $conf ) { parent::__construct( $conf ); - $this->conf = $conf; $required = [ 'sectionsByDB', 'sectionLoads', 'serverTemplate' ]; $optional = [ 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName', 'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer', diff --git a/includes/logging/LogEntry.php b/includes/logging/LogEntry.php index 33dd69b8fd..5cad31f2fe 100644 --- a/includes/logging/LogEntry.php +++ b/includes/logging/LogEntry.php @@ -793,8 +793,24 @@ class ManualLogEntry extends LogEntryBase implements Taggable { * @param string $to One of: rcandudp (default), rc, udp */ public function publish( $newId, $to = 'rcandudp' ) { + $canAddTags = true; + // FIXME: this code should be removed once all callers properly call publish() + if ( $to === 'udp' && !$newId && !$this->getAssociatedRevId() ) { + \MediaWiki\Logger\LoggerFactory::getInstance( 'logging' )->warning( + 'newId and/or revId must be set when calling ManualLogEntry::publish()', + [ + 'newId' => $newId, + 'to' => $to, + 'revId' => $this->getAssociatedRevId(), + // pass a new exception to register the stack trace + 'exception' => new RuntimeException() + ] + ); + $canAddTags = false; + } + DeferredUpdates::addCallableUpdate( - function () use ( $newId, $to ) { + function () use ( $newId, $to, $canAddTags ) { $log = new LogPage( $this->getType() ); if ( !$log->isRestricted() ) { Hooks::runWithoutAbort( 'ManualLogEntryBeforePublish', [ $this ] ); @@ -806,9 +822,14 @@ class ManualLogEntry extends LogEntryBase implements Taggable { $rc->save( $rc::SEND_NONE ); } else { $tags = $this->getTags(); - if ( $tags ) { - $revId = $this->getAssociatedRevId(); // Use null if $revId is 0 - ChangeTags::addTags( $tags, null, $revId > 0 ? $revId : null, $newId ); + if ( $tags && $canAddTags ) { + $revId = $this->getAssociatedRevId(); + ChangeTags::addTags( + $tags, + null, + $revId > 0 ? $revId : null, + $newId > 0 ? $newId : null + ); } } diff --git a/includes/objectcache/ObjectCache.php b/includes/objectcache/ObjectCache.php index dc8b146c3a..fed085425a 100644 --- a/includes/objectcache/ObjectCache.php +++ b/includes/objectcache/ObjectCache.php @@ -238,7 +238,6 @@ class ObjectCache { global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType; $candidates = [ $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType ]; foreach ( $candidates as $candidate ) { - $cache = false; if ( $candidate !== CACHE_NONE && $candidate !== CACHE_ANYTHING ) { $cache = self::getInstance( $candidate ); // CACHE_ACCEL might default to nothing if no APCu diff --git a/includes/objectcache/SqlBagOStuff.php b/includes/objectcache/SqlBagOStuff.php index b2d61a8925..e450212dc6 100644 --- a/includes/objectcache/SqlBagOStuff.php +++ b/includes/objectcache/SqlBagOStuff.php @@ -344,7 +344,7 @@ class SqlBagOStuff extends BagOStuff { if ( $exptime == 0 ) { $encExpiry = $this->getMaxDateTime( $db ); } else { - $exptime = $this->convertExpiry( $exptime ); + $exptime = $this->convertToExpiry( $exptime ); $encExpiry = $db->timestamp( $exptime ); } foreach ( $serverKeys as $tableName => $tableKeys ) { @@ -406,7 +406,7 @@ class SqlBagOStuff extends BagOStuff { if ( $exptime == 0 ) { $encExpiry = $this->getMaxDateTime( $db ); } else { - $exptime = $this->convertExpiry( $exptime ); + $exptime = $this->convertToExpiry( $exptime ); $encExpiry = $db->timestamp( $exptime ); } // (T26425) use a replace if the db supports it instead of @@ -542,7 +542,7 @@ class SqlBagOStuff extends BagOStuff { $db = $this->getDB( $serverIndex ); $db->update( $tableName, - [ 'exptime' => $db->timestamp( $this->convertExpiry( $expiry ) ) ], + [ 'exptime' => $db->timestamp( $this->convertToExpiry( $expiry ) ) ], [ 'keyname' => $key, 'exptime > ' . $db->addQuotes( $db->timestamp( time() ) ) ], __METHOD__ ); diff --git a/includes/poolcounter/PoolCounter.php b/includes/poolcounter/PoolCounter.php index ba0b4cb318..060faec52e 100644 --- a/includes/poolcounter/PoolCounter.php +++ b/includes/poolcounter/PoolCounter.php @@ -39,7 +39,7 @@ * that start with "nowait:". However, only 0 timeouts (non-blocking requests) * can be used with "nowait:" keys. * - * By default PoolCounter_Stub is used, which provides no locking. You + * By default PoolCounterNull is used, which provides no locking. You * can get a useful one in the PoolCounter extension. */ abstract class PoolCounter { @@ -111,7 +111,7 @@ abstract class PoolCounter { public static function factory( $type, $key ) { global $wgPoolCounterConf; if ( !isset( $wgPoolCounterConf[$type] ) ) { - return new PoolCounter_Stub; + return new PoolCounterNull; } $conf = $wgPoolCounterConf[$type]; $class = $conf['class']; @@ -208,23 +208,3 @@ abstract class PoolCounter { return $type . ':' . ( hexdec( substr( sha1( $key ), 0, 4 ) ) % $slots ); } } - -// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps -class PoolCounter_Stub extends PoolCounter { - - public function __construct() { - /* No parameters needed */ - } - - public function acquireForMe() { - return Status::newGood( PoolCounter::LOCKED ); - } - - public function acquireForAnyone() { - return Status::newGood( PoolCounter::LOCKED ); - } - - public function release() { - return Status::newGood( PoolCounter::RELEASED ); - } -} diff --git a/includes/poolcounter/PoolCounterNull.php b/includes/poolcounter/PoolCounterNull.php new file mode 100644 index 0000000000..95a5057131 --- /dev/null +++ b/includes/poolcounter/PoolCounterNull.php @@ -0,0 +1,44 @@ +getName(), 'QueryPage::recache', 'vslow' ] ); @@ -500,6 +506,9 @@ abstract class QueryPage extends SpecialPage { return [ 'value' ]; } + /** + * @return string + */ public function getCachedTimestamp() { if ( is_null( $this->cachedTimestamp ) ) { $dbr = wfGetDB( DB_REPLICA ); @@ -754,98 +763,6 @@ abstract class QueryPage extends SpecialPage { function preprocessResults( $db, $res ) { } - /** - * Similar to above, but packaging in a syndicated feed instead of a web page - * @param string $class - * @param int $limit - * @return bool - */ - function doFeed( $class = '', $limit = 50 ) { - if ( !$this->getConfig()->get( 'Feed' ) ) { - $this->getOutput()->addWikiMsg( 'feed-unavailable' ); - return false; - } - - $limit = min( $limit, $this->getConfig()->get( 'FeedLimit' ) ); - - $feedClasses = $this->getConfig()->get( 'FeedClasses' ); - if ( isset( $feedClasses[$class] ) ) { - /** @var RSSFeed|AtomFeed $feed */ - $feed = new $feedClasses[$class]( - $this->feedTitle(), - $this->feedDesc(), - $this->feedUrl() ); - $feed->outHeader(); - - $res = $this->reallyDoQuery( $limit, 0 ); - foreach ( $res as $obj ) { - $item = $this->feedResult( $obj ); - if ( $item ) { - $feed->outItem( $item ); - } - } - - $feed->outFooter(); - return true; - } else { - return false; - } - } - - /** - * Override for custom handling. If the titles/links are ok, just do - * feedItemDesc() - * @param object $row - * @return FeedItem|null - */ - function feedResult( $row ) { - if ( !isset( $row->title ) ) { - return null; - } - $title = Title::makeTitle( intval( $row->namespace ), $row->title ); - if ( $title ) { - $date = $row->timestamp ?? ''; - $comments = ''; - if ( $title ) { - $talkpage = $title->getTalkPage(); - $comments = $talkpage->getFullURL(); - } - - return new FeedItem( - $title->getPrefixedText(), - $this->feedItemDesc( $row ), - $title->getFullURL(), - $date, - $this->feedItemAuthor( $row ), - $comments ); - } else { - return null; - } - } - - function feedItemDesc( $row ) { - return isset( $row->comment ) ? htmlspecialchars( $row->comment ) : ''; - } - - function feedItemAuthor( $row ) { - return $row->user_text ?? ''; - } - - function feedTitle() { - $desc = $this->getDescription(); - $code = $this->getConfig()->get( 'LanguageCode' ); - $sitename = $this->getConfig()->get( 'Sitename' ); - return "$sitename - $desc [$code]"; - } - - function feedDesc() { - return $this->msg( 'tagline' )->text(); - } - - function feedUrl() { - return $this->getPageTitle()->getFullURL(); - } - /** * Creates a new LinkBatch object, adds all pages from the passed ResultWrapper (MUST include * title and optional the namespace field) and executes the batch. This operation will pre-cache diff --git a/languages/i18n/az.json b/languages/i18n/az.json index b917b9de32..cb005a9ae1 100644 --- a/languages/i18n/az.json +++ b/languages/i18n/az.json @@ -33,7 +33,8 @@ "Neriman2003", "Fitoschido", "Toghrul Rahimli", - "Vlad5250" + "Vlad5250", + "Hüseynzadə" ] }, "tog-underline": "Keçidlərin altını xətlə:", @@ -425,7 +426,7 @@ "userlogin-signwithsecure": "Etibarlı bağlantıdan istifadə edin", "cannotlogin-title": "Daxil olmaq mümkün olmadı", "cannotlogin-text": "Daxil olmaq mümkün deyil.", - "cannotloginnow-title": "Daxil olmaq indi mümkün deyil", + "cannotloginnow-title": "Ä°ndi daxil olmaq mümkün deyil", "cannotloginnow-text": "$1 istifadə edərkən daxil olmaq mümkün deyil.", "cannotcreateaccount-title": "Hesablar yaradıla bilmədi", "cannotcreateaccount-text": "Bu vikidə birbaşa hesab yaratma aktiv deyil.", diff --git a/languages/i18n/be-tarask.json b/languages/i18n/be-tarask.json index ec4d676921..6a17cc9adb 100644 --- a/languages/i18n/be-tarask.json +++ b/languages/i18n/be-tarask.json @@ -1988,7 +1988,7 @@ "pager-newer-n": "$1 {{PLURAL:$1|навейшая|навейшыя|навейшых}}", "pager-older-n": "$1 {{PLURAL:$1|старэйшая|старэйшыя|старэйшых}}", "suppress": "Падавіць вэрсію", - "querypage-disabled": "Гэта спэцыяльная старонка адключаная для падвышэньня прадукцыйнасьці", + "querypage-disabled": "Гэтая спэцыяльная старонка адключаная для падвышэньня прадукцыйнасьці.", "apihelp": "Даведка API", "apihelp-no-such-module": "Модуль «$1» ня знойдзены.", "apisandbox": "Пясочніца API", @@ -2259,6 +2259,8 @@ "deleting-backlinks-warning": "Увага: [[Special:WhatLinksHere/{{FULLPAGENAME}}|іншыя старонкі]] ўключаюць або спасылаюцца на старонку, якую вы зьбіраецеся выдаліць.", "deleting-subpages-warning": "Папярэджаньне: старонка, якую вы зьбіраецеся выдаліць, мае [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|$1 падстаронку|$1 падстаронкі|$1 падстаронак|51=болей за 50 падстаронак}}]].", "rollback": "Адкаціць рэдагаваньні", + "rollback-confirmation-confirm": "Калі ласка, пацьвердзіце:", + "rollback-confirmation-yes": "Адкаціць", "rollbacklink": "адкат", "rollbacklinkcount": "адкаціць $1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}", "rollbacklinkcount-morethan": "адкаціць больш за $1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}", diff --git a/languages/i18n/bg.json b/languages/i18n/bg.json index 12ce570d11..5df588db49 100644 --- a/languages/i18n/bg.json +++ b/languages/i18n/bg.json @@ -3375,6 +3375,9 @@ "pagelang-reason": "Причина", "pagelang-submit": "Изпращане", "pagelang-nonexistent-page": "Страницата $1 не съществува.", + "pagelang-unchanged-language": "Страницата $1 вече е със зададен език $2.", + "pagelang-unchanged-language-default": "Страницата $1 вече е със зададен език, съвпадащ с езика по подразбиране за това уики.", + "pagelang-db-failed": "Базата данни не успя да смени езика на страницата.", "right-pagelang": "Промяна езика на страница", "action-pagelang": "промяна езика на страницата", "log-name-pagelang": "Дневник на езиковите промени", @@ -3399,6 +3402,8 @@ "mediastatistics-header-multimedia": "Мултимедия", "mediastatistics-header-office": "Офис", "mediastatistics-header-total": "Всички файлове", + "json-error-state-mismatch": "Невалиден или грешно структуриран JSON", + "json-error-ctrl-char": "Грешка в контролния знак. Вероятно е неправилно кодиран", "json-error-syntax": "Синтактична грешка", "headline-anchor-title": "Препратка към този раздел", "special-characters-group-latin": "Латиница", @@ -3482,6 +3487,11 @@ "log-action-filter-protect-move_prot": "Преместване на защитата", "log-action-filter-rights-rights": "Ръчна промяна", "log-action-filter-rights-autopromote": "Автоматична промяна", + "log-action-filter-suppress-event": "Потискане на дневника", + "log-action-filter-suppress-revision": "Потискане на версията", + "log-action-filter-suppress-delete": "Потискане на страницата", + "log-action-filter-suppress-block": "Потискане на потребителя чрез блокиране", + "log-action-filter-suppress-reblock": "Потискане на потребителя чрез повторно блокиране", "log-action-filter-upload-upload": "Ново качване", "log-action-filter-upload-overwrite": "Повторно качване", "log-action-filter-upload-revert": "Връщане", diff --git a/languages/i18n/bn.json b/languages/i18n/bn.json index f2c60166a1..7ba93cc5f0 100644 --- a/languages/i18n/bn.json +++ b/languages/i18n/bn.json @@ -2269,6 +2269,9 @@ "deleting-backlinks-warning": "সতর্কীকরণ: আপনি যেটি মুছে ফেলতে যাচ্ছেন তা [[Special:WhatLinksHere/{{FULLPAGENAME}}|অন্যান্য পাতাসমূহে]] সংযুক্ত অথবা অন্তর্ভুক্ত রয়েছে।", "deleting-subpages-warning": "সতর্কীকরণ: আপনি যে পাতাটি মুছে ফেলতে যাচ্ছেন তাঁর [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|একটি উপপাতা|$1টি উপপাতা|51=৫০টির বেশী}}]] রয়েছে।", "rollback": "সম্পাদনা ফিরিয়ে নিন", + "rollback-confirmation-confirm": "দয়া করে নিশ্চিত করুন:", + "rollback-confirmation-yes": "পুনর্বহাল করুন", + "rollback-confirmation-no": "বাতিল করুন", "rollbacklink": "পুনর্বহাল", "rollbacklinkcount": "$1টি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন", "rollbacklinkcount-morethan": "$1টির বেশি {{PLURAL:$1|সম্পাদনা}} রোলব্যাক করুন", diff --git a/languages/i18n/diq.json b/languages/i18n/diq.json index c17a6bbdee..366a783d91 100644 --- a/languages/i18n/diq.json +++ b/languages/i18n/diq.json @@ -2076,6 +2076,7 @@ "delete-warning-toobig": "no pel wayirê tarixê vurnayiş ê derg o, $1 {{PLURAL:$1|revizyonê|revizyonê}} seri de.\nhewn a kerdışê ıney {{SITENAME}} şuxul bıne gırano;\nbı diqqet dewam kerê.", "deleteprotected": "Şıma nêşenê ena perer esternê, çıkı per starya ya.", "rollback": "vurnayişan tepiya bıger", + "rollback-confirmation-no": "Bıtexelne", "rollbacklink": "ageyrayış", "rollbacklinkcount": "$1 {{PLURAL:$1|vurnayış|vurnayışi}} peyd gıroti", "rollbacklinkcount-morethan": "$1 {{PLURAL:$1|vurnayış|vuranyışi}} tewr peyd gırot", diff --git a/languages/i18n/et.json b/languages/i18n/et.json index 6f32bc033e..a8515a946c 100644 --- a/languages/i18n/et.json +++ b/languages/i18n/et.json @@ -78,6 +78,7 @@ "tog-norollbackdiff": "Ära näita erinevusi pärast tühistamist", "tog-useeditwarning": "Hoiata mind, kui lahkun redigeerimisleheküljelt muudatusi salvestamata", "tog-prefershttps": "Kasuta sisselogimisel alati turvalist ühendust", + "tog-showrollbackconfirmation": "Küsi tühistamislingile klõpsamise järel kinnitust", "underline-always": "Alati", "underline-never": "Mitte kunagi", "underline-default": "Kujunduse või brauseri vaikeväärtus", @@ -463,6 +464,7 @@ "badretype": "Sisestatud paroolid ei lange kokku.", "usernameinprogress": "Selle kasutajanimega konto loomine on juba pooleli.\nPalun oota.", "userexists": "Sisestatud kasutajanimi on juba kasutusel.\nPalun valige uus nimi.", + "createacct-normalization": "Tehniliste piirangute tõttu kohandatakse sinu kasutajanimi kujule \"$2\".", "loginerror": "Viga sisselogimisel", "createacct-error": "Tõrge konto loomisel", "createaccounterror": "Kasutajakonto loomine ebaõnnestus: $1", @@ -2271,6 +2273,9 @@ "deleting-backlinks-warning": "Hoiatus: [[Special:WhatLinksHere/{{FULLPAGENAME}}|Teised leheküljed]] viitavad leheküljele, mida oled kustutamas, või see lehekülg on kasutuses mallina.", "deleting-subpages-warning": "Hoiatus: Oled kustutamas lehekülge, millel on [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|alamlehekülg|$1 alamlehekülge|51=üle 50 alamlehekülje}}]].", "rollback": "Tühista muudatused", + "rollback-confirmation-confirm": "Palun kinnita:", + "rollback-confirmation-yes": "Tühista", + "rollback-confirmation-no": "Loobu", "rollbacklink": "tühista", "rollbacklinkcount": "tühista {{PLURAL:$1|üks muudatus|$1 muudatust}}", "rollbacklinkcount-morethan": "tühista üle {{PLURAL:$1|ühe muudatuse|10 muudatuse}}", @@ -2477,6 +2482,8 @@ "ipb-confirm": "Kinnita blokeering", "ipb-sitewide": "Saidiülene", "ipb-partial": "Osaline", + "ipb-sitewide-help": "Viki kõigi lehekülgede muutmine ja kogu ülejäänud kaastöö.", + "ipb-partial-help": "Teatud lehekülgede või nimeruumide muutmine.", "ipb-pages-label": "Leheküljed", "ipb-namespaces-label": "Nimeruumid", "badipaddress": "Vigane IP-aadress", @@ -3055,6 +3062,7 @@ "confirm-unwatch-top": "Kas eemaldad selle lehekülje oma jälgimisloendist?", "confirm-rollback-button": "Sobib", "confirm-rollback-top": "Kas tühistad sellel leheküljel tehtud muudatused?", + "confirm-rollback-bottom": "See toiming tühistab koheselt valitud muudatused sellel leheküljel.", "confirm-mcrrestore-title": "Redaktsiooni taastamine", "confirm-mcrundo-title": "Muudatuse eemaldamine", "mcrundofailed": "Eemaldamine ebaõnnestus", @@ -3440,7 +3448,7 @@ "logentry-rights-autopromote": "$1 {{GENDER:$2|viidi}} automaatselt üle teise rühma; enne oli $4, nüüd on $5", "logentry-upload-upload": "$1 {{GENDER:$2|laadis üles}} faili $3", "logentry-upload-overwrite": "$1 {{GENDER:$2|laadis üles}} uue versiooni failist $3", - "logentry-upload-revert": "$1 {{GENDER:$2|laadis üles}} faili $3", + "logentry-upload-revert": "$1 {{GENDER:$2|taastas}} faili $3 vanema versiooni", "log-name-managetags": "Märgiste haldamise logi", "log-description-managetags": "Sellel leheküljel on toodud [[Special:Tags|märgiste]] haldamisega seotud tegevused. Logis on ainult toimingud, mida administraatorid on teinud käsitsi. Siin puuduvad logisissekanded viki tarkvaras koostatud või sealt kustutatud märgiste kohta.", "logentry-managetags-create": "$1 {{GENDER:$2|koostas}} märgise \"$4\"", @@ -3675,6 +3683,7 @@ "log-action-filter-suppress-reblock": "Kasutaja varjamine taasblokeerimise teel", "log-action-filter-upload-upload": "Uus üleslaadimine", "log-action-filter-upload-overwrite": "Uuesti üleslaadimine", + "log-action-filter-upload-revert": "Taastamine", "authmanager-authn-not-in-progress": "Autentimine pole teoksil või seansiandmed läksid kaduma. Palun alusta uuesti.", "authmanager-authn-no-primary": "Ette antud autentimisandmeid ei õnnestunud autentida.", "authmanager-authn-no-local-user": "Ette antud autentimisandmed pole selles vikis seotud ühegi kasutajaga.", @@ -3775,6 +3784,8 @@ "passwordpolicies-policy-maximalpasswordlength": "Parool peab olema $1 {{PLURAL:$1|märgist}} lühem.", "passwordpolicies-policy-passwordcannotbepopular": "Parool ei tohi olla {{PLURAL:$1|populaarne parool|$1 populaarse parooli loendis}}.", "passwordpolicies-policy-passwordnotinlargeblacklist": "Parool ei saa olla 100 000 kõige levinuma parooli loendis.", + "passwordpolicies-policyflag-forcechange": "peab muutma sisselogimisel", + "passwordpolicies-policyflag-suggestchangeonlogin": "soovita muutmist sisselogimisel", "easydeflate-invaliddeflate": "Ette antud sisu ei ole õigesti vähendatud", "unprotected-js": "Turvalisuse huvides ei saa JavaScripti laadida kaitsmata lehekülgedelt. Palun koosta JavaScripti ainult nimeruumis MediaWiki või kasutajate nimeruumi alamleheküljel." } diff --git a/languages/i18n/fa.json b/languages/i18n/fa.json index 76decac821..f7fd250b4c 100644 --- a/languages/i18n/fa.json +++ b/languages/i18n/fa.json @@ -72,7 +72,8 @@ "Physicsch", "Nbi", "Amjad Khan", - "Ahmad252" + "Ahmad252", + "FarsiNevis" ] }, "tog-underline": "خط کشیدن زیر پیوندها:", @@ -705,7 +706,8 @@ "subject-preview": "پیش‌نمایش موضوع:", "previewerrortext": "در زمان تلاش برای نمایش دادن تغییرات شما، خطایی رخ داد.", "blockedtitle": "کاربر بسته شده‌است", - "blockedtext-partial": "حساب کاربری یا آدرس آی‌پی شما از انجام تغییرات در این صفحه منع شده‌است. شما همچنان می‌توانید در دیگر صفحه‌های این ویکی ویرایش کنید. برای مشاهده جزئیات کامل قطع دسترسی به [[ویژه:مشارکت‌های من|مشارکت‌های حساب]] مراجعه کنید.\n\nاین قطع دسترسی توسط $1 انجام گرفته‌است.\n\nدلیل قطع دسترسی $2 است.\n\n* زمان آغاز قطع دسترسی: $8\n* زمان پایان قطع دسترسی: $6\n* موارد مورد نظر: $7\n* شناسه قطع دسترسی: #$5", + "blocked-email-user": "دسترسی حساب کاربری شما از ارسال ایمیل قطع شده است. شما همچنان می‌توانید سایر صفحات این ویکی را ویرایش کنید.می‌توانید جزئیات کامل قطع دسترسی را در [[Special:MyContributions|مشارکت‌های حساب]] ببینید.\n\nقطع دسترسی توسط $1 انجام شده است.\n\nدلیل داده‌شده $2 بوده است.\n\n* شروع قطع دسترسی: $8\n* اتمام قطع دسترسی: $6\n* هدف قطع دسترسی: $7\n* شناسه قطع دسترسی #$5", + "blockedtext-partial": "حساب کاربری یا آدرس آی‌پی شما از انجام تغییرات در این صفحه منع شده‌است. شما همچنان می‌توانید در دیگر صفحه‌های این ویکی ویرایش کنید. برای مشاهده جزئیات کامل قطع دسترسی به [[Special:MyContributions|مشارکت‌های حساب]] مراجعه کنید.\n\nاین قطع دسترسی توسط $1 انجام گرفته‌است.\n\nدلیل قطع دسترسی $2 است.\n\n* زمان آغاز قطع دسترسی: $8\n* زمان پایان قطع دسترسی: $6\n* موارد مورد نظر: $7\n* شناسه قطع دسترسی: #$5", "blockedtext": "دسترسی حساب کاربری یا نشانی آی‌پی شما بسته شده‌است.\n\nاین قطع دسترسی توسط $1 انجام شده است.\nدلیل ارائه‌شده چنین است: $2\n\n* شروع قطع دسترسی: $8\n* پایان قطع دسترسی: $6\n* کاربری هدف قطع دسترسی: $7\n\nشما می‌توانید با $1 یا [[{{MediaWiki:Grouppage-sysop}}|مدیری]] دیگر تماس بگیرید و در این باره صحبت کنید.\nتوجه کنید که شما نمی‌توانید از قابلیت «{{int:emailuser}}» استفاده کنید مگر آنکه آدرس ایمیل معتبری در [[Special:Preferences|ترجیحات کاربری]] خودتان ثبت کرده باشید و نیز باید امکان استفاده از این قابلیت برای شما قطع نشده باشد.\nنشانی آی‌پی فعلی شما $3 و شمارهٔ قطع دسترسی شما $5 است.\nلطفاً تمامی جزئیات فوق را در کلیهٔ درخواست‌هایی که در این باره مطرح می‌کنید ذکر کنید.", "autoblockedtext": "دسترسی نشانی آی‌پی شما قطع شده‌است، زیرا این نشانی آی‌پی توسط کاربر دیگری استفاده شده که دسترسی او توسط $1 قطع شده‌است.\nدلیل ارائه‌شده چنین است:\n\n:''$2''\n\n* شروع قطع دسترسی: $8\n* پایان قطع دسترسی: $6\n* کاربری هدف قطع دسترسی: $7\n\nشما می‌توانید با $1 یا [[{{MediaWiki:Grouppage-sysop}}|مدیری]] دیگر تماس بگیرید و در این باره صحبت کنید.\nتوجه کنید که شما نمی‌توانید از قابلیت «{{int:emailuser}}» استفاده کنید مگر آنکه نشانی ایمیل معتبری در [[Special:Preferences|ترجیحات کاربری]] خودتان ثبت کرده باشید و نیز باید امکان استفاده از این قابلیت برای شما قطع نشده باشد.\nنشانی آی‌پی فعلی شما $3 و شمارهٔ قطع دسترسی شما $5 است.\nلطفاً تمامی جزئیات فوق را در کلیهٔ درخواست‌هایی که در این باره مطرح می‌کنید ذکر کنید.", "systemblockedtext": "نام کاربری یا نشانی آی‌پی شما خودکار توسط مدیاویکی مسدود شده‌است.\nدلیل ارائه‌شده:\n\n:$2\n\n* آغاز بلاک: $8\n* پایان بلاک: $6\n* قطع دسترسی‌شده مورد نظر: $7\n\nنشانی آی‌پی کنونی شما $3 است.\nخواهشمند است تمام جزئیات بالا را در هر پرس‌وجویی که انجام می‌دهید قرار دهید.", @@ -791,6 +793,9 @@ "edit-gone-missing": "امکان به‌روز کردن صفحه وجود ندارد.\nبه نظرمی‌رسد که صفحه حذف شده باشد.", "edit-conflict": "تعارض ویرایشی.", "edit-no-change": "ویرایش شما نادیده گرفته شد، زیرا تغییری در متن داده نشده بود.", + "edit-slots-cannot-add": "این {{PLURAL:$1|اسلات|اسلات‌ها}} پشتیبانی نمی‌شود: $2.", + "edit-slots-cannot-remove": "امکان حذف این {{PLURAL:$1|اسلات|اسلات‌ها}} وجود ندارد: $2.", + "edit-slots-missing": "این {{PLURAL:$1|اسلات|اسلات‌ها}} ناموجود است: $2.", "postedit-confirmation-created": "صفحه ایجاد شده است.", "postedit-confirmation-restored": "صفحه بازیابی شده است.", "postedit-confirmation-saved": "ویرایش شما ذخیره شد.", @@ -799,7 +804,7 @@ "defaultmessagetext": "متن پیش‌فرض پیغام", "content-failed-to-parse": "عدم موفقیت در تجزیه محتوای $2 برای مدل $1: $3", "invalid-content-data": "داده محتوای نامعتبر", - "content-not-allowed-here": "محتوای «$1» در صفحهٔ [[:$2]] مجاز نیست", + "content-not-allowed-here": "محتوای «$1» در صفحهٔ [[:$2]] بخش «$3» مجاز نیست", "editwarning-warning": "خروج از این صفحه ممکن است باعث شود که شما هر شانسی که به وجود آورده‌اید را از دست بدهید.\nاگر شما وارد سامانه شده‌اید، می‌توانید این هشدار را در بخش «{{int:prefs-editing}}» ترجیحاتتان غیرفعال کنید.", "editpage-invalidcontentmodel-title": "مدل محتوای پشتیبانی نشده", "editpage-invalidcontentmodel-text": "مدل محتوای «$1» پشتیبای نمی‌شود.", @@ -2033,6 +2038,7 @@ "move": "انتقال", "movethispage": "انتقال این صفحه", "unusedimagestext": "پرونده‌های زیر موجودند اما در هیچ صفحه‌ای به کار نرفته‌اند.\nلطفاً توجه داشته باشید که دیگر وبگاه‌ها ممکن است با یک نشانی اینترنتی مستقیم به یک پرونده پیوند دهند، و با وجود این که در استفادهٔ فعال هستند در این جا فهرست شوند.", + "unusedimagestext-categorizedimgisused": "فایل موردنظر موجود است اما در هیچ صفحه‌ای استفاده نشده است. تصاویر رده‌بندی‌شده حتی اگر در هیچ صفحه‌ای درج نشده باشند، به عنوان تصاویر مورداستفاده محسوب می‌شوند.\nلطفا توجه داشته باشید که دیگر وب‌سایت‌ها ممکن است به صورت مستقیم به یک فایل پیوند داده باشند و با وجود اینکه آن فایل فعال محسوب می‌شود در اینجا لیست شده باشد.", "unusedcategoriestext": "این رده‌ها وجود دارند ولی هیچ مقاله یا ردهٔ دیگری از آنها استفاده نمی‌کند.", "notargettitle": "مقصدی نیست", "notargettext": "شما صفحهٔ یا کاربر مقصدی برای انجام این عمل روی آن مشخص نکرده‌اید.", @@ -2312,7 +2318,7 @@ "deleting-backlinks-warning": "هشدار: [[Special:WhatLinksHere/{{FULLPAGENAME}}|صفحه‌های دیگری]] هستند که به صفحه‌ای که شما در حال حذف آن هستید پیوند دارند یا آن را تراگنجانیده‌اند.", "deleting-subpages-warning": "هشدار: صفحه‌ای که شما می‌خواهید حذف کنید [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|یک زیرصفحه|$1 زیرصفحه|51=بیش از پنجاه زیرصفحه}}]] دارد.", "rollback": "واگردانی ویرایش‌ها", - "rollback-confirmation-confirm": "لطفا تایید کنید:", + "rollback-confirmation-confirm": "لطفاً تأیید کنید:", "rollback-confirmation-yes": "واگردانی", "rollback-confirmation-no": "انصراف", "rollbacklink": "واگردانی", @@ -2672,6 +2678,7 @@ "movepage-moved": "'''«$1» به «$2» منتقل شد'''", "movepage-moved-redirect": "یک تغییرمسیر ایجاد شد.", "movepage-moved-noredirect": "از ایجاد تغییرمسیر ممانعت شد.", + "movepage-delete-first": "صفحه مقصد تعداد زیادی نسخه برای حذف دارد. لطفا ابتدا صفحه را دستی حذف کنید و سپس دوباره سعی کنید.", "articleexists": "صفحه‌ای با این نام از قبل وجود دارد، یا نامی که انتخاب کرده‌اید معتبر نیست.\nلطفاً نام دیگری انتخاب کنید.", "cantmove-titleprotected": "شما نمی‌توانید صفحه را به این نشانی انتقال دهید، چرا که عنوان جدید در برابر ایجاد محافظت شده‌است", "movetalk": "صفحهٔ بحث هم منتقل شود", @@ -3098,6 +3105,7 @@ "mcrundofailed": "واگردانی ناموفق بود", "mcrundo-missingparam": "فقدان پارامترهای ضروری در درخواست", "mcrundo-changed": "این صفحه از زمانی که شما تفاوت را دیده‌اید تغییر کرده است. تغییر جدید را مشاهده کنید.", + "mcrundo-parse-failed": "قادر به تجزیه نسخه جدید نیست: $1", "semicolon-separator": "؛ ", "comma-separator": "، ", "percent": "$1Ùª", @@ -3492,8 +3500,10 @@ "logentry-block-reblock": "$1 {{GENDER:$2|تنظیمات}} بستن {{GENDER:$4|$3}} را به پایان قطع دسترسی $5 $6 تغییر داد.", "logentry-partialblock-block-page": "{{PLURAL:$1|صفحه|صفحات}} $2", "logentry-partialblock-block-ns": "{{PLURAL:$1|فضای نام|فضاهای نام}} $2", - "logentry-partialblock-block": "$1 {{GENDER:$4|$3}} را از ویرایش $7 با انقضای $5 $6 قطع دسترسی کرد", - "logentry-partialblock-reblock": "$1 {{GENDER:$2|تنظیمات}} بستن {{GENDER:$4|$3}} را به جلوگیری از ویرایش $7 و پایان قطع دسترسی $5 $6 تغییر داد.", + "logentry-partialblock-block": "$1 {{GENDER:$4|$3}} را از ویرایش $7 با انقضای $5 $6 {{GENDER:$2|قطع دسترسی کرد}}", + "logentry-partialblock-reblock": "$1 {{GENDER:$2|تنظیمات}} بستن {{GENDER:$4|$3}} را به جلوگیری از ویرایش $7 و پایان قطع دسترسی $5 $6 تغییر داد", + "logentry-non-editing-block-block": "$1 {{GENDER:$4|$3}} را از اعمال مشخص‌شده غیرویرایشی با انقضای $5 $6 {{GENDER:$2|قطع دسترسی کرد}}", + "logentry-non-editing-block-reblock": "$1 {{GENDER:$2|تنظیمات}} بستن {{GENDER:$4|$3}} را به جلوگیری از اعمال مشخص‌شده غیرویرایشی و پایان قطع دسترسی $5 $6 تغییر داد.", "logentry-suppress-block": "$1 {{GENDER:$2|بسته شد}} {{GENDER:$4|$3}} با پایان قطع دسترسی در زمان $5 $6", "logentry-suppress-reblock": "$1 {{GENDER:$2|تنظیمات}} بستن برای {{GENDER:$4|$3}} به پایان قطع دسترسی $5 $6 تغییر یافت", "logentry-import-upload": "$1 $3 را توسط بارگذار پرونده {{GENDER:$2|درون‌ریزی کرد}}", @@ -3857,8 +3867,8 @@ "passwordpolicies-policy-maximalpasswordlength": "گذرواژه باید کمتر از $1 {{PLURAL:$1|نویسه|نویسه}} طول داشته باشد", "passwordpolicies-policy-passwordcannotbepopular": "گذرواژه نمی‌تواند {{PLURAL:$1|گذرواژه پراستفاده باشد|در فهرست $1 گذرواژه‌های پراستفاده باشد}}", "passwordpolicies-policy-passwordnotinlargeblacklist": "گذرواژه نمی‌تواند یکی از Û±Û°Û°Ù¬Û°Û°Û° گذرواژه پراستفاده باشد.", - "passwordpolicies-policyflag-forcechange": "در هنگام ورود باید تغییر کند", - "passwordpolicies-policyflag-suggestchangeonlogin": "در هنگام ورود پیشنهاد تغییر داده می‌شود", + "passwordpolicies-policyflag-forcechange": "در هنگام ورود باید تغییر دهید", + "passwordpolicies-policyflag-suggestchangeonlogin": "در هنگام ورود، پیشنهاد تغییر بده", "easydeflate-invaliddeflate": "محتوی تهیه‌شده به صورت درست خالی نشده‌است", "unprotected-js": "به دلایل امنیتی، جاوااسکریپت نمی‌تواند از صفحات محافظت‌نشده بارگیری شود. لطفا جاوااسکریپت را تنها در فضای نام مدیاویکی: و یا در زیرصفحهٔ کاربری خودتان ایجاد کنید." } diff --git a/languages/i18n/fr.json b/languages/i18n/fr.json index 6afa75e26a..cd60511246 100644 --- a/languages/i18n/fr.json +++ b/languages/i18n/fr.json @@ -217,6 +217,7 @@ "tog-useeditwarning": "M’avertir quand je quitte une page en cours de modification sans avoir sauvegardé", "tog-prefershttps": "Toujours utiliser une connexion sécurisée lorsque je suis connecté", "tog-showrollbackconfirmation": "Afficher une demande de confirmation en cliquant sur un lien d’annulation", + "tog-showrollbackconfirmation-prerelease-warning": "Veuillez prendre note : Cette fonctionnalité n’est pas encore disponible. Si vous définissez cette préférence maintenant, votre choix sera pris en compte [https://meta.wikimedia.org/wiki/WMDE_Technical_Wishes/Rollback#Status quand la fonctionnalité sera livrée].", "underline-always": "Toujours", "underline-never": "Jamais", "underline-default": "Valeur par défaut du thème ou du navigateur", @@ -3236,6 +3237,7 @@ "confirm-unwatch-top": "Supprimer cette page de votre liste de suivi ?", "confirm-rollback-button": "OK", "confirm-rollback-top": "Révoquer les modifications de cette page ?", + "confirm-rollback-bottom": "Cette action annulera immédiatement les modifications sélectionnées sur cette page.", "confirm-mcrrestore-title": "Restaurer une version", "confirm-mcrundo-title": "Annuler une modification", "mcrundofailed": "L’annulation a échoué", diff --git a/languages/i18n/fy.json b/languages/i18n/fy.json index 4830ea007e..a3903bbd5a 100644 --- a/languages/i18n/fy.json +++ b/languages/i18n/fy.json @@ -1851,6 +1851,8 @@ "blankpage": "Side is leech", "intentionallyblankpage": "Dizze side is bewust leech lizzen en wurdt brûkt foar benchmarks, ensfh.", "tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|Lebel|Lebels}}]]: $2", + "tag-mw-new-redirect": "Nije trochferwizing", + "tag-mw-undo": "Ungedien meitsjen", "tags-source-header": "Boarne", "tags-active-header": "Aktyf?", "tags-actions-header": "Aksjes", @@ -1885,6 +1887,10 @@ "logentry-delete-delete": "$1 {{GENDER:$2|hat}} de side $3 wiske", "revdelete-restricted": "hat beheinings oplein oan behearders", "revdelete-unrestricted": "hat beheinings foar behearders goedmakke", + "logentry-move-move": "$1 {{GENDER:$2|hat}} de side $3 omneamd ta $4", + "logentry-move-move-noredirect": "$1 {{GENDER:$2|hat}} de side $3 omneamd ta $4 sûnder in trochferwizing efter te litten", + "logentry-move-move_redir": "$1 {{GENDER:$2|hat}} de side $3 omneamd ta $4 fan de trochferwizing", + "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|hat}} de side $3 omneamd ta $4 fan de trochferwizing, sûnder in trochferwizing efter te litten", "logentry-newusers-create": "It meidochakkount $1 is {{GENDER:$2|oanmakke}}", "logentry-newusers-autocreate": "It meidochakkount $1 is automatysk {{GENDER:$2|oanmakke}}", "logentry-upload-upload": "$1 hat $3 {{GENDER:$2|opladen}}", diff --git a/languages/i18n/ga.json b/languages/i18n/ga.json index c0c38322ed..fc566aa109 100644 --- a/languages/i18n/ga.json +++ b/languages/i18n/ga.json @@ -22,7 +22,8 @@ "Macofe", "Tem", "Nmacu", - "ديفيد" + "ديفيد", + "Xqt" ] }, "tog-underline": "Folínte faoi naisc:", @@ -1369,7 +1370,7 @@ "ilsubmit": "Cuardaigh", "bydate": "de réir dáta", "sp-newimages-showfrom": "Taispeáin íomhánna nua as $2, $1", - "days": "{{PLURAL:$1|$1 lá}}", + "days": "$1 lá", "bad_image_list": "Is é seo a leanas an formáid:\n\nNíl ach míreanna liosta amháin (línte ag tosú le *) san áireamh.\nIs riachtanach gur nasc do dhrochchomhad é an chéad nasc ar líne.\nIs eisceachtaí iad na naisc eile ar an líne céanna, .i. leathanaigh gur féidir an comhad a bheith orthu go hinlíne.", "metadata": "Meiteasonraí", "metadata-help": "Tá breis eolais sa comhad seo, curtha, is dócha, as ceamara digiteach ná scanóir a chruthaigh ná a digitigh é.\nMá tá an comhad mionathraithe as an bunleagan, b'fhéidir nach mbeidh ceann de na sonraí fágtha sa comhad atá athruithe.", diff --git a/languages/i18n/gcr.json b/languages/i18n/gcr.json index c033735e56..c6c810e351 100644 --- a/languages/i18n/gcr.json +++ b/languages/i18n/gcr.json @@ -172,15 +172,15 @@ "print": "Enprimé", "view": "Lir", "view-foreign": "Wè asou $1", - "edit": "Modifyé", - "edit-local": "Modifyé dèskripsyon lokal", + "edit": "Chanjé", + "edit-local": "Chanjé dèskripsyon lokal-a", "create": "Kréyé", "create-local": "Ajouté roun dèskripsyon lokal", "delete": "Siprimen", "undelete_short": "Rèstoré {{PLURAL:$1|roun modifikasyon|$1 modifikasyon}}", "viewdeleted_short": "Wè {{PLURAL:$1|roun modifikasyon ki siprimen|$1 modifikasyon ki siprimen}}", "protect": "Protéjé", - "protect_change": "modifyé", + "protect_change": "chanjé", "unprotect": "Chanjé protègsyon-an", "newpage": "Nouvèl paj", "talkpagelinktext": "diskisyon", @@ -248,10 +248,10 @@ "newmessageslinkplural": "{{PLURAL:$1|oun nouvèl mésaj|dé nouvèl mésaj}}", "newmessagesdifflinkplural": "{{PLURAL:$1|dannyé modifikasyon}}", "youhavenewmessagesmulti": "Zòt gen dé nouvèl mésaj asou $1.", - "editsection": "Modifyé", - "editold": "modifyé", + "editsection": "chanjé", + "editold": "chanjé", "viewsourceold": "wè sours-a", - "editlink": "modifyé", + "editlink": "chanjé", "viewsourcelink": "wè sours-a", "editsectionhint": "Modifyé ségsyon-an : $1", "toc": "Baydivan", @@ -392,7 +392,7 @@ "yourpasswordagain": "Konfirmen modipas-a :", "createacct-yourpasswordagain": "Konfirmen modipas-a", "createacct-yourpasswordagain-ph": "Rantré òkò menm modipas-a", - "userlogin-remembermypassword": "Gardé mo sésyon aktiv", + "userlogin-remembermypassword": "Gadé mo sésyon agtiv", "userlogin-signwithsecure": "Itilizé roun konnègsyon sékirizé", "cannotlogin-title": "Enposib di konnègté so kò", "cannotlogin-text": "Konnègsyon-an pa posib", @@ -401,7 +401,7 @@ "cannotcreateaccount-title": "Kréyasyon di kont enposib", "cannotcreateaccount-text": "Kréyasyon-an dirèk di kont itilizatò pa fika agtivé asou sa wiki.", "yourdomainname": "Zòt domenn :", - "password-change-forbidden": "Zòt pa pouvé modifyé mo di pas asou sa wiki.", + "password-change-forbidden": "Zòt pa pouvé chanjé modipas-ya asou sa wiki.", "externaldberror": "Swé roun lérò prodjwi so kò asou baz-a di data d'otantifikasyon, swé zòt pa otorizé à mété à jou zòt kont ègstèrn.", "login": "Konnègsyon", "login-security": "Vérifyé zòt idantité", @@ -435,7 +435,7 @@ "createacct-benefit-body1": "modifikasyon{{PLURAL:$1|}}", "createacct-benefit-body2": "paj{{PLURAL:$1|}}", "createacct-benefit-body3": "{{PLURAL:$1|kontribitò résan}}", - "badretype": "Mo di pas ki zòt sézi pa ka korèsponn.", + "badretype": "Modipas-ya ki zòt sézi pa ka korèsponn.", "usernameinprogress": "Oun kréyasyon di kont pou sa non d'itilizatò ja an kour.\nSouplé, pasyanté.", "userexists": "Non d'itilizatò sézi ja itilizé.\nSouplé, chwézi roun non diféran.", "loginerror": "Lérò di konnègsyon", @@ -453,11 +453,11 @@ "nosuchusershort": "I pa gen kontribitò ké non-an « $1 ».\nSouplé, vérifyé lòrtograf.", "nouserspecified": "Zòt divèt sézi roun non d'itilizatò.", "login-userblocked": "{{GENDER:$1|Sa itilizatò}} bloké. Konnègsyon-an pa otorizé.", - "wrongpassword": "Non d'itilizatò oben mo di pas enkorèk.\nSouplé, éséyé òkò.", + "wrongpassword": "Non-an di itilizatò oben modipas enkorèk.\nSouplé, éséyé òkò.", "wrongpasswordempty": "Zòt pa rantré pyès modipas.\nSouplé, éséyé òkò.", "passwordtooshort": "Zòt mo di pas divèt kontni omwen $1 karaktèr{{PLURAL:$1|}}.", - "passwordtoolong": "Mo di pas pa pouvé dépasé $1 karaktèr{{PLURAL:$1|}}.", - "passwordtoopopular": "Mo di pas ki tròp kouran pa pouvé fika itilizé. Souplé, chwézi roun mo di pas pli difisil à douviné.", + "passwordtoolong": "Modipas-ya pa pouvé dépasé $1 karagtèr{{PLURAL:$1|}}.", + "passwordtoopopular": "Modipas ki tròp kouran pa pouvé fika itilizé. Souplé, chwézi roun modipas ki pi difisil pou sonjé.", "password-name-match": "Zòt mo di pas divèt fika diféran di zòt non d'itilizatò.", "password-login-forbidden": "Litilizasyon-an di sa non d'itilizatò oben di sa modipas sa entèrdi.", "mailmypassword": "Réynisyalizé modipas-a", @@ -504,14 +504,14 @@ "newpassword": "Nouvèl modipas :", "retypenew": "Konfirmen modipas nòv-a :", "resetpass_submit": "Chanjé modipas-a é konnègté so kò.", - "changepassword-success": "Zòt modipas modifyé !", + "changepassword-success": "Zòt modipas chanjé !", "changepassword-throttled": "Zòt fè tròp di tantativ di konnègsyon résaman. \nSouplé, antann $1 anvan di réyéséyé.", "botpasswords": "Modipas di robo", "botpasswords-summary": "Modipas-ya di robo ka pèrmèt di agsédé à roun kont itilizatò vya API-a san itilizé idantifyan-yan di konnègsyon prensipal. Drwè itilizatò-ya ki disponnib lò to konnègté ké roun modipas robo pouvé fika rédjwi.\n\nSi zòt pa ka wè poukisa zòt ké lé fè sa, a ki zòt pa benzwen di fè sa. Pésonn divèt janmen doumandé zòt di an jénéré roun é di bay li.", "botpasswords-disabled": "Modipas-ya di robo dézagtivé.", "botpasswords-no-central-id": "Pou itilizé modipas-ya di robo, zòt divèt fika konnègté à roun kont ki santralizé.", "botpasswords-existing": "Modipas di robo ki ka ègzisté", - "botpasswords-createnew": "Kréyé roun mo di pas nòv di robo", + "botpasswords-createnew": "Kréyé roun nouvèl modipas di robo", "botpasswords-editexisting": "Modifyé roun modipas di robo ki ka ègzisté", "botpasswords-label-needsreset": "(Modipas-a divèt fika réynisyalizé)", "botpasswords-label-appid": "Non di robo :", @@ -535,11 +535,11 @@ "botpasswords-newpassword": "Nouvèl modipas-a pou konnègté so kò à$1 sa $2. Souplé, anréjistré li pou fè référans asou li iltèryòrman.
(Pou ansyen robo-ya ki ka nésésité ki non-an ki fourni pou konnègsyon-an ka fika menm-an ki non-an di itilizatò évantchwèl, zòt pouvé osi itilizé $3 kou non di itilizatò é $4 kou modipas).", "botpasswords-no-provider": "BotPasswordsSessionProvider pa disponnib.", "botpasswords-restriction-failed": "Rèstrigsyon-yan di modipas di robo ka anpéché sa konnègsyon.", - "botpasswords-invalid-name": "Non-an d'itilizatò spésifyé pa ka kontni di séparatò di mo di pas di robo (« $1 »).", + "botpasswords-invalid-name": "Non-an d'itilizatò ki èspésifyé pa ka kontni di séparatò di modipas di robo (« $1 »).", "botpasswords-not-exist": "{{GENDER:$1|Itilizatò|Itilizatris}}-a « $1 » pa gen di mo di pas di robo nonmen « $2 ».", "botpasswords-needs-reset": "Modipas-a di robo di non « $2 » di itilizatò-a « $1 » divèt fika réynisyalizé.", "resetpass_forbidden": "Modipas-ya pa pouvé fika chanjé.", - "resetpass_forbidden-reason": "Mo di pas pa pouvé fika modifyé : $1", + "resetpass_forbidden-reason": "Modipas-ya pa pouvé fika chanjé : $1", "resetpass-no-info": "Zòt divèt fika konnègté pou agsédé dirèkman à sa paj.", "resetpass-submit-loggedin": "Chanjé di modipas", "resetpass-submit-cancel": "Annilé", @@ -550,7 +550,7 @@ "resetpass-expired-soft": "Zòt modipas èspiré, é divèt fika modifyé. Souplé, chwézi roun nouvèl atchwèlman oben kliké asou « {{int:authprovider-resetpass-skip-label}} » pou fè li plita.", "resetpass-validity-soft": "Zòt modipas pa valid : $1\n\nSouplé, chwézi roun nouvèl modipas atchwèlman, oben kliké asou « {{int:authprovider-resetpass-skip-label}} » pou modifyé li plita.", "passwordreset": "Réynisyalizasyon di modipas", - "passwordreset-text-one": "Ranplisé sa fòrmilèr pou zòt mo di pas.", + "passwordreset-text-one": "Ranpli sa fòrmilèr pou réynisyalizé zòt modipas.", "passwordreset-emaildisabled": "Fongsyonnalité-ya di kourilèt fika dézagtivé asou sa wiki.", "passwordreset-username": "Non di itilizatò :", "passwordreset-domain": "Domenn :", @@ -839,7 +839,7 @@ "protectedarticle": "protéjé « [[$1]] »", "modifiedarticleprotection": "modifyé nivo-a di protègsyon di « [[$1]] »", "protect-default": "Otorizé tout itilizatò-ya", - "restriction-edit": "Modifyé", + "restriction-edit": "Chanjé", "restriction-move": "Rounonmen", "namespace": "Lèspas di non", "invert": "Envèrsé sélègsyon-an", @@ -981,7 +981,7 @@ "pageinfo-magic-words": "{{PLURAL:$1|Mo majik}} ($1)", "pageinfo-hidden-categories": "{{PLURAL:$1|Katégori kaché|}} ($1)", "pageinfo-templates": "{{PLURAL:$1|Modèl enkli}} ($1)", - "pageinfo-toolboxlink": "Lenfòrmasyon asou paj-a", + "pageinfo-toolboxlink": "Lenfòrmasyon asou paj", "pageinfo-contentpage": "Konté kou paj di kontni", "pageinfo-contentpage-yes": "Enren", "patrol-log-page": "Journal dé roulèktir", diff --git a/languages/i18n/he.json b/languages/i18n/he.json index 01162a4aa3..1716310255 100644 --- a/languages/i18n/he.json +++ b/languages/i18n/he.json @@ -86,7 +86,7 @@ "tog-useeditwarning": "הצגת אזהרה בעת עזיבת דף עריכה עם שינויים שטרם נשמרו", "tog-prefershttps": "תמיד להשתמש בתקשורת מאובטחת לאחר הכניסה לחשבון", "tog-showrollbackconfirmation": "הצגת הודעת אישור לאחר לחיצה על קישור \"שחזור\"", - "tog-showrollbackconfirmation-prerelease-warning": "לתשומת לבך: האפשרות הזאת עדיין אינה זמינה. אם {{GENDER:|תגדיר|תגדירי}} ההעדפה הזאת עכשיו, הבחירה שלך תיזכר [https://meta.wikimedia.org/wiki/WMDE_Technical_Wishes/Rollback#Status לזמן שהאפשרות הזאת תצא לאור].", + "tog-showrollbackconfirmation-prerelease-warning": "לתשומת לבך: תכונה זו עדיין אינה זמינה. אם {{GENDER:|תפעיל|תפעילי}} את ההעדפה הזאת עכשיו, בחירתך תישמר ותיכנס לתוקף [https://meta.wikimedia.org/wiki/WMDE_Technical_Wishes/Rollback#Status כשהתכונה תהפוך לזמינה].", "underline-always": "תמיד", "underline-never": "לעולם לא", "underline-default": "ברירת המחדל של העיצוב או של הדפדפן", @@ -2285,7 +2285,7 @@ "deleting-backlinks-warning": "אזהרה: [[Special:WhatLinksHere/{{FULLPAGENAME}}|דפים אחרים]] מקשרים לדף זה (שעומד להימחק) או מכלילים אותו.", "deleting-subpages-warning": "אזהרה: לדף שעומד להימחק יש [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|דף משנה|$1 דפי משנה|51=יותר מ־50 דפי משנה}}]].", "rollback": "שחזור עריכות", - "rollback-confirmation-confirm": "נא לאשר:", + "rollback-confirmation-confirm": "לאישורך:", "rollback-confirmation-yes": "שחזור", "rollback-confirmation-no": "ביטול", "rollbacklink": "שחזור", @@ -3076,7 +3076,7 @@ "confirm-unwatch-top": "להסיר את הדף הזה מרשימת המעקב שלך?", "confirm-rollback-button": "אישור", "confirm-rollback-top": "לשחזר את העריכות בדף זה?", - "confirm-rollback-bottom": "הפעולה הזאת תשחזר באופן מיידי את השינויים שנבחרו בדף הזה.", + "confirm-rollback-bottom": "פעולה זו תשחזר באופן מיידי את השינויים שבחרת בדף זה.", "confirm-mcrrestore-title": "שחזור גרסה", "confirm-mcrundo-title": "ביטול שינוי", "mcrundofailed": "הביטול נכשל", diff --git a/languages/i18n/hr.json b/languages/i18n/hr.json index 238310800e..9aeb97dea7 100644 --- a/languages/i18n/hr.json +++ b/languages/i18n/hr.json @@ -44,11 +44,11 @@ "Zeljko.filipin" ] }, - "tog-underline": "Podcrtavanje poveznica", - "tog-hideminor": "Sakrij manje izmjene u nedavnim promjenama", - "tog-hidepatrolled": "Sakrij pregledane izmjene u nedavnim promjenama", - "tog-newpageshidepatrolled": "Sakrij pregledane stranice iz popisa novih stranica", - "tog-hidecategorization": "Sakrij kategorizaciju stranica", + "tog-underline": "Podcrtavanje veza:", + "tog-hideminor": "Sakrivaj manje izmjene sa popisa nedavnih promjena", + "tog-hidepatrolled": "Sakrivaj patrolirane izmjene sa popisa nedavnih promjena", + "tog-newpageshidepatrolled": "Sakrivaj patrolirane stranice sa popisa novih stranica", + "tog-hidecategorization": "Sakrivaj kategorizaciju stranica", "tog-extendwatchlist": "Proširi popis praćenih stranica tako da prikaže sve promjene, ne samo najnovije", "tog-usenewrc": "Grupne promjene po stranici u popisu nedavnih izmjena i popisu praćenih stranica (zahtijeva JavaScript)", "tog-numberheadings": "Automatski označi naslove brojevima", @@ -284,9 +284,9 @@ "youhavenewmessagesmulti": "Imate nove poruke na $1", "editsection": "uredi", "editold": "uredi", - "viewsourceold": "vidi izvor", + "viewsourceold": "prikaži izvor", "editlink": "uredi", - "viewsourcelink": "vidi izvornik", + "viewsourcelink": "prikaži izvor", "editsectionhint": "Uredi odlomak: $1", "toc": "Sadržaj", "showtoc": "prikaži", @@ -368,7 +368,7 @@ "perfcached": "Sljedeći podaci su iz međuspremnika i možda nisu najsvježiji. Međuspremnik sadrži $1 {{PLURAL:$1|rezultat|rezultata}} pretraživanja.", "perfcachedts": "Sljedeći podaci su iz međuspremnika i posljednji puta su ažurirani u $1. Međuspremnik sadrži $4 {{PLURAL:$4|rezultat|rezultata}} pretraživanja.", "querypage-no-updates": "Osvježavanje ove stranice je trenutačno onemogućeno. Nove promjene neće biti vidljive.", - "viewsource": "Vidi izvornik", + "viewsource": "Prikaži izvor", "viewsource-title": "Vidi kôd stranice $1", "actionthrottled": "Uređivanje je usporeno", "actionthrottledtext": "Kao mjera protiv spama, ograničeni vam je broj ovih radnji u određenom vremenu, i trenutačno ste dostigli to ograničenje. Pokušajte opet za par minuta.", diff --git a/languages/i18n/hu.json b/languages/i18n/hu.json index 929235c2cd..41135bc3c2 100644 --- a/languages/i18n/hu.json +++ b/languages/i18n/hu.json @@ -98,6 +98,7 @@ "tog-norollbackdiff": "Ne jelenjenek meg az eltérések visszaállítás után", "tog-useeditwarning": "Figyelmeztessen, ha szerkesztéskor a módosítások mentése nélkül akarom elhagyni a lapot", "tog-prefershttps": "Mindig biztonságos kapcsolatot használjon, amikor be vagyok jelentkezve", + "tog-showrollbackconfirmation": "Megerősítés kérése, amikor a visszaállítás linkre kattintasz", "underline-always": "mindig", "underline-never": "soha", "underline-default": "Felület és böngésző alapértelmezése szerint", @@ -2286,6 +2287,9 @@ "deleting-backlinks-warning": "Figyelem: [[Special:WhatLinksHere/{{FULLPAGENAME}}|Más lapok]] hivatkoznak a törlendő oldalra (vagy beillesztik azt).", "deleting-subpages-warning": "Figyelem: A törlésre jelölt lapnak [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|van egy allapja|$1 allapja van|51=több mint 50 allapja van}}]].", "rollback": "Szerkesztések visszaállítása", + "rollback-confirmation-confirm": "Kérlek erősítsd meg:", + "rollback-confirmation-yes": "Visszaállítás", + "rollback-confirmation-no": "Mégse", "rollbacklink": "visszaállítás", "rollbacklinkcount": "$1 szerkesztés visszaállítása", "rollbacklinkcount-morethan": "több mint $1 szerkesztés visszaállítása", @@ -2492,6 +2496,8 @@ "ipb-confirm": "Blokk megerősítése", "ipb-sitewide": "Teljes körű", "ipb-partial": "Részleges", + "ipb-sitewide-help": "A wiki összes lapja és minden egyéb közreműködési művelet.", + "ipb-partial-help": "Meghatározott lapok vagy névterek.", "ipb-pages-label": "Lapok", "ipb-namespaces-label": "Névterek", "badipaddress": "Érvénytelen IP-cím", @@ -2580,6 +2586,7 @@ "ipb_expiry_old": "A lejárati idő a múltban van.", "ipb_expiry_temp": "A láthatatlan felhasználóinév-blokkok lehetnek állandóak.", "ipb_hide_invalid": "A felhasználói fiókot nem lehet elrejteni; több mint $1 szerkesztése van.", + "ipb_hide_partial": "Felhasználói nevek elrejtésekor és blokkolásakor a blokknak az egész wikire ki kell terjednie.", "ipb_already_blocked": "\"$1\" már blokkolva", "ipb-needreblock": "$1 már blokkolva van. Meg szeretnéd változtatni a beállításokat?", "ipb-otherblocks-header": "További {{PLURAL:$1|blokk|blokkok}}", @@ -3066,6 +3073,7 @@ "confirm-unwatch-top": "El szeretnéd távolítani a lapot a figyelőlistádról?", "confirm-rollback-button": "OK", "confirm-rollback-top": "Visszavonod a változtatásokat?", + "confirm-rollback-bottom": "Ez a művelet azonnal visszaállítja a lap kiválasztott változtatásait.", "confirm-mcrrestore-title": "Egy változat visszaállítása", "confirm-mcrundo-title": "Egy változtatás visszavonva", "mcrundofailed": "A visszavonás nem sikerült", @@ -3771,5 +3779,7 @@ "passwordpolicies-policy-maximalpasswordlength": "A jelszó legfeljebb $1 karakter hosszú lehet", "passwordpolicies-policy-passwordcannotbepopular": "A jelszó nem {{PLURAL:$1|lehet a gyakran használt jelszó|szerepelhet a(z) $1 leggyakrabban használt jelszó listáján}}", "passwordpolicies-policy-passwordnotinlargeblacklist": "A jelszó nem szerepelhet a 100 000 leggyakrabban használt jelszó listáján .", + "passwordpolicies-policyflag-forcechange": "lecserélés követelése bejelentkezéskor", + "passwordpolicies-policyflag-suggestchangeonlogin": "lecserélés ajánlása bejelentkezéskor", "unprotected-js": "Biztonsági okokból JavaScript nem tölthető be védtelen lapokról. Kérlek egyedül a MediaWiki névtérben készíts JavaScriptet, vagy szerkesztői allapként." } diff --git a/languages/i18n/io.json b/languages/i18n/io.json index edeeb8a9a3..d7eb2bb4f9 100644 --- a/languages/i18n/io.json +++ b/languages/i18n/io.json @@ -173,7 +173,7 @@ "help": "Helpo", "help-mediawiki": "Helpo pri MediaWiki", "search": "Sercho", - "search-ignored-headings": " #
\n# Tituli qui ignoresos per la sistemo di serchado.\n# Modifiki en ca parto efikeskos balde pos la titulo di la pagino adicionesos a l'indexo.\n# Tu povas acelerar la riindexigo di la pagino facante nihila editado.\n# La sintaxo esas quale infre:\n#   * Omna texti qui finas kun la signo \"#\" fine de la lineo, esas komentaro.\n#   * Omna lineo ne blanka - to esas: skriptata -, esas l'exakta titulo por ignorar la diferi inter mayuskula e minuskula literi, ed altra.\nReferi\nExtera ligili\nVidez anke\n #
", + "search-ignored-headings": " #
\n# Tituli qui ignoresos dal sistemo di serchado.\n# Modifiki en ca parto efikeskos balde pos la titulo di la pagino adicionesos a l'indexo.\n# Tu povas acelerar la riindexigo di la pagino facante nihila editado.\n# La sintaxo esas quale infre:\n#   * Omna texti qui finas kun la signo \"#\" fine de la lineo, esas komentaro.\n#   * Omna lineo ne blanka - to esas: skriptata -, esas l'exakta titulo por ignorar la diferi inter mayuskula e minuskula literi, ed altra.\nReferi\nExtera ligili\nVidez anke\n #
", "searchbutton": "Serchez", "go": "Irar", "searcharticle": "Irez", diff --git a/languages/i18n/mk.json b/languages/i18n/mk.json index 2d3e88b638..20ebf2ab66 100644 --- a/languages/i18n/mk.json +++ b/languages/i18n/mk.json @@ -71,6 +71,7 @@ "tog-useeditwarning": "Предупреди ме кога сакам да напуштам страница за уредување без да ги имам зачувано промените", "tog-prefershttps": "Секогаш најавувај ме преку безбедна врска", "tog-showrollbackconfirmation": "Прикажи потврдница при стискање на врската за отповикување", + "tog-showrollbackconfirmation-prerelease-warning": "Имајте предвид: Оваа можност сè уште не е достапна. Ако ја зададете поставката сега, изборот ќе ви се зачува [https://meta.wikimedia.org/wiki/WMDE_Technical_Wishes/Rollback#Status кога ќе излезе алатката].", "underline-always": "Секогаш", "underline-never": "Никогаш", "underline-default": "Според рувото или прелистувачот", @@ -2276,7 +2277,7 @@ "deleting-backlinks-warning": "Предупредување: До страницата што сакате да ја избришете водат [[Special:WhatLinksHere/{{FULLPAGENAME}}|други страници]] или пак се превметнуваат во неа.", "deleting-subpages-warning": "Предупредување: Страницата што сакате да ја избришете има [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|потстраница|$1 потстраници|51=преку 50 потстраници}}]].", "rollback": "Отповикај промени", - "rollback-confirmation-confirm": "Да {{PLURAL:$1|0=ги отповикам овие уредувања|отповикам едно уредување|отповикам $1 уредувања}}?", + "rollback-confirmation-confirm": "Потврдете:", "rollback-confirmation-yes": "Отповикај", "rollback-confirmation-no": "Откажи", "rollbacklink": "отповикај", diff --git a/languages/i18n/ms.json b/languages/i18n/ms.json index 62e2047ec1..61d40cb8f1 100644 --- a/languages/i18n/ms.json +++ b/languages/i18n/ms.json @@ -1205,8 +1205,8 @@ "rightslogtext": "Ini ialah log perubahan terhadap hak pengguna.", "action-read": "membaca laman ini", "action-edit": "menyunting laman ini", - "action-createpage": "mencipta laman", - "action-createtalk": "mencipta laman perbincangan", + "action-createpage": "ciptakan laman ini", + "action-createtalk": "ciptakan laman perbincangan ini", "action-createaccount": "mencipta akaun pengguna ini", "action-history": "melihat sejarah halaman ini", "action-minoredit": "menanda suntingan ini sebagai suntingan kecil", diff --git a/languages/i18n/nan.json b/languages/i18n/nan.json index 89ea2a3d34..ff71467797 100644 --- a/languages/i18n/nan.json +++ b/languages/i18n/nan.json @@ -919,13 +919,15 @@ "cantrollback": "Bô-hoat-tō· kā siu-kái ká-tńg--khì; téng ūi kòng-hiàn-chiá sī chit ia̍h î-it ê chok-chiá.", "alreadyrolled": "Bô-hoat-tō· kā [[User:$2|$2]] ([[User talk:$2|Thó-lūn]]) tùi [[:$1]] ê siu-kái ká-tńg-khì; í-keng ū lâng siu-kái a̍h-sī ká-tńg chit ia̍h. Téng 1 ūi siu-kái-chiá sī [[User:$3|$3]] ([[User talk:$3|Thó-lūn]]).", "editcomment": "Siu-kái phêng-lūn sī: $1.", - "protectedarticle": "pó-hō͘ \"[[$1]]\"", + "protectedarticle": "pó-hō͘ liáu \"[[$1]]\"", "protect-title": "Kái-piàn \"$1\" ê pó-hō͘ chân-kip", "prot_1movedto2": "[[$1]] sóa khì tī [[$2]]", "protect-legend": "Khak-tēng beh pó-hō·", "protectcomment": "Lí-iû:", - "protect-level-autoconfirmed": "Chí ín-chún chū-tōng khak-jīn iōng-chiá", - "protect-level-sysop": "Chí ín-chún koán-lí jîn-oân", + "protect-level-autoconfirmed": "Ta ín-chún chū-tōng khak-jīn iōng-chiá", + "protect-level-sysop": "Ta ín-chún koán-lí jîn-oân", + "protect-expiring": "chì $1 (UTC) kòe-kî", + "protect-expiring-local": "chì $1 kòe-kî", "protect-cascade": "Cascading protection - pó-hō͘ jīm-hô pau-hâm tī chit ia̍h ê ia̍h.", "restriction-edit": "Siu-kái", "restriction-move": "Sóa khì", diff --git a/maintenance/convertLinks.php b/maintenance/convertLinks.php index af60eaa2ba..59820a5a8f 100644 --- a/maintenance/convertLinks.php +++ b/maintenance/convertLinks.php @@ -126,7 +126,6 @@ class ConvertLinks extends Maintenance { $res = $dbw->query( "SELECT COUNT(*) AS count FROM $links" ); $row = $dbw->fetchObject( $res ); $numRows = $row->count; - $dbw->freeResult( $res ); if ( $numRows == 0 ) { $this->output( "Updating schema (no rows to convert)...\n" ); @@ -168,7 +167,6 @@ class ConvertLinks extends Maintenance { } } } - $dbw->freeResult( $res ); $dbw->bufferResults( true ); $this->output( "Finished loading IDs.\n\n" ); $this->performanceLog( @@ -214,7 +212,6 @@ class ConvertLinks extends Maintenance { $numBadLinks++; } } - $dbw->freeResult( $res ); # $this->output( "rowOffset: $rowOffset\ttuplesAdded: " # . "$tuplesAdded\tnumBadLinks: $numBadLinks\n" ); if ( $tuplesAdded != 0 ) { diff --git a/maintenance/dictionary/mediawiki.dic b/maintenance/dictionary/mediawiki.dic index fc17a3dd0e..45457f543f 100644 --- a/maintenance/dictionary/mediawiki.dic +++ b/maintenance/dictionary/mediawiki.dic @@ -2425,7 +2425,6 @@ mkdir mms mobile mobileformat -mobilelanding mobileview modified modifiedarticleprotection diff --git a/maintenance/dumpTextPass.php b/maintenance/dumpTextPass.php index f515df70f4..61c63e996b 100644 --- a/maintenance/dumpTextPass.php +++ b/maintenance/dumpTextPass.php @@ -839,6 +839,7 @@ TEXT if ( $newAddress === false ) { return false; } + $newAddress = trim( $newAddress ); if ( strpos( $newAddress, ':' ) === false ) { $newAddress = SqlBlobStore::makeAddressFromTextId( intval( $newAddress ) ); } diff --git a/maintenance/manageForeignResources.php b/maintenance/manageForeignResources.php new file mode 100644 index 0000000000..54554b8c5b --- /dev/null +++ b/maintenance/manageForeignResources.php @@ -0,0 +1,86 @@ +addDescription( <<addArg( 'action', 'One of "update", "verify" or "make-sri"', true ); + $this->addArg( 'module', 'Name of a single module (Default: all)', false ); + $this->addOption( 'verbose', 'Be verbose', false, false, 'v' ); + } + + /** + * @return bool + * @throws Exception + */ + public function execute() { + global $IP; + $frm = new ForeignResourceManager( + "{$IP}/resources/lib/foreign-resources.yaml", + "{$IP}/resources/lib", + function ( $text ) { + $this->output( $text ); + }, + function ( $text ) { + $this->error( $text ); + }, + function ( $text ) { + if ( $this->hasOption( 'verbose' ) ) { + $this->output( $text ); + } + } + ); + + $action = $this->getArg( 0 ); + $module = $this->getArg( 1, 'all' ); + return $frm->run( $action, $module ); + } +} + +$maintClass = ManageForeignResources::class; +require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/resources/foreign-resources.yaml b/maintenance/resources/foreign-resources.yaml deleted file mode 100644 index d4458aa905..0000000000 --- a/maintenance/resources/foreign-resources.yaml +++ /dev/null @@ -1,210 +0,0 @@ -### Format of this file -# -# The top-level keys are directory names (under resources/lib/). -# They should match module names (as registered in Resources.php), but there are exceptions. -# Each top-level key holds a resource descriptor that must have one of -# the following `type` values: -# -# - `tar`: For tarball archive (may be gzip-compressed). -# - `file: For a plain file. -# - `multi-file`: For multiple plain files. -# -### Type tar -# -# The `src` and `integrity` keys are required. -# -# * `src`: Full URL to the remote resource. -# * `integrity`: Cryptographic hash (integrity metadata format per ). -# * `dest`: An object mapping paths to files or directory from the remote resource to a destination -# in the module directory. The value of key in dest may be omitted, which will extract the key -# directly to the module directory. -# -### Type file -# -# The `src` and `integrity` keys are required. -# -# * `src`: Full URL to the remote resource. -# * `integrity`: Cryptographic hash (integrity metadata format per ). -# * `dest`: The name of the file in the module directory. Default: Basename of URL. -# -### Type multi-file -# -# The `files` key is required. -# -# * `files`: An object mapping destination paths to an object containing `src` and `integrity` -# keys. - -CLDRPluralRuleParser: - type: file - src: https://raw.githubusercontent.com/santhoshtr/CLDRPluralRuleParser/0dda851/src/CLDRPluralRuleParser.js - integrity: sha384-M4taeYYG2+9Ob1/La16iO+zlRRmBV5lBR3xUKkQT6kfkJ0aLbCi6yc0RYI1BDzdh - -easy-deflate: - type: multi-file - files: - deflate.js: - src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/deflate.js - integrity: sha384-sHnZLDSWMUhA2w9ygkzCK8YFvoh/fQKY6lXMbvmrYzjuNURiLB0DZFCDNMpGyZ77 - easydeflate.js: - src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/easydeflate.js - integrity: sha384-EwPfP2RMkDPa1HkzQsXgzTsy1KEjcIzQPA1HDS/JPHjvEMvVUsCxWwm1oXql/jk2 - inflate.js: - src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/inflate.js - integrity: sha384-hMg44Hw424mUYvmzKl0JT4J8UU/1YYhTiGRtR0YX/MXNLK9qWTK0d62FBCDGxmxw - README.md: - src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/README.md - integrity: sha384-6kwcfCLivvqXBZy2ATyya+mTVWLk3eaQyBdC6tbpBtkygnBrM2SNkq3jz/l7IkvP - -html5shiv: - type: file - src: https://raw.githubusercontent.com/aFarkas/html5shiv/3.7.3/src/html5shiv.js - integrity: sha384-RPXhaTf22QktT8KTwZ6bUz/C+7CnccaIw5W/y/t0FW5WSDGj3wc3YtRIJC0w47in - -jquery: - type: file - src: https://code.jquery.com/jquery-3.3.1.js - # Integrity from link modals https://code.jquery.com/jquery/ - integrity: sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60= - dest: jquery.js - -jquery.client: - type: tar - src: https://registry.npmjs.org/jquery-client/-/jquery-client-2.0.2.tgz - integrity: sha256-8c8nBbBykHEMc4I7ksdKJvvw/P7WkaC2X46RTPdz/pw= - dest: - package/AUTHORS.txt: - package/jquery.client.js: - package/LICENSE-MIT: - package/README.md: - -jquery.cookie: - type: multi-file - files: - jquery.cookie.js: - src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/jquery.cookie.js - integrity: sha384-Xxq63E9KDgzUJ6WPNPqVeOtRIwZyx6y9DzEwY2u6LYKSnWrjSoGtWSKmTindYBf2 - MIT-LICENSE.txt: - src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/MIT-LICENSE.txt - integrity: sha384-zYsGf3KJ7S0AhOICjcoh0kkn7aGZlzYUXXX5xz8dwR9KjLMM+/JPR2g/jVOGGeId - CHANGELOG.md: - src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/CHANGELOG.md - integrity: sha384-SQOHhLc7PHxHDQpGE/zv9XfXKL0A7OBu8kuyVDnHVp+zSoWyRw4xUJ+LSm5ql4kS - -jquery.form: - type: file - src: https://raw.githubusercontent.com/jquery-form/form/ff80d9ddf4/jquery.form.js - integrity: sha384-h4G2CrcSbixzMvrrK259cNBYaL/vS1D4+KdUN9NJDzQnTU1bQ6Avluget+Id13M7 - dest: jquery.form.js - -jquery.fullscreen: - type: file - src: https://raw.githubusercontent.com/theopolisme/jquery-fullscreen/v2.1.0/jquery.fullscreen.js - integrity: sha384-G4KPs2d99tgcsyUnJ3eeZ1r2hEKDwZfc4+/xowL/LIemq2VVwEE8HpVAWt4WYNLR - dest: jquery.fullscreen.js - -jquery.hoverIntent: - type: file - src: https://raw.githubusercontent.com/briancherne/jquery-hoverIntent/823603fdac/jquery.hoverIntent.js - integrity: sha384-lca0haN0hqFGGh2aYUhtAgX9dhVHfQnTADH4svDeM6gcXnL7aFGeAi1NYwipDMyS - dest: jquery.hoverIntent.js - -jquery.jStorage: - type: file - src: https://raw.githubusercontent.com/andris9/jStorage/v0.4.12/jstorage.js - integrity: sha384-geMeN8k803kPp6cqRL4VNfuSM1L8DcbKRk0St/KHJzxgpX9S0y9FA6HxA/JgucrJ - dest: jstorage.js - -jquery.throttle-debounce: - type: file - src: https://raw.githubusercontent.com/cowboy/jquery-throttle-debounce/v1.1/jquery.ba-throttle-debounce.js - integrity: sha384-ULOy4DbAghrCqRcrTJLXOY9e4gDpWh0BeEf6xMSL0VtNudXWggcb6AmrVrl4KDAP - dest: jquery.ba-throttle-debounce.js - -moment: - type: tar - src: https://codeload.github.com/moment/moment/tar.gz/2.24.0 - integrity: sha384-2/I9rfqkN8AAgh5wOXXphuo827uV7lMmOodrCfIvqC6W6JKKiDGOwd+lE3e8R0yz - dest: - moment-2.24.0/moment.js: - moment-2.24.0/CHANGELOG.md: - moment-2.24.0/README.md: - moment-2.24.0/LICENSE: - moment-2.24.0/locale/*.js: locale - -mustache: - type: multi-file - files: - mustache.js: - src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/mustache.js - integrity: sha384-k2UYqmzoiq/qgIzZvcYBxbXQW4YdPAsXDOTkHTGb9TCZ9sjCkyT4TlaUN0wQRkql - LICENSE: - src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/LICENSE - integrity: sha384-MYVwXwula9+YkyXexOJVZ0v0DaVvG22uX57mNq5Di+7u8OH9EG9q3yuXkp1Iehiq - -oojs: - type: tar - src: https://registry.npmjs.org/oojs/-/oojs-2.2.2.tgz - integrity: sha256-ebgQW2EGrSkBCnDJBGqDpsBDjA3PMN/M8U5DyLHt9mw= - dest: - package/dist/oojs.jquery.js: - package/AUTHORS.txt: - package/LICENSE-MIT: - package/README.md: - -oojs-router: - type: tar - src: https://registry.npmjs.org/oojs-router/-/oojs-router-0.2.0.tgz - integrity: sha384-VngYqdQ3vTDMXbm4e4FUZCCGos7fB0Jkr9V+kBL5MElprK1h0yQZOzBNnMHtSJS/ - dest: - package/dist/oojs-router.js: - package/LICENSE: - package/AUTHORS.txt: - package/History.md: - -ooui: - type: tar - src: https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.31.1.tgz - integrity: sha384-M9KdU6u02zSKCVczcw6YJmSvFLhdeagNg9CPhizYVqrybL8bamrF5u6YfrFGEyiv - dest: - # Main stuff - package/dist/oojs-ui-core.js{,.map.json}: - package/dist/oojs-ui-core-{wikimediaui,apex}.css: - package/dist/oojs-ui-widgets.js{,.map.json}: - package/dist/oojs-ui-widgets-{wikimediaui,apex}.css: - package/dist/oojs-ui-toolbars.js{,.map.json}: - package/dist/oojs-ui-toolbars-{wikimediaui,apex}.css: - package/dist/oojs-ui-windows.js{,.map.json}: - package/dist/oojs-ui-windows-{wikimediaui,apex}.css: - package/dist/oojs-ui-{wikimediaui,apex}.js{,.map.json}: - package/dist/i18n: - package/dist/images: - # WikimediaUI theme - package/dist/themes/wikimediaui/images/icons/*.{svg,png}: themes/wikimediaui/images/icons - package/dist/themes/wikimediaui/images/indicators/*.{svg,png}: themes/wikimediaui/images/indicators - package/dist/themes/wikimediaui/images/textures/*.{gif,svg}: themes/wikimediaui/images/textures - package/src/themes/wikimediaui/*.json: themes/wikimediaui - package/dist/wikimedia-ui-base.less: - # Apex theme (icons, indicators, and textures) - package/src/themes/apex/*.json: themes/apex - # Misc stuff - package/dist/AUTHORS.txt: - package/dist/History.md: - package/dist/LICENSE-MIT: - package/dist/README.md: - -qunitjs: - type: multi-file - # Integrity from link modals at https://code.jquery.com/qunit/ - files: - qunit.js: - src: http://code.jquery.com/qunit/qunit-2.9.1.js - integrity: sha256-eNccBdxd8zReziWcVjEsPeyJDi3LKMYnzMXyDv8bzsU= - qunit.css: - src: https://code.jquery.com/qunit/qunit-2.9.1.css - integrity: sha256-SSS7o92V7wzcIFg3qnJL9mc4msePaT4klbxtuSGvVVo= - -sinonjs: - type: file - src: https://sinonjs.org/releases/sinon-1.17.7.js - integrity: sha384-wR63Jwy75KqwBfzCmXd6gYws6uj3qV/XMAybzXrkEYGYG3AQ58ZWwr1fVpkHa5e8 - dest: sinon.js diff --git a/maintenance/resources/manageForeignResources.php b/maintenance/resources/manageForeignResources.php deleted file mode 100644 index 6de82c0912..0000000000 --- a/maintenance/resources/manageForeignResources.php +++ /dev/null @@ -1,80 +0,0 @@ -addDescription( <<addArg( 'action', 'One of "update", "verify" or "make-sri"', true ); - $this->addArg( 'module', 'Name of a single module (Default: all)', false ); - $this->addOption( 'verbose', 'Be verbose', false, false, 'v' ); - } - - /** - * @return bool - * @throws Exception - */ - public function execute() { - global $IP; - $frm = new ForeignResourceManager( - __DIR__ . '/foreign-resources.yaml', - "{$IP}/resources/lib", - function ( $text ) { - $this->output( $text ); - }, - function ( $text ) { - $this->error( $text ); - }, - function ( $text ) { - if ( $this->hasOption( 'verbose' ) ) { - $this->output( $text ); - } - } - ); - - $action = $this->getArg( 0 ); - $module = $this->getArg( 1, 'all' ); - return $frm->run( $action, $module ); - } -} - -$maintClass = ManageForeignResources::class; -require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/storage/checkStorage.php b/maintenance/storage/checkStorage.php index a95789d366..26d4e79787 100644 --- a/maintenance/storage/checkStorage.php +++ b/maintenance/storage/checkStorage.php @@ -86,7 +86,6 @@ class CheckStorage { foreach ( $res as $row ) { $this->oldIdMap[$row->rev_id] = $row->rev_text_id; } - $dbr->freeResult( $res ); if ( !count( $this->oldIdMap ) ) { continue; @@ -147,7 +146,6 @@ class CheckStorage { $this->addError( 'unfixable', "Error: invalid flags field \"$flags\"", $id ); } } - $dbr->freeResult( $res ); // Output errors for any missing text rows foreach ( $missingTextRows as $oldId => $revId ) { @@ -187,7 +185,6 @@ class CheckStorage { $externalNormalBlobs[$cluster][$id][] = $row->old_id; } } - $dbr->freeResult( $res ); } // Check external concat blobs for the right header @@ -210,7 +207,6 @@ class CheckStorage { foreach ( $res as $row ) { unset( $xBlobIds[$row->blob_id] ); } - $extDb->freeResult( $res ); // Print errors for missing blobs rows foreach ( $xBlobIds as $blobId => $oldId ) { $this->addError( @@ -279,7 +275,6 @@ class CheckStorage { $this->addError( 'unfixable', "Error: unrecognised object class \"$className\"", $oldId ); } } - $dbr->freeResult( $res ); } // Check local concat blob validity @@ -333,7 +328,6 @@ class CheckStorage { unset( $concatBlobs[$row->old_id] ); } - $dbr->freeResult( $res ); } // Check targets of unresolved stubs @@ -421,7 +415,6 @@ class CheckStorage { } unset( $oldIds[$row->blob_id] ); } - $extDb->freeResult( $res ); // Print errors for missing blobs rows foreach ( $oldIds as $blobId => $oldIds2 ) { diff --git a/resources/Resources.php b/resources/Resources.php index 8da38a8d91..7d9336656a 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -592,6 +592,7 @@ return [ 'group' => 'jquery.ui', ], 'jquery.ui.spinner' => [ + 'deprecated' => 'Please use "jquery.spinner" instead.', 'scripts' => 'resources/lib/jquery.ui/jquery.ui.spinner.js', 'dependencies' => [ 'jquery.ui.core', @@ -1453,7 +1454,9 @@ return [ 'styles' => 'resources/src/mediawiki.action/mediawiki.action.history.css', ], 'mediawiki.action.history.styles' => [ - 'styles' => 'resources/src/mediawiki.action/mediawiki.action.history.styles.css', + 'skinStyles' => [ + 'default' => 'resources/src/mediawiki.action/mediawiki.action.history.styles.css', + ], ], 'mediawiki.action.view.dblClickEdit' => [ 'scripts' => 'resources/src/mediawiki.action/mediawiki.action.view.dblClickEdit.js', @@ -2023,8 +2026,8 @@ return [ 'parentheses-end', 'pipe-separator' ], - 'styles' => [ - 'resources/src/mediawiki.interface.helpers.styles.less', + 'skinStyles' => [ + 'default' => 'resources/src/mediawiki.interface.helpers.styles.less', ], 'targets' => [ 'desktop', 'mobile' diff --git a/resources/lib/foreign-resources.yaml b/resources/lib/foreign-resources.yaml new file mode 100644 index 0000000000..f862850bbe --- /dev/null +++ b/resources/lib/foreign-resources.yaml @@ -0,0 +1,259 @@ +# ## Format of this file +# +# The top-level keys in this file correspond with directories under resources/lib/. +# These in turn are registered as module bundles in Resources.php. +# +# ## How to install an foreign resource +# +# 1. Add or update the url(s) for the upstream module to this YAML file. +# +# Look at other modules for examples. To install a module from npm, +# we use the tarball distribution from npmjs.org. This is the same as what +# the npm CLI uses. For example, to install jquery-client@9.2.0, use: +# . +# +# 2. If the upstream maintainers publish an integrity hash, set that as well. +# Otherwise, use manageForeignResources.php to compute the integrity hash. +# +# Run `php manageForeignResources.php make-sri "my module name"` +# +# This will download the specified file(s) and print their integrity hashes, +# already formatted in YAML, ready for copying to this file. +# +# 3. Last but not least, decide where files go. +# +# If you specified a direct url to JavaScript or CSS file, this step is +# optional. See the corresponding documentation section below for more +# information and examples for "dest" keys. Once you've set any "dest" keys, +# run `php manageForeignResources.php update "my module name"`. +# +# ## Package formats +# +# Each top-level key must use one of these types: +# +# - `file`: For a plain file. +# - `multi-file`: For multiple plain files. +# - `tar`: For a tarball archive (may be compressed). +# +# ### The "file" type +# +# * `src`: Full URL to the remote resource. +# * `integrity`: Cryptographic hash (integrity metadata format per ). +# * `dest`: [optional] The file name to use in the module directory. Default: Basename of URL. +# +# For example, the following would produce resources/lib/mymodule/x.js: +# +# mymodule: +# type: file +# src: https://mymodule.example/1.2.3/x.js +# integrity: sha384-Je+NE+saisQuoi +# +# ### The "multi-file" type +# +# * `files`: An object mapping destination paths to `src` and `integrity` keys. +# +# For example: +# +# mymodule: +# type: multi-file +# files: +# x.js: +# src: https://mymodule.example/1.2.3/x.js +# integrity: sha384-Je+NE+saisQuoi +# x.css: +# src: https://mymodule.example/1.2.3/x.css +# integrity: sha384-Je+NE+saisQuoi +# +# ### The "tar" type +# +# * `src`: Full URL to the remote resource. +# * `integrity`: Cryptographic hash (integrity metadata format per ). +# * `dest`: [optional] The default is to extract all files from the package. +# To only extract some of the files or directories, use "dest" to specify +# files, directories, and/or glob patterns. You can use a site like https://unpkg.com/ +# to easily inspect an npm package, like . +# +# For example: +# +# mymodule: +# type: tar +# src: https://registry.npmjs.org/jquery-client/-/jquery-client-9.2.0.tgz +# integrity: sha384-Je+NE+saisQuoi +# dest: +# package/dist/x.js: +# package/dist/i18n: +# package/dist/style/*.css: +# +# The would extract the "x.js" file, the "i18n" directory (recursive), +# and any "*.css" files from the "style" directory. +# + +CLDRPluralRuleParser: + type: file + src: https://raw.githubusercontent.com/santhoshtr/CLDRPluralRuleParser/0dda851/src/CLDRPluralRuleParser.js + integrity: sha384-M4taeYYG2+9Ob1/La16iO+zlRRmBV5lBR3xUKkQT6kfkJ0aLbCi6yc0RYI1BDzdh + +easy-deflate: + type: multi-file + files: + deflate.js: + src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/deflate.js + integrity: sha384-sHnZLDSWMUhA2w9ygkzCK8YFvoh/fQKY6lXMbvmrYzjuNURiLB0DZFCDNMpGyZ77 + easydeflate.js: + src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/easydeflate.js + integrity: sha384-EwPfP2RMkDPa1HkzQsXgzTsy1KEjcIzQPA1HDS/JPHjvEMvVUsCxWwm1oXql/jk2 + inflate.js: + src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/inflate.js + integrity: sha384-hMg44Hw424mUYvmzKl0JT4J8UU/1YYhTiGRtR0YX/MXNLK9qWTK0d62FBCDGxmxw + README.md: + src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/README.md + integrity: sha384-6kwcfCLivvqXBZy2ATyya+mTVWLk3eaQyBdC6tbpBtkygnBrM2SNkq3jz/l7IkvP + +html5shiv: + type: file + src: https://raw.githubusercontent.com/aFarkas/html5shiv/3.7.3/src/html5shiv.js + integrity: sha384-RPXhaTf22QktT8KTwZ6bUz/C+7CnccaIw5W/y/t0FW5WSDGj3wc3YtRIJC0w47in + +jquery: + type: file + src: https://code.jquery.com/jquery-3.3.1.js + # Integrity from link modals https://code.jquery.com/jquery/ + integrity: sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60= + dest: jquery.js + +jquery.client: + type: tar + src: https://registry.npmjs.org/jquery-client/-/jquery-client-2.0.2.tgz + integrity: sha256-8c8nBbBykHEMc4I7ksdKJvvw/P7WkaC2X46RTPdz/pw= + dest: + package/AUTHORS.txt: + package/jquery.client.js: + package/LICENSE-MIT: + package/README.md: + +jquery.cookie: + type: multi-file + files: + jquery.cookie.js: + src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/jquery.cookie.js + integrity: sha384-Xxq63E9KDgzUJ6WPNPqVeOtRIwZyx6y9DzEwY2u6LYKSnWrjSoGtWSKmTindYBf2 + MIT-LICENSE.txt: + src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/MIT-LICENSE.txt + integrity: sha384-zYsGf3KJ7S0AhOICjcoh0kkn7aGZlzYUXXX5xz8dwR9KjLMM+/JPR2g/jVOGGeId + CHANGELOG.md: + src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/CHANGELOG.md + integrity: sha384-SQOHhLc7PHxHDQpGE/zv9XfXKL0A7OBu8kuyVDnHVp+zSoWyRw4xUJ+LSm5ql4kS + +jquery.form: + type: file + src: https://raw.githubusercontent.com/jquery-form/form/ff80d9ddf4/jquery.form.js + integrity: sha384-h4G2CrcSbixzMvrrK259cNBYaL/vS1D4+KdUN9NJDzQnTU1bQ6Avluget+Id13M7 + +jquery.fullscreen: + type: file + src: https://raw.githubusercontent.com/theopolisme/jquery-fullscreen/v2.1.0/jquery.fullscreen.js + integrity: sha384-G4KPs2d99tgcsyUnJ3eeZ1r2hEKDwZfc4+/xowL/LIemq2VVwEE8HpVAWt4WYNLR + +jquery.hoverIntent: + type: file + src: https://raw.githubusercontent.com/briancherne/jquery-hoverIntent/823603fdac/jquery.hoverIntent.js + integrity: sha384-lca0haN0hqFGGh2aYUhtAgX9dhVHfQnTADH4svDeM6gcXnL7aFGeAi1NYwipDMyS + +jquery.jStorage: + type: file + src: https://raw.githubusercontent.com/andris9/jStorage/v0.4.12/jstorage.js + integrity: sha384-geMeN8k803kPp6cqRL4VNfuSM1L8DcbKRk0St/KHJzxgpX9S0y9FA6HxA/JgucrJ + +jquery.throttle-debounce: + type: file + src: https://raw.githubusercontent.com/cowboy/jquery-throttle-debounce/v1.1/jquery.ba-throttle-debounce.js + integrity: sha384-ULOy4DbAghrCqRcrTJLXOY9e4gDpWh0BeEf6xMSL0VtNudXWggcb6AmrVrl4KDAP + +moment: + type: tar + src: https://codeload.github.com/moment/moment/tar.gz/2.24.0 + integrity: sha384-2/I9rfqkN8AAgh5wOXXphuo827uV7lMmOodrCfIvqC6W6JKKiDGOwd+lE3e8R0yz + dest: + moment-2.24.0/moment.js: + moment-2.24.0/CHANGELOG.md: + moment-2.24.0/README.md: + moment-2.24.0/LICENSE: + moment-2.24.0/locale/*.js: locale + +mustache: + type: multi-file + files: + mustache.js: + src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/mustache.js + integrity: sha384-k2UYqmzoiq/qgIzZvcYBxbXQW4YdPAsXDOTkHTGb9TCZ9sjCkyT4TlaUN0wQRkql + LICENSE: + src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/LICENSE + integrity: sha384-MYVwXwula9+YkyXexOJVZ0v0DaVvG22uX57mNq5Di+7u8OH9EG9q3yuXkp1Iehiq + +oojs: + type: tar + src: https://registry.npmjs.org/oojs/-/oojs-2.2.2.tgz + integrity: sha256-ebgQW2EGrSkBCnDJBGqDpsBDjA3PMN/M8U5DyLHt9mw= + dest: + package/dist/oojs.jquery.js: + package/AUTHORS.txt: + package/LICENSE-MIT: + package/README.md: + +oojs-router: + type: tar + src: https://registry.npmjs.org/oojs-router/-/oojs-router-0.2.0.tgz + integrity: sha384-VngYqdQ3vTDMXbm4e4FUZCCGos7fB0Jkr9V+kBL5MElprK1h0yQZOzBNnMHtSJS/ + dest: + package/dist/oojs-router.js: + package/LICENSE: + package/AUTHORS.txt: + package/History.md: + +ooui: + type: tar + src: https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.31.1.tgz + integrity: sha384-M9KdU6u02zSKCVczcw6YJmSvFLhdeagNg9CPhizYVqrybL8bamrF5u6YfrFGEyiv + dest: + # Main stuff + package/dist/oojs-ui-core.js{,.map.json}: + package/dist/oojs-ui-core-{wikimediaui,apex}.css: + package/dist/oojs-ui-widgets.js{,.map.json}: + package/dist/oojs-ui-widgets-{wikimediaui,apex}.css: + package/dist/oojs-ui-toolbars.js{,.map.json}: + package/dist/oojs-ui-toolbars-{wikimediaui,apex}.css: + package/dist/oojs-ui-windows.js{,.map.json}: + package/dist/oojs-ui-windows-{wikimediaui,apex}.css: + package/dist/oojs-ui-{wikimediaui,apex}.js{,.map.json}: + package/dist/i18n: + package/dist/images: + # WikimediaUI theme + package/dist/themes/wikimediaui/images/icons/*.{svg,png}: themes/wikimediaui/images/icons + package/dist/themes/wikimediaui/images/indicators/*.{svg,png}: themes/wikimediaui/images/indicators + package/dist/themes/wikimediaui/images/textures/*.{gif,svg}: themes/wikimediaui/images/textures + package/src/themes/wikimediaui/*.json: themes/wikimediaui + package/dist/wikimedia-ui-base.less: + # Apex theme (icons, indicators, and textures) + package/src/themes/apex/*.json: themes/apex + # Misc stuff + package/dist/AUTHORS.txt: + package/dist/History.md: + package/dist/LICENSE-MIT: + package/dist/README.md: + +qunitjs: + type: multi-file + # Integrity from link modals at https://code.jquery.com/qunit/ + files: + qunit.js: + src: http://code.jquery.com/qunit/qunit-2.9.1.js + integrity: sha256-eNccBdxd8zReziWcVjEsPeyJDi3LKMYnzMXyDv8bzsU= + qunit.css: + src: https://code.jquery.com/qunit/qunit-2.9.1.css + integrity: sha256-SSS7o92V7wzcIFg3qnJL9mc4msePaT4klbxtuSGvVVo= + +sinonjs: + type: file + src: https://sinonjs.org/releases/sinon-1.17.7.js + integrity: sha384-wR63Jwy75KqwBfzCmXd6gYws6uj3qV/XMAybzXrkEYGYG3AQ58ZWwr1fVpkHa5e8 + dest: sinon.js diff --git a/resources/src/mediawiki.feedback/feedback.css b/resources/src/mediawiki.feedback/feedback.css index 92e1d04d07..c2d48353ff 100644 --- a/resources/src/mediawiki.feedback/feedback.css +++ b/resources/src/mediawiki.feedback/feedback.css @@ -1,13 +1,3 @@ -.feedback-spinner { - display: inline-block; - zoom: 1; - *display: inline; /* IE7 and below */ /* stylelint-disable declaration-block-no-duplicate-properties */ - /* @embed */ - background: url( images/spinner.gif ); - width: 18px; - height: 18px; -} - .mw-feedbackDialog-welcome-message, .mw-feedbackDialog-feedback-terms { line-height: 1.4; diff --git a/resources/src/mediawiki.feedback/feedback.js b/resources/src/mediawiki.feedback/feedback.js index 5b73e7c19e..3ffc496cac 100644 --- a/resources/src/mediawiki.feedback/feedback.js +++ b/resources/src/mediawiki.feedback/feedback.js @@ -230,9 +230,6 @@ padded: true } ); - this.$spinner = $( '
' ) - .addClass( 'feedback-spinner' ); - // Feedback form this.feedbackMessageLabel = new OO.ui.LabelWidget( { classes: [ 'mw-feedbackDialog-welcome-message' ] diff --git a/resources/src/mediawiki.feedback/images/spinner.gif b/resources/src/mediawiki.feedback/images/spinner.gif deleted file mode 100644 index aed0ea41f5..0000000000 Binary files a/resources/src/mediawiki.feedback/images/spinner.gif and /dev/null differ diff --git a/tests/phpunit/includes/WikiMapTest.php b/tests/phpunit/includes/WikiMapTest.php index e87e434f94..df05671dd2 100644 --- a/tests/phpunit/includes/WikiMapTest.php +++ b/tests/phpunit/includes/WikiMapTest.php @@ -237,13 +237,13 @@ class WikiMapTest extends MediaWikiLangTestCase { public function provideGetWikiIdFromDomain() { return [ - [ 'db-prefix', 'db-prefix' ], + [ 'db-prefix_', 'db-prefix_' ], [ wfWikiID(), wfWikiID() ], - [ new DatabaseDomain( 'db-dash', null, 'prefix' ), 'db-dash-prefix' ], + [ new DatabaseDomain( 'db-dash', null, 'prefix_' ), 'db-dash-prefix_' ], [ wfWikiID(), wfWikiID() ], - [ new DatabaseDomain( 'db-dash', null, 'prefix' ), 'db-dash-prefix' ], - [ new DatabaseDomain( 'db', 'mediawiki', 'prefix' ), 'db-prefix' ], // schema ignored - [ new DatabaseDomain( 'db', 'custom', 'prefix' ), 'db-custom-prefix' ], + [ new DatabaseDomain( 'db-dash', null, 'prefix_' ), 'db-dash-prefix_' ], + [ new DatabaseDomain( 'db', 'mediawiki', 'prefix_' ), 'db-prefix_' ], // schema ignored + [ new DatabaseDomain( 'db', 'custom', 'prefix_' ), 'db-custom-prefix_' ], ]; } @@ -279,15 +279,15 @@ class WikiMapTest extends MediaWikiLangTestCase { [ 'db', 'db', null, '' ], [ 'db-schema-','db', 'schema', '' ], [ 'db','db', 'mediawiki', '' ], // common b/c case - [ 'db-prefix', 'db', null, 'prefix' ], - [ 'db-schema-prefix', 'db', 'schema', 'prefix' ], - [ 'db-prefix', 'db', 'mediawiki', 'prefix' ], // common b/c case + [ 'db-prefix_', 'db', null, 'prefix_' ], + [ 'db-schema-prefix_', 'db', 'schema', 'prefix_' ], + [ 'db-prefix_', 'db', 'mediawiki', 'prefix_' ], // common b/c case // Bad hyphen cases (best effort support) [ 'db-stuff', 'db-stuff', null, '' ], - [ 'db-stuff-prefix', 'db-stuff', null, 'prefix' ], + [ 'db-stuff-prefix_', 'db-stuff', null, 'prefix_' ], [ 'db-stuff-schema-', 'db-stuff', 'schema', '' ], - [ 'db-stuff-schema-prefix', 'db-stuff', 'schema', 'prefix' ], - [ 'db-stuff-prefix', 'db-stuff', 'mediawiki', 'prefix' ] // common b/c case + [ 'db-stuff-schema-prefix_', 'db-stuff', 'schema', 'prefix_' ], + [ 'db-stuff-prefix_', 'db-stuff', 'mediawiki', 'prefix_' ] // common b/c case ]; } diff --git a/tests/phpunit/includes/cache/MessageCacheTest.php b/tests/phpunit/includes/cache/MessageCacheTest.php index d8330ef57b..b03a3098d1 100644 --- a/tests/phpunit/includes/cache/MessageCacheTest.php +++ b/tests/phpunit/includes/cache/MessageCacheTest.php @@ -223,8 +223,6 @@ class MessageCacheTest extends MediaWikiLangTestCase { /** * Regression test for T218918 - * @group Broken - * @fixme Disabled per https://phabricator.wikimedia.org/T219042 */ public function testLoadFromDB_fetchLatestRevision() { // Create three revisions of the same message page. @@ -239,7 +237,7 @@ class MessageCacheTest extends MediaWikiLangTestCase { $importRevision = new WikiRevision( new HashConfig() ); $importRevision->setTitle( $r3->getTitle() ); $importRevision->setComment( 'Imported edit' ); - $importRevision->setTimestamp( '19991122334455' ); + $importRevision->setTimestamp( '19991122001122' ); $importRevision->setText( 'IMPORTED OLD TEST' ); $importRevision->setUsername( 'Alan Smithee' ); diff --git a/tests/phpunit/includes/db/DatabaseSqliteTest.php b/tests/phpunit/includes/db/DatabaseSqliteTest.php index e61bd05a67..63b24dc688 100644 --- a/tests/phpunit/includes/db/DatabaseSqliteTest.php +++ b/tests/phpunit/includes/db/DatabaseSqliteTest.php @@ -164,9 +164,9 @@ class DatabaseSqliteTest extends MediaWikiTestCase { $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); $this->assertEquals( 'foo', $db->tableName( 'foo' ) ); $this->assertEquals( 'sqlite_master', $db->tableName( 'sqlite_master' ) ); - $db->tablePrefix( 'foo' ); + $db->tablePrefix( 'foo_' ); $this->assertEquals( 'sqlite_master', $db->tableName( 'sqlite_master' ) ); - $this->assertEquals( 'foobar', $db->tableName( 'bar' ) ); + $this->assertEquals( 'foo_bar', $db->tableName( 'bar' ) ); } /** @@ -531,7 +531,7 @@ class DatabaseSqliteMock extends DatabaseSqlite { return Database::factory( 'SqliteMock', $p ); } - function query( $sql, $fname = '', $tempIgnore = false ) { + function query( $sql, $fname = '', $flags = 0 ) { return true; } diff --git a/tests/phpunit/includes/db/DatabaseTestHelper.php b/tests/phpunit/includes/db/DatabaseTestHelper.php index 9679c6cec2..fb4041dd92 100644 --- a/tests/phpunit/includes/db/DatabaseTestHelper.php +++ b/tests/phpunit/includes/db/DatabaseTestHelper.php @@ -133,10 +133,10 @@ class DatabaseTestHelper extends Database { return $s; } - public function query( $sql, $fname = '', $tempIgnore = false ) { + public function query( $sql, $fname = '', $flags = 0 ) { $this->checkFunctionName( $fname ); - return parent::query( $sql, $fname, $tempIgnore ); + return parent::query( $sql, $fname, $flags ); } public function tableExists( $table, $fname = __METHOD__ ) { diff --git a/tests/phpunit/includes/db/LBFactoryTest.php b/tests/phpunit/includes/db/LBFactoryTest.php index 3d1bf59315..02c25e7b0d 100644 --- a/tests/phpunit/includes/db/LBFactoryTest.php +++ b/tests/phpunit/includes/db/LBFactoryTest.php @@ -438,6 +438,13 @@ class LBFactoryTest extends MediaWikiTestCase { ] ); } + /** + * @covers \Wikimedia\Rdbms\LoadBalancer::getConnection + * @covers \Wikimedia\Rdbms\DatabaseMysqlBase::doSelectDomain + * @covers \Wikimedia\Rdbms\DatabaseMysqlBase::selectDB + * @covers \Wikimedia\Rdbms\DatabaseMssql::selectDB + * @covers DatabaseOracle::selectDB + */ public function testNiceDomains() { global $wgDBname; @@ -518,6 +525,13 @@ class LBFactoryTest extends MediaWikiTestCase { $factory->destroy(); } + /** + * @covers \Wikimedia\Rdbms\LoadBalancer::getConnection + * @covers \Wikimedia\Rdbms\DatabaseMysqlBase::doSelectDomain + * @covers \Wikimedia\Rdbms\DatabaseMysqlBase::selectDB + * @covers \Wikimedia\Rdbms\DatabaseMssql::selectDB + * @covers DatabaseOracle::selectDB + */ public function testTrickyDomain() { global $wgDBname; @@ -530,7 +544,7 @@ class LBFactoryTest extends MediaWikiTestCase { $factory = $this->newLBFactoryMulti( [ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ], [ - 'dbName' => 'do_not_select_me' // explodes if DB is selected + 'dbname' => 'do_not_select_me' // explodes if DB is selected ] ); $lb = $factory->getMainLB(); @@ -584,46 +598,94 @@ class LBFactoryTest extends MediaWikiTestCase { $factory->destroy(); } + /** + * @covers \Wikimedia\Rdbms\LoadBalancer::getConnection + * @covers \Wikimedia\Rdbms\DatabaseMysqlBase::doSelectDomain + * @covers \Wikimedia\Rdbms\DatabaseMysqlBase::selectDB + * @covers \Wikimedia\Rdbms\DatabaseMssql::selectDB + * @covers DatabaseOracle::selectDB + */ public function testInvalidSelectDB() { - // FIXME: fails under sqlite - $this->markTestSkippedIfDbType( 'sqlite' ); + if ( wfGetDB( DB_MASTER )->databasesAreIndependent() ) { + $this->markTestSkipped( "Not applicable per databasesAreIndependent()" ); + } + $dbname = 'unittest-domain'; // explodes if DB is selected $factory = $this->newLBFactoryMulti( [ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ], [ - 'dbName' => 'do_not_select_me' // explodes if DB is selected + 'dbname' => 'do_not_select_me' // explodes if DB is selected ] ); $lb = $factory->getMainLB(); /** @var IDatabase $db */ $db = $lb->getConnection( DB_MASTER, [], '' ); - if ( $db->getType() === 'sqlite' ) { + \Wikimedia\suppressWarnings(); + try { $this->assertFalse( $db->selectDB( 'garbage-db' ) ); - } elseif ( $db->databasesAreIndependent() ) { - try { - $e = null; - $db->selectDB( 'garbage-db' ); - } catch ( \Wikimedia\Rdbms\DBConnectionError $e ) { - // expected - } - $this->assertInstanceOf( \Wikimedia\Rdbms\DBConnectionError::class, $e ); - $this->assertFalse( $db->isOpen() ); - } else { - \Wikimedia\suppressWarnings(); - try { - $this->assertFalse( $db->selectDB( 'garbage-db' ) ); - $this->fail( "No error thrown." ); - } catch ( \Wikimedia\Rdbms\DBExpectedError $e ) { - $this->assertEquals( - "Could not select database 'garbage-db'.", - $e->getMessage() - ); - } - \Wikimedia\restoreWarnings(); + $this->fail( "No error thrown." ); + } catch ( \Wikimedia\Rdbms\DBQueryError $e ) { + $this->assertRegExp( '/[\'"]garbage-db[\'"]/', $e->getMessage() ); + } + \Wikimedia\restoreWarnings(); + } + + /** + * @covers \Wikimedia\Rdbms\DatabaseSqlite::selectDB + * @covers \Wikimedia\Rdbms\DatabasePostgres::selectDB + * @expectedException \Wikimedia\Rdbms\DBConnectionError + */ + public function testInvalidSelectDBIndependant() { + $dbname = 'unittest-domain'; // explodes if DB is selected + $factory = $this->newLBFactoryMulti( + [ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ], + [ + 'dbname' => 'do_not_select_me' // explodes if DB is selected + ] + ); + $lb = $factory->getMainLB(); + + if ( !wfGetDB( DB_MASTER )->databasesAreIndependent() ) { + $this->markTestSkipped( "Not applicable per databasesAreIndependent()" ); + } + + /** @var IDatabase $db */ + $lb->getConnection( DB_MASTER, [], '' ); + } + + /** + * @covers \Wikimedia\Rdbms\DatabaseSqlite::selectDB + * @covers \Wikimedia\Rdbms\DatabasePostgres::selectDB + * @expectedException \Wikimedia\Rdbms\DBConnectionError + */ + public function testInvalidSelectDBIndependant2() { + $dbname = 'unittest-domain'; // explodes if DB is selected + $factory = $this->newLBFactoryMulti( + [ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ], + [ + 'dbname' => 'do_not_select_me' // explodes if DB is selected + ] + ); + $lb = $factory->getMainLB(); + + if ( !wfGetDB( DB_MASTER )->databasesAreIndependent() ) { + $this->markTestSkipped( "Not applicable per databasesAreIndependent()" ); } + + $db = $lb->getConnection( DB_MASTER ); + \Wikimedia\suppressWarnings(); + $db->selectDB( 'garbage-db' ); + \Wikimedia\restoreWarnings(); } + /** + * @covers \Wikimedia\Rdbms\LoadBalancer::getConnection + * @covers \Wikimedia\Rdbms\LoadBalancer::redefineLocalDomain + * @covers \Wikimedia\Rdbms\DatabaseMysqlBase::selectDB + * @covers \Wikimedia\Rdbms\DatabaseMssql::selectDB + * @covers DatabaseOracle::selectDB + */ public function testRedefineLocalDomain() { global $wgDBname; @@ -645,10 +707,10 @@ class LBFactoryTest extends MediaWikiTestCase { ); unset( $conn1 ); - $factory->redefineLocalDomain( 'somedb-prefix' ); - $this->assertEquals( 'somedb-prefix', $factory->getLocalDomainID() ); + $factory->redefineLocalDomain( 'somedb-prefix_' ); + $this->assertEquals( 'somedb-prefix_', $factory->getLocalDomainID() ); - $domain = new DatabaseDomain( $wgDBname, null, 'pref' ); + $domain = new DatabaseDomain( $wgDBname, null, 'pref_' ); $factory->redefineLocalDomain( $domain ); $n = 0; diff --git a/tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php b/tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php index b36fe11f85..e188ba8f69 100644 --- a/tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php +++ b/tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php @@ -13,7 +13,7 @@ class DatabaseDomainTest extends PHPUnit\Framework\TestCase { public static function provideConstruct() { return [ 'All strings' => - [ 'foo', 'bar', 'baz', 'foo-bar-baz' ], + [ 'foo', 'bar', 'baz_', 'foo-bar-baz_' ], 'Nothing' => [ null, null, '', '' ], 'Invalid $database' => @@ -23,9 +23,9 @@ class DatabaseDomainTest extends PHPUnit\Framework\TestCase { 'Invalid $prefix' => [ 'foo', 'bar', 0, '', true ], 'Dash' => - [ 'foo-bar', 'baz', 'baa', 'foo?hbar-baz-baa' ], + [ 'foo-bar', 'baz', 'baa_', 'foo?hbar-baz-baa_' ], 'Question mark' => - [ 'foo?bar', 'baz', 'baa', 'foo??bar-baz-baa' ], + [ 'foo?bar', 'baz', 'baa_', 'foo??bar-baz-baa_' ], ]; } @@ -53,17 +53,17 @@ class DatabaseDomainTest extends PHPUnit\Framework\TestCase { 'Basic' => [ 'foo', 'foo', null, '' ], 'db+prefix' => - [ 'foo-bar', 'foo', null, 'bar' ], + [ 'foo-bar_', 'foo', null, 'bar_' ], 'db+schema+prefix' => - [ 'foo-bar-baz', 'foo', 'bar', 'baz' ], + [ 'foo-bar-baz_', 'foo', 'bar', 'baz_' ], '?h -> -' => - [ 'foo?hbar-baz-baa', 'foo-bar', 'baz', 'baa' ], + [ 'foo?hbar-baz-baa_', 'foo-bar', 'baz', 'baa_' ], '?? -> ?' => - [ 'foo??bar-baz-baa', 'foo?bar', 'baz', 'baa' ], + [ 'foo??bar-baz-baa_', 'foo?bar', 'baz', 'baa_' ], '? is left alone' => - [ 'foo?bar-baz-baa', 'foo?bar', 'baz', 'baa' ], + [ 'foo?bar-baz-baa_', 'foo?bar', 'baz', 'baa_' ], 'too many parts' => - [ 'foo-bar-baz-baa', '', '', '', true ], + [ 'foo-bar-baz-baa_', '', '', '', true ], 'from instance' => [ DatabaseDomain::newUnspecified(), null, null, '' ], ]; @@ -90,13 +90,13 @@ class DatabaseDomainTest extends PHPUnit\Framework\TestCase { 'Basic' => [ 'foo', 'foo', null, '' ], 'db+prefix' => - [ 'foo-bar', 'foo', null, 'bar' ], + [ 'foo-bar_', 'foo', null, 'bar_' ], 'db+schema+prefix' => - [ 'foo-bar-baz', 'foo', 'bar', 'baz' ], + [ 'foo-bar-baz_', 'foo', 'bar', 'baz_' ], '?h -> -' => - [ 'foo?hbar-baz-baa', 'foo-bar', 'baz', 'baa' ], + [ 'foo?hbar-baz-baa_', 'foo-bar', 'baz', 'baa_' ], '?? -> ?' => - [ 'foo??bar-baz-baa', 'foo?bar', 'baz', 'baa' ], + [ 'foo??bar-baz-baa_', 'foo?bar', 'baz', 'baa_' ], 'Nothing' => [ '', null, null, '' ], ]; @@ -136,23 +136,23 @@ class DatabaseDomainTest extends PHPUnit\Framework\TestCase { 'Basic' => [ 'foo', 'foo', null, '', true ], 'db+prefix' => - [ 'foo-bar', 'foo', null, 'bar', true ], + [ 'foo-bar_', 'foo', null, 'bar_', true ], 'db+schema+prefix' => - [ 'foo-bar-baz', 'foo', 'bar', 'baz', true ], + [ 'foo-bar-baz_', 'foo', 'bar', 'baz_', true ], 'db+dontcare_schema+prefix' => - [ 'foo-bar-baz', 'foo', null, 'baz', false ], + [ 'foo-bar-baz_', 'foo', null, 'baz_', false ], '?h -> -' => - [ 'foo?hbar-baz-baa', 'foo-bar', 'baz', 'baa', true ], + [ 'foo?hbar-baz-baa_', 'foo-bar', 'baz', 'baa_', true ], '?? -> ?' => - [ 'foo??bar-baz-baa', 'foo?bar', 'baz', 'baa', true ], + [ 'foo??bar-baz-baa_', 'foo?bar', 'baz', 'baa_', true ], 'Nothing' => [ '', null, null, '', true ], 'dontcaredb+dontcaredbschema+prefix' => - [ 'mywiki-mediawiki-prefix', null, null, 'prefix', false ], + [ 'mywiki-mediawiki-prefix_', null, null, 'prefix_', false ], 'dontcaredb+schema+prefix' => - [ 'mywiki-schema-prefix', null, 'schema', 'prefix', false ], + [ 'mywiki-schema-prefix_', null, 'schema', 'prefix_', false ], 'db+dontcareschema+prefix' => - [ 'mywiki-schema-prefix', 'mywiki', null, 'prefix', false ], + [ 'mywiki-schema-prefix_', 'mywiki', null, 'prefix_', false ], 'postgres-db-jobqueue' => [ 'postgres-mediawiki-', 'postgres', null, '', false ] ]; @@ -178,13 +178,13 @@ class DatabaseDomainTest extends PHPUnit\Framework\TestCase { public static function provideIsCompatible2() { return [ 'db+schema+prefix' => - [ 'mywiki-schema-prefix', 'thatwiki', 'schema', 'prefix' ], + [ 'mywiki-schema-prefix_', 'thatwiki', 'schema', 'prefix_' ], 'dontcaredb+dontcaredbschema+prefix' => - [ 'thatwiki-mediawiki-otherprefix', null, null, 'prefix' ], + [ 'thatwiki-mediawiki-otherprefix_', null, null, 'prefix_' ], 'dontcaredb+schema+prefix' => - [ 'mywiki-otherschema-prefix', null, 'schema', 'prefix' ], + [ 'mywiki-otherschema-prefix_', null, 'schema', 'prefix_' ], 'db+dontcareschema+prefix' => - [ 'notmywiki-schema-prefix', 'mywiki', null, 'prefix' ], + [ 'notmywiki-schema-prefix_', 'mywiki', null, 'prefix_' ], ]; } diff --git a/tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php b/tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php index 40c260cc94..c0d25553dc 100644 --- a/tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php +++ b/tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php @@ -1343,7 +1343,7 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase { } /** - * @covers Wikimedia\Rdbms\Database::registerTempTableOperation + * @covers Wikimedia\Rdbms\Database::registerTempTableWrite */ public function testSessionTempTables() { $temp1 = $this->database->tableName( 'tmp_table_1' ); diff --git a/tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php b/tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php index bd1c1126c9..f81e9bb42b 100644 --- a/tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php +++ b/tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php @@ -633,10 +633,10 @@ class DatabaseTest extends PHPUnit\Framework\TestCase { $oldDomain = $this->db->getDomainId(); $this->assertInternalType( 'string', $old, 'Prefix is string' ); $this->assertSame( $old, $this->db->tablePrefix(), "Prefix unchanged" ); - $this->assertSame( $old, $this->db->tablePrefix( 'xxx' ) ); - $this->assertSame( 'xxx', $this->db->tablePrefix(), "Prefix set" ); + $this->assertSame( $old, $this->db->tablePrefix( 'xxx_' ) ); + $this->assertSame( 'xxx_', $this->db->tablePrefix(), "Prefix set" ); $this->db->tablePrefix( $old ); - $this->assertNotEquals( 'xxx', $this->db->tablePrefix() ); + $this->assertNotEquals( 'xxx_', $this->db->tablePrefix() ); $this->assertSame( $oldDomain, $this->db->getDomainId() ); $old = $this->db->dbSchema(); @@ -659,10 +659,10 @@ class DatabaseTest extends PHPUnit\Framework\TestCase { $oldSchema = $this->db->dbSchema(); $oldPrefix = $this->db->tablePrefix(); - $this->db->selectDomain( 'testselectdb-xxx' ); + $this->db->selectDomain( 'testselectdb-xxx_' ); $this->assertSame( 'testselectdb', $this->db->getDBname() ); $this->assertSame( '', $this->db->dbSchema() ); - $this->assertSame( 'xxx', $this->db->tablePrefix() ); + $this->assertSame( 'xxx_', $this->db->tablePrefix() ); $this->db->selectDomain( $oldDomain ); $this->assertSame( $oldDatabase, $this->db->getDBname() ); @@ -670,10 +670,10 @@ class DatabaseTest extends PHPUnit\Framework\TestCase { $this->assertSame( $oldPrefix, $this->db->tablePrefix() ); $this->assertSame( $oldDomain, $this->db->getDomainId() ); - $this->db->selectDomain( 'testselectdb-schema-xxx' ); + $this->db->selectDomain( 'testselectdb-schema-xxx_' ); $this->assertSame( 'testselectdb', $this->db->getDBname() ); $this->assertSame( 'schema', $this->db->dbSchema() ); - $this->assertSame( 'xxx', $this->db->tablePrefix() ); + $this->assertSame( 'xxx_', $this->db->tablePrefix() ); $this->db->selectDomain( $oldDomain ); $this->assertSame( $oldDatabase, $this->db->getDBname() ); diff --git a/tests/selenium/specs/page.js b/tests/selenium/specs/page.js index d35843b884..80e12cd56f 100644 --- a/tests/selenium/specs/page.js +++ b/tests/selenium/specs/page.js @@ -91,7 +91,7 @@ describe( 'Page', function () { // check HistoryPage.open( name ); - assert.strictEqual( HistoryPage.comment.getText(), `(Created or updated page with "${content}")` ); + assert.strictEqual( HistoryPage.comment.getText(), `Created or updated page with "${content}"` ); } ); it( 'should be deletable', function () {