1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/mediawiki_ynh.git synced 2024-09-03 19:46:05 +02:00

[fix] PHP5 CLI dependency + sources in the git repo

This commit is contained in:
Kload 2014-05-08 09:57:31 +02:00
parent feb297c93b
commit b138f92d88
2785 changed files with 1443335 additions and 11 deletions

View file

@ -17,6 +17,9 @@ db_pwd=$(dd if=/dev/urandom bs=1 count=200 2> /dev/null | tr -c -d '[A-Za-z0-9]'
# Use 'mediawiki' as database name and user
db_user=mediawiki
# Instal php5-cli dependency
sudo apt-get install php5-cli -y
# Initialize database and store mysql password for upgrade
sudo yunohost app initdb $db_user -p $db_pwd -s $(readlink -e ../conf/SQL/mysql.initial.sql)
sudo yunohost app setting mediawiki mysqlpwd -v $db_pwd
@ -24,19 +27,11 @@ sudo yunohost app setting mediawiki mysqlpwd -v $db_pwd
# Copy files to the right place
final_path=/var/www/mediawiki
sudo mkdir -p $final_path
sudo wget http://releases.wikimedia.org/mediawiki/1.22/mediawiki-1.22.6.tar.gz
sudo tar xvzf mediawiki-*.tar.gz
sudo mv mediawiki-*/* $final_path
sudo rm -R mediawiki-*
sudo cp ../sources/mediawiki/* $final_path
sudo cp ../conf/LocalSettings.php $final_path/
# LDAP Extension
sudo wget https://codeload.github.com/wikimedia/mediawiki-extensions-LdapAuthentication/legacy.tar.gz/REL1_22
sudo tar -xzf REL1_22
sudo mkdir $final_path/extensions/LdapAuthentication
sudo mv wikimedia-mediawiki-extensions-LdapAuthentication*/* $final_path/extensions/LdapAuthentication/
sudo rm -R wikimedia-mediawiki-extensions-LdapAuthentication*
sudo rm REL1_22
sudo cp -r ../sources/LdapAuthentication $final_path/extensions/
# Change variables in Mediawiki configuration
sudo sed -i "s/ynh_wiki_name/$wiki_name/g" $final_path/LocalSettings.php
@ -60,4 +55,4 @@ sudo cp ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/mediawiki.conf
# Reload Nginx and regenerate SSOwat conf
sudo service nginx reload
sudo yunohost app ssowatconf
sudo yunohost app ssowatconf

4
sources/LdapAuthentication/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.svn
*~
*.kate-swp
.*.swp

View file

@ -0,0 +1,5 @@
[gerrit]
host=gerrit.wikimedia.org
port=29418
project=mediawiki/extensions/LdapAuthentication.git
defaultbranch=master

View file

@ -0,0 +1,363 @@
<?php
/**
* Internationalisation file for extension LdapAuthentication.
*
* @file
* @ingroup Extensions
*/
$messages = array();
/** English
* @author Ryan Lane
*/
$messages['en'] = array(
'ldapauthentication-desc' => 'LDAP authentication plugin with support for multiple LDAP authentication methods',
);
/** Message documentation (Message documentation)
* @author Fryed-peach
* @author Shirayuki
*/
$messages['qqq'] = array(
'ldapauthentication-desc' => '{{desc|name=LDAP Authentication|url=http://www.mediawiki.org/wiki/Extension:LDAP_Authentication}}',
);
/** Afrikaans (Afrikaans)
* @author Naudefj
*/
$messages['af'] = array(
'ldapauthentication-desc' => 'Uitbreiding vir LDAP-outentisiteit wat die meeste LDAP-outentisiteitsmetodes ondersteun',
);
/** Gheg Albanian (Gegë)
* @author Mdupont
*/
$messages['aln'] = array(
'ldapauthentication-desc' => 'plugin LDAP vertetimi me mbështetje për metoda të shumta tek LDAP',
);
/** Arabic (العربية)
* @author Meno25
*/
$messages['ar'] = array(
'ldapauthentication-desc' => 'إضافة تحقيق LDAP بدعم لوسائل تحقيق LDAP متعددة',
);
/** Asturian (asturianu)
* @author Xuacu
*/
$messages['ast'] = array(
'ldapauthentication-desc' => "Complemento p'autenticación LDAP con sofitu pa dellos métodos d'autenticación LDAP",
);
/** Belarusian (Taraškievica orthography) (беларуская (тарашкевіца))
* @author EugeneZelenko
*/
$messages['be-tarask'] = array(
'ldapauthentication-desc' => 'Дапаўненьне LDAP-аўтэнтыфікацыі з падтрымкай некалькіх мэтадаў аўтэнтыфікацыі LDAP',
);
/** Breton (brezhoneg)
* @author Fulup
*/
$messages['br'] = array(
'ldapauthentication-desc' => 'Adveziant gwiriekaat LDAP ennañ meur a hentenn wiriekaat LDAP',
);
/** Bosnian (bosanski)
* @author CERminator
*/
$messages['bs'] = array(
'ldapauthentication-desc' => 'Proširenje LDAP autentifikacije sa podrškom za mnoge metode LDAP autentifikacije',
);
/** Catalan (català)
* @author Paucabot
*/
$messages['ca'] = array(
'ldapauthentication-desc' => "Connector d'autentificació LDAP amb suport per a diversos mètodes d'autenticació LDAP",
);
/** Czech (česky)
* @author Mormegil
*/
$messages['cs'] = array(
'ldapauthentication-desc' => 'Autentizační modul pro LDAP podporující několik autentizačních metod LDAP',
);
/** German (Deutsch)
* @author Imre
* @author Kghbln
*/
$messages['de'] = array(
'ldapauthentication-desc' => 'Ermöglicht die LDAP-Authentifizierung mit Hilfe mehrerer Authentifizierungsmethoden',
);
/** Lower Sorbian (dolnoserbski)
* @author Michawiki
*/
$messages['dsb'] = array(
'ldapauthentication-desc' => 'Tykac awtentifikacije LDAP z pódpěru za někotare metody LDAP-awtentifikacije',
);
/** Esperanto (Esperanto)
* @author Blahma
*/
$messages['eo'] = array(
'ldapauthentication-desc' => 'LDAP-aŭtentiga kromprogramo kun subteno de pluraj LDAP-aŭtentigaj metodoj',
);
/** Spanish (español)
* @author Translationista
*/
$messages['es'] = array(
'ldapauthentication-desc' => 'Complemento de autentificación LDAP con apoyo de múltiples métodos de autentificación LDAP',
);
/** Finnish (suomi)
* @author Centerlink
*/
$messages['fi'] = array(
'ldapauthentication-desc' => 'LDAP-todentamisliitännäinen useiden LDAP-todennustapojen tuella',
);
/** French (français)
* @author IAlex
* @author Urhixidur
*/
$messages['fr'] = array(
'ldapauthentication-desc' => 'Extension dauthentification LDAP prenant en charge de multiples méthodes dauthentification LDAP',
);
/** Galician (galego)
* @author Toliño
*/
$messages['gl'] = array(
'ldapauthentication-desc' => 'Complemento de autenticación LDAP con soporte para varios métodos de autenticación LDAP',
);
/** Swiss German (Alemannisch)
* @author Als-Holder
*/
$messages['gsw'] = array(
'ldapauthentication-desc' => 'LDAP-Authentifizierigs-Plugin mit Unterstitzig fir multipli LDAP-Authentifizierigs-Merthode',
);
/** Hebrew (עברית)
* @author YaronSh
*/
$messages['he'] = array(
'ldapauthentication-desc' => 'תוסף אימות LDAP עם תמיכה במספר שיטות LDAP לאימות',
);
/** Upper Sorbian (hornjoserbsce)
* @author Michawiki
*/
$messages['hsb'] = array(
'ldapauthentication-desc' => 'Tykač awtentifikacije LDAP z podpěru za wjacore metody LDAP-awtentifikacije',
);
/** Hungarian (magyar)
* @author Glanthor Reviol
*/
$messages['hu'] = array(
'ldapauthentication-desc' => 'LDAP hitelesítési bővítmény többféle LDAP azonosítási módszer támogatásával',
);
/** Interlingua (interlingua)
* @author McDutchie
*/
$messages['ia'] = array(
'ldapauthentication-desc' => 'Plugin pro authentication LDAP con supporto pro multiple methodos de authentication LDAP',
);
/** Indonesian (Bahasa Indonesia)
* @author IvanLanin
*/
$messages['id'] = array(
'ldapauthentication-desc' => 'Pengaya otentikasi LDAP dengan dukungan untuk berbagai metode otentikasi LDAP',
);
/** Italian (italiano)
* @author HalphaZ
*/
$messages['it'] = array(
'ldapauthentication-desc' => 'Plugin di autenticazione LDAP con supporto a diversi metodi di autenticazione LDAP',
);
/** Japanese (日本語)
* @author Aotake
* @author Shirayuki
*/
$messages['ja'] = array(
'ldapauthentication-desc' => '複数の LDAP 認証方式対応の LDAP 認証プラグイン',
);
/** Korean (한국어)
* @author 아라
*/
$messages['ko'] = array(
'ldapauthentication-desc' => '여러 LDAP 인증 방법에 대해 지원하는 LDAP 인증 플러그인',
);
/** Colognian (Ripoarisch)
* @author Purodha
*/
$messages['ksh'] = array(
'ldapauthentication-desc' => 'Dat Zohsatzprojramm för et Enlogge övver <i lang="en">LDAP</i> löht ungerscheidlijje Mettoohde zoh, för et Prööfe, wä wä es.',
);
/** Luxembourgish (Lëtzebuergesch)
* @author Robby
*/
$messages['lb'] = array(
'ldapauthentication-desc' => 'Authentifikatiouns-Plugin fir LDAP mat Ënnerstëtzung fir multipel LDAP Authentifikatiouns-Methoden',
);
/** Macedonian (македонски)
* @author Bjankuloski06
*/
$messages['mk'] = array(
'ldapauthentication-desc' => 'LDAP приклучок за потврдување со поддршка за повеќе методи на LDAP потврдување',
);
/** Malay (Bahasa Melayu)
* @author Anakmalaysia
*/
$messages['ms'] = array(
'ldapauthentication-desc' => 'Pemalam pengesahan LDAP dengan sokongan untuk berbilang kaedah pengesahan LDAP',
);
/** Norwegian Bokmål (norsk bokmål)
* @author Nghtwlkr
*/
$messages['nb'] = array(
'ldapauthentication-desc' => 'Programutvidelse for LDAP-autentisering med støtte for flere LDAP-autentiseringsmetoder',
);
/** Dutch (Nederlands)
* @author Siebrand
*/
$messages['nl'] = array(
'ldapauthentication-desc' => 'LDAP-authenticatieplug-in met ondersteuning voor meerdere LDAP-authenticatiemethoden',
);
/** Occitan (occitan)
* @author Cedric31
*/
$messages['oc'] = array(
'ldapauthentication-desc' => "Plugin d'autentificacion LDAP amb supòrt de metòdes d'autentificacion LDAP multiples",
);
/** Polish (polski)
* @author Sp5uhe
*/
$messages['pl'] = array(
'ldapauthentication-desc' => 'Wtyczka autoryzacji użytkowników z użyciem LDAP ze wsparciem dla wielu metod autoryzacji',
);
/** Piedmontese (Piemontèis)
* @author Borichèt
* @author Dragonòt
*/
$messages['pms'] = array(
'ldapauthentication-desc' => "Plugin për l'autenticassion LDAP con apògg për vàire manere d'autenticassion LDAP",
);
/** Portuguese (português)
* @author Hamilton Abreu
*/
$messages['pt'] = array(
'ldapauthentication-desc' => "''Plugin'' de autenticação LDAP, com suporte para vários métodos de autenticação",
);
/** Brazilian Portuguese (português do Brasil)
* @author Giro720
*/
$messages['pt-br'] = array(
'ldapauthentication-desc' => "''Plugin'' de autenticação LDAP, com suporte para vários métodos de autenticação",
);
/** tarandíne (tarandíne)
* @author Joetaras
*/
$messages['roa-tara'] = array(
'ldapauthentication-desc' => "plugin de autendicazione LDAP cu 'u supporte pe autendicaziune multeple de metode LDAP",
);
/** Russian (русский)
* @author Александр Сигачёв
*/
$messages['ru'] = array(
'ldapauthentication-desc' => 'Плагин LDAP-аутентификации с поддержкой нескольких методов проверки подлинности LDAP',
);
/** Slovak (slovenčina)
* @author Helix84
*/
$messages['sk'] = array(
'ldapauthentication-desc' => 'Zásuvný modul na autentifikáciu prostredníctvom LDAP s podporou viacerých metód LDAP',
);
/** Serbian (Cyrillic script) (српски (ћирилица))
* @author Михајло Анђелковић
*/
$messages['sr-ec'] = array(
'ldapauthentication-desc' => 'Плагин за LDAP ауторизацију, са подршком за више метода LDAP ауторизације',
);
/** Serbian (Latin script) (srpski (latinica))
*/
$messages['sr-el'] = array(
'ldapauthentication-desc' => 'Plagin za LDAP autorizaciju, sa podrškom za više metoda LDAP autorizacije',
);
/** Swedish (svenska)
* @author Boivie
*/
$messages['sv'] = array(
'ldapauthentication-desc' => 'LDAP-autentiseringsplugin med stöd för flera LDAP-autentiseringsmetoder',
);
/** Tagalog (Tagalog)
* @author AnakngAraw
*/
$messages['tl'] = array(
'ldapauthentication-desc' => 'Pampasak na pangpagpapatotoo ng LDAP na may suporta para sa maramihang mga pamamaraan ng pagpapatotoo ng LDAP',
);
/** Turkish (Türkçe)
* @author Vito Genovese
*/
$messages['tr'] = array(
'ldapauthentication-desc' => 'Birden çok LDAP kimlik doğrulama yöntemini destekleyen LDAP kimlik doğrulama eklentisi',
);
/** Ukrainian (українська)
* @author Ytsukeng Fyvaprol
*/
$messages['uk'] = array(
'ldapauthentication-desc' => 'Плагін LDAP-аутентифікації з підтримкою декількох методів перевірки автентичності LDAP',
);
/** Vietnamese (Tiếng Việt)
* @author Minh Nguyen
*/
$messages['vi'] = array(
'ldapauthentication-desc' => 'Phần bổ trợ xác thực LDAP hỗ trợ nhiều phương pháp xác thực LDAP',
);
/** Simplified Chinese (中文(简体)‎)
* @author Yanmiao liu
*/
$messages['zh-hans'] = array(
'ldapauthentication-desc' => '具有多种LDAP认证方法支持的LDAP认证插件',
);
/** Traditional Chinese (中文(繁體)‎)
* @author Anakmalaysia
*/
$messages['zh-hant'] = array(
'ldapauthentication-desc' => '具有多種LDAP認證方法支持的LDAP認證外掛程式',
);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,124 @@
<?php
class LdapAutoAuthentication {
/**
* Does the web server authentication piece of the LDAP plugin.
*
* @param $user User
* @param $result bool
* @return bool
*/
public static function Authenticate( $user, &$result = null ) {
/**
* @var $wgAuth LdapAuthenticationPlugin
*/
global $wgAuth;
$wgAuth->printDebug( "Entering AutoAuthentication.", NONSENSITIVE );
if ( $user->isLoggedIn() ) {
$wgAuth->printDebug( "User is already logged in.", NONSENSITIVE );
return true;
}
$wgAuth->printDebug( "User isn't logged in, calling setup.", NONSENSITIVE );
// Let regular authentication plugins configure themselves for auto
// authentication chaining
$wgAuth->autoAuthSetup();
$autoauthname = $wgAuth->getConf( 'AutoAuthUsername' );
$wgAuth->printDebug( "Calling authenticate with username ($autoauthname).", NONSENSITIVE );
// The user hasn't already been authenticated, let's check them
$authenticated = $wgAuth->authenticate( $autoauthname, '' );
if ( !$authenticated ) {
// If the user doesn't exist in LDAP, there isn't much reason to
// go any further.
$wgAuth->printDebug( "User wasn't found in LDAP, exiting.", NONSENSITIVE );
return false;
}
// We need the username that MediaWiki will always use, not necessarily the one we
// get from LDAP.
$mungedUsername = $wgAuth->getCanonicalName( $autoauthname );
$wgAuth->printDebug( "User exists in LDAP; finding the user by name ($mungedUsername) in MediaWiki.", NONSENSITIVE );
$localId = User::idFromName( $mungedUsername );
$wgAuth->printDebug( "Got id ($localId).", NONSENSITIVE );
// Is the user already in the database?
if ( !$localId ) {
$userAdded = self::attemptAddUser( $user, $mungedUsername );
if ( !$userAdded ) {
$result = false;
return false;
}
} else {
$wgAuth->printDebug( "User exists in local database, logging in.", NONSENSITIVE );
$user->setID( $localId );
$user->loadFromId();
$user->setCookies();
$wgAuth->updateUser( $user );
wfSetupSession();
$result = true;
}
return true;
}
/**
* @param $user User
* @param $mungedUsername String
* @return bool
*/
public static function attemptAddUser( $user, $mungedUsername ) {
/**
* @var $wgAuth LdapAuthenticationPlugin
*/
global $wgAuth;
if ( !$wgAuth->autoCreate() ) {
$wgAuth->printDebug( "Cannot automatically create accounts.", NONSENSITIVE );
return false;
}
$wgAuth->printDebug( "User does not exist in local database; creating.", NONSENSITIVE );
// Checks passed, create the user
$user->loadDefaults( $mungedUsername );
$status = $user->addToDatabase();
if ( $status !== null && !$status->isOK() ) {
$wgAuth->printDebug( "Creation failed: " . $status->getWikiText(), NONSENSITIVE );
return false;
}
$wgAuth->initUser( $user, true );
$user->setCookies();
wfSetupSession();
# Update user count
$ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
$ssUpdate->doUpdate();
# Notify hooks (e.g. Newuserlog)
wfRunHooks( 'AuthPluginAutoCreate', array( $user ) );
return true;
}
/**
* No logout link in MW
* @param $personal_urls array
* @param $title Title
* @return bool
*/
public static function NoLogout( &$personal_urls, $title ) {
/**
* @var $wgAuth LdapAuthenticationPlugin
*/
global $wgAuth;
$wgAuth->printDebug( "Entering NoLogout.", NONSENSITIVE );
unset( $personal_urls['logout'] );
return true;
}
}

View file

@ -0,0 +1 @@
This authentication plugin allows MediaWiki to use an LDAP store as its user database for authentication, and some authorization. Full functionality and configuration information can be found at: http://www.mediawiki.org/wiki/Extension:LDAP_Authentication

View file

@ -0,0 +1,13 @@
CREATE TABLE /*_*/ldap_domains (
-- IF for domain
domain_id int not null primary key auto_increment,
-- domain itself
domain varchar(255) binary not null,
-- User to which this domain belongs
user_id int not null
) /*$wgDBTableOptions*/;
CREATE INDEX /*i*/user_id on /*_*/ldap_domains (user_id);

View file

@ -0,0 +1,13 @@
CREATE TABLE ldap_domains (
-- IF for domain
domain_id serial PRIMARY KEY,
-- domain itself
domain varchar(255) not null,
-- User to which this domain belongs
user_id integer not null
) /*$wgDBTableOptions*/;
CREATE INDEX user_id on ldap_domains (user_id);

380
sources/mediawiki/COPYING Normal file
View file

@ -0,0 +1,380 @@
== License and copyright information ==
=== License ===
MediaWiki is licensed under the terms of the GNU General Public License,
version 2 or later. Derivative works and later versions of the code must be
free software licensed under the same or a compatible license. This includes
"extensions" that use MediaWiki functions or variables; see
http://www.gnu.org/licenses/gpl-faq.html#GPLAndPlugins for details.
For the full text of version 2 of the license, see
https://www.gnu.org/licenses/gpl-2.0.html or '''GNU General Public License'''
below.
=== Copyright owners ===
MediaWiki contributors, including those listed in the CREDITS file, hold the
copyright to this work.
=== Additional license information ===
Some components of MediaWiki imported from other projects may be under other
Free and Open Source, or Free Culture, licenses. Specific details of their
licensing information can be found in those components.
Sections of code written exclusively by Lee Crocker or Erik Moeller are also
released into the public domain, which does not impair the obligations of users
under the GPL for use of the whole code or other sections thereof.
MediaWiki uses the following Creative Commons icons to illustrate links to the
CC licenses:
* skins/common/images/cc-by-nc-sa.png
* skins/common/images/cc-by-sa.png
These icons are trademarked, and used subject to the CC trademark license,
available at http://creativecommons.org/policies#trademark
== GNU GENERAL PUBLIC LICENSE ==
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
=== Preamble ===
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
== TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ==
'''0.''' This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
'''1.''' You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
'''2.''' You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
'''a)''' You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
'''b)''' You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
'''c)''' If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
'''3.''' You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
'''a)''' Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
'''b)''' Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
'''c)''' Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
'''4.''' You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
'''5.''' You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
'''6.''' Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
'''7.''' If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
'''8.''' If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
'''9.''' The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
'''10.''' If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
=== NO WARRANTY ===
'''11.''' BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
'''12.''' IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
'''END OF TERMS AND CONDITIONS'''
== How to Apply These Terms to Your New Programs ==
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

242
sources/mediawiki/CREDITS Normal file
View file

@ -0,0 +1,242 @@
MediaWiki 1.22 is a collaborative project released under the
GNU General Public License v2. We would like to recognize the
following names for their contribution to the product.
<!-- Please notice that the following can be found parsed under Special:Version/Credits -->
== Developers ==
* Aaron Schulz
* Alex Z.
* Alexander Monk
* Alexandre Emsenhuber
* Andrew Garrett
* Arthur Richards
* Aryeh Gregor
* Antoine Musso
* Brian Wolff
* Bertrand Grondin
* Brad Jorsch
* Brion Vibber
* Bryan Tong Minh
* Chad Horohoe
* Charles Melbye
* church of emacs
* Daniel Friesen
* Daniel Kinzler
* Daniel Renfro
* Danny B.
* David McCabe
* Derk-Jan Hartman
* Domas Mituzas
* Emufarmers
* Fran Rogers
* Greg Sabino Mullane
* Guy Van den Broeck
* Happy-melon
* Hojjat
* Ian Baker
* Ilmari Karonen
* Jack D. Pond
* Jack Phoenix
* Jan Paul Posma
* Jason Richey
* Jeroen De Dauw
* John Du Hart
* Jon Harald Søby
* Juliano F. Ravasi
* Ryan Kaldari
* Leo Koppelkamm
* Leon Weber
* Leslie Hoare
* Marco Schuster
* Marius Hoch
* Matěj Grabovský
* Matt Johnston
* Max Semenik
* Meno25
* MinuteElectron
* Mohamed Magdy
* Nathaniel Herman
* Neil Kandalgaonkar
* Nicolas Dumazet
* Niklas Laxström
* Ori Livneh
* Patrick Reilly
* Philip Tzou
* Platonides
* Purodha Blissenbach
* Raimond Spekking
* Remember the dot
* Roan Kattouw
* Robert Stojnić
* Robin Pepermans
* Rotem Liss
* Ryan Lane
* Ryan Schmidt
* Sam Reed
* Shinjiman
* Siebrand Mazeland
* SQL
* Soxred93
* Szymon Świerkosz
* Thomas Bleher
* Tim Starling
* Timo Tijhof
* Thomas Gries
* Trevor Parscal
* Victor Vasiliev
* Yesid Carrillo
* Yuri Astrakhan
== Patch Contributors ==
* Aaron Pramana
* Aaron Ball
* Agbad
* Ahmad Sherif
* Alejandro Mery
* Amalthea
* Amir E. Aharoni
* Andrew Dunbar
* Antonio Ospite
* Asier Lostalé
* Azliq7
* Bagariavivek
* Bartosz Dziewoński
* Beau
* Benny Situ
* Bergi
* Borislav Manolov
* Brad Jorsch
* Brent G
* Brianna Laugher
* Carlin
* Carsten Nielsen
* Chris Steipp
* Christian Neubauer
* Christian Aistleitner
* Conrad Irwin
* cryptocoryne
* Dan Barrett
* Dan Collins
* Dan Nessett
* Daniel Arnold
* Daniel Werner
* David Baumgarten
* Denny Vrandecic
* Dévai Tamás
* Ebrahim Byagowi
* Edward Z. Yang
* Elvis Stansvik
* Erwin Dokter
* Federico Leva
* FunPika
* fomafix
* Gabriel Wicke
* Gero Scholz
* Gilles van den Hoven
* Grunny
* Harry Burt
* Ireas
* Jacob Block
* Jan Gerber
* Jan Luca Naumann
* Jaska Zedlik
* Jeremy Baron
* Jidanni
* Jimmy Xu
* Jonathan Wiltshire
* John N
* JuneHyeon Bae
* Jure Kajzer
* Karun Dambiec
* Katie Filbert
* Kevin Israel
* Kim Hyun-Joon
* Lee Worden
* Lejonel
* liangent
* Louperivois
* Lucas Garczewski
* Luigi Corsaro
* Lupo
* Madman
* Manuel Menal
* Marcin Cieślak
* Marcus Buck
* Marc-André Pelletier
* Mark Hershberger
* Mark Holmquist
* Marooned
* Mathias Ertl
* Matthias Mullie
* Matthew Britton
* mati
* Max
* Max Sikström
* merl
* Michael Dale
* Michael De La Rue
* Michael M.
* Michael Newton
* Michael Walsh
* Mike Horvath
* Mormegil
* moejoe0000
* MrBlueSky
* MrPete
* MZMcBride
* mybugs.mail
* Nakon
* Nathan Larson
* nephele
* Nik
* Nx.devnull
* Nikola Kovacs
* Nikolaos S. Karastathis
* Nischay Nahata
* Olaf Lenz
* Olivier Finlay Beaton
* Patricio Molina
* Paul Copperman
* Paul Oranje
* Peter Gehres
* Petr Onderka
* PieRRoMaN
* quietust
* René Kijewski
* rgcjonas
* Rob Moen
* Robert Treat
* RockMFR
* Russell Blau
* Rusty Burchfield
* S Page
* Salvatore Ingala
* Santhosh Thottingal
* Scott Colcord
* se4598
* Sébastien Santoro
* Simon Walker
* Solitarius
* Søren Løvborg
* Srikanth Lakshmanan
* Stefano Codari
* Str4nd
* Subramanya Sastry
* svip
* The Evil IP address
* Tim Landscheidt
* Tisane
* Tyler Anthony Romeo
* Umherirrender
* Van de Bugger
* Ville Stadista
* Vitaliy Filippov
* Waldir Pimenta
* William Demchick
* Yusuke Matsubara
* Yuvaraj Pandian T
* Zachary Hauri
== Translators ==
* [https://translatewiki.net/wiki/Translating:MediaWiki/Credits Translators on translatewiki.net and others]

2
sources/mediawiki/FAQ Normal file
View file

@ -0,0 +1,2 @@
The MediaWiki FAQ can be found at:
http://www.mediawiki.org/wiki/Manual:FAQ

10914
sources/mediawiki/HISTORY Normal file

File diff suppressed because it is too large Load diff

89
sources/mediawiki/INSTALL Normal file
View file

@ -0,0 +1,89 @@
---
Installing MediaWiki
---
Starting with MediaWiki 1.2.0, it's possible to install and configure the wiki
"in-place", as long as you have the necessary prerequisites available.
Required software:
* Web server with PHP 5.3.2 or higher.
* A SQL server, the following types are supported
** MySQL 5.0.2 or higher
** PostgreSQL 8.3 or higher
** SQLite 3.3.7 or higher
** Oracle 9.0.1 or higher
MediaWiki is developed and tested mainly on Unix/Linux platforms, but should
work on Windows as well.
If your PHP is configured as a CGI plug-in rather than an Apache module you may
experience problems, as this configuration is not well tested. safe_mode is also
not tested and unlikely to work.
Support for rendering mathematical formulas requires installing the Math extension,
see http://www.mediawiki.org/wiki/Extension:Math
Don't forget to check the RELEASE-NOTES file...
Additional documentation is available online, which may include more detailed
notes on particular operating systems and workarounds for difficult hosting
environments:
http://www.mediawiki.org/wiki/Manual:Installation_guide
******************* WARNING *******************
REMEMBER: ALWAYS BACK UP YOUR DATABASE BEFORE
ATTEMPTING TO INSTALL OR UPGRADE!!!
******************* WARNING *******************
----
In-place web install
----
Decompress the MediaWiki installation archive either on your server, or on your
local machine and upload the directory tree. Rename it from "mediawiki-1.x.x" to
something nice, like "wiki", since it will be appearing in your URL,
ie. /wiki/index.php/Article.
+--------------------------------------------------------------------------+
| Note: If you plan to use a fancy URL-rewriting scheme to prettify your |
| URLs, such as http://www.example.com/wiki/Article, you should put the |
| files in a *different* directory from the virtual path where page names |
| will appear. It is common in this case to use w as the folder name and |
| /wiki/ as the virtual article path where your articles pretend to be. |
| |
| See: http://www.mediawiki.org/wiki/Manual:Short_URL |
+--------------------------------------------------------------------------+
Hop into your browser and surf into the wiki directory. It'll direct you into
the config script. Fill out the form... remember you're probably not on an
encrypted connection.
Gaaah! :)
If all goes well, you should soon be told that it's set up your wiki database
and generated a configuration file. There is now a copy of "LocalSettings.php"
available to download from the installer. Download this now, there is not a
way (yet) to get it after you exit the installer. Place it in the main wiki
directory, and the wiki should now be working.
Once the wiki is set up, you should remove the mw-config directory (though it will
refuse to config again if the wiki is set up).
----
Don't forget that this is free software under development! Chances are good
there's a crucial step that hasn't made it into the documentation. You should
probably sign up for the MediaWiki developers' mailing list; you can ask for
help (please provide enough information to work with, and preferably be aware of
what you're doing!) and keep track of major changes to the software, including
performance improvements and security patches.
http://lists.wikimedia.org/mailman/listinfo/mediawiki-announce (low traffic)
http://lists.wikimedia.org/mailman/listinfo/mediawiki-l (site admin support)
http://lists.wikimedia.org/mailman/listinfo/wikitech-l (development)

33
sources/mediawiki/README Normal file
View file

@ -0,0 +1,33 @@
== MediaWiki ==
MediaWiki is a popular and free, open-source wiki software package written in
PHP. It serves as the platform for Wikipedia and the other projects of the Wikimedia
Foundation, which deliver content in over 280 languages to more than half a billion
people each month. MediaWiki's reliability and robust feature set have earned it a
large and vibrant community of third-party users and developers.
MediaWiki is:
* feature-rich and extensible, both on-wiki and with over 2,000 extensions;
* scalable and suitable for both small and large sites;
* available in your language; and
* simple to install, working on most hardware/software combinations.
For system requirements, installation, and upgrade details, see the files
RELEASE-NOTES, INSTALL, and UPGRADE.
* Ready to get started?
** https://www.mediawiki.org/wiki/Download
* Looking for the technical manual?
** https://www.mediawiki.org/wiki/Manual:Contents
* Seeking help from a person?
** https://www.mediawiki.org/wiki/Communication
* Looking to file a bug report or a feature request?
** https://bugs.mediawiki.org/
* Interested in helping out?
** https://www.mediawiki.org/wiki/How_to_contribute
MediaWiki is the result of global collaboration and cooperation. The CREDITS
file lists technical contributors to the project. The COPYING file explains
MediaWiki's copyright and license (GNU General Public License, version 2 or
later). Many thanks to the Wikimedia community for testing and suggestions.

View file

@ -0,0 +1 @@
README

View file

@ -0,0 +1,721 @@
= MediaWiki release notes =
Security reminder: MediaWiki does not require PHP's register_globals. If you
have it on, turn it '''off''' if you can.
== MediaWiki 1.22.6 ==
This is a security release of the MediaWiki 1.22 branch.
=== Changes since 1.22.5 ===
* (bug 63251) SECURITY: Escape sortKey in pageInfo.
== MediaWiki 1.22.5 ==
This is a security and maintenance release of the MediaWiki 1.22 branch.
=== Changes since 1.22.4 ===
* (bug 62497) SECURITY: Add CSRF token on Special:ChangePassword.
* (bug 62467) Set a title for the context during import on the cli.
* Fix custom local MediaWiki:Helppage values.
* mediawiki.js: Fix documentation breakage.
* (bug 58153) Make MySQLi work with non standard port.
* (bug 53887) Reintroduced a link to help pages in the default sidebar, that
any sysop can customize by editing [[MediaWiki:Sidebar]] locally. The link
now points to a mediawiki.org page which is guaranteed to exist. Nothing needs
to be done on your end, but remember to adjust [[MediaWiki:Sidebar]] for the
needs of your wikis. Everyone can help with the shared documentation by
translating: https://www.mediawiki.org/wiki/Special:Translate/agg-Help_pages .
* (bug 53888) Corrected a regression in 1.22 which introduced red links on the
login page. If you previously installed 1.22.x and have created a local page
to make the red link blue, write its title as in [[MediaWiki:helplogin-url]]
if you didn't already. Otherwise, you don't need to do anything, but you can
translate the help page at https://www.mediawiki.org/wiki/Help:Logging_in .
== MediaWiki 1.22.4 ==
This is a maintenance release of the MediaWiki 1.22 branch.
=== Changes since 1.22.3 ===
* Use the correct branch of the extensions' git repositories.
== MediaWiki 1.22.3 ==
This is a security and bugfix release of the MediaWiki 1.22 branch.
=== Changes since 1.22.2 ===
* (bug 60771) SECURITY: Disallow uploading SVG files using non-whitelisted
namespaces. Also disallow iframe elements. User will get an error
including the namespace name if they use a non- whitelisted namespace.
* (bug 61346) SECURITY: Make token comparison use constant time. It seems like
our token comparison would be vulnerable to timing attacks. This will take
constant time.
* (bug 61362) SECURITY: API: Don't find links in the middle of api.php links.
* (bug 53710) Add sequence support for upsert in DatabaseOracle in the same way
as in selectInsert
* (bug 60231, 58719) Various fixes to job running code in Wiki.php: Make it
async on Windows. Fixed possible "invalid filename" errors on Windows.
Redirect output to dev/null to avoid hanging PHP.
* (bug 60083) Correct sequence name for fresh Postgres installation. Spotted
by gebhkla
* (bug 60531) Avoid variable naming conflicts in
DatabasePostgres::selectSQLText. Spotted by gebhkla
* (bug 60094) Fix rebuildall.php fatal error with PostgreSQL. The fix for
47055 introduced a fatal error when running rebuildall.php. This is a
workaround suggested by gebhkla on Bugzilla. It just checks to make sure
$options is actually an array before calling array_search on it.
* (bug 43817c12) Add error handling if descriptionmsg isn't defined for
extension.
* (bug 60543) Special:PrefixIndex omits stripprefix=1 for "Next page" link.
== MediaWiki 1.22.2 ==
This is a security and bugfix release of the MediaWiki 1.22 branch.
=== Changes since 1.22.1 ===
* (bug 60339) SECURITY: Sanitize shell arguments to DjVu files, and other media
formats.
* (bug 58253) Check for very old PCRE versions in installer and updater.
* (bug 60054) Make WikiPage::$mPreparedEdit public.
== MediaWiki 1.22.1 ==
This is a security and maintenance release of the MediaWiki 1.22 branch.
=== Changes since 1.22.0 ===
* (bug 57550) SECURITY: Disallow stylesheets in SVG Uploads
* (bug 58088) SECURITY: Don't normalize U+FF3C to \ in CSS Checks
* (bug 58472) SECURITY: Disallow -o-link in styles
* (bug 58553) SECURITY: Return error on invalid XML for SVG Uploads
* (bug 58699) SECURITY: Fix RevDel log entry information leaks
* (bug 58178) Restore compatibility with curl < 7.16.2.
* (bug 56931) Updated the plural rules to CLDR 24. They are in new format
which is detailed in UTS 35 Rev 33. The PHP parser and evaluator as well as
the JavaScript evaluator were updated to support the new format. Plural rules
for some languages have changed, most notably Russian. Affected software
messages have been updated and marked for review at translatewiki.net.
This change is backported from the development branch of MediaWiki 1.23.
* (bug 58434) The broken installer for database backend Oracle was fixed.
* (bug 58167) The web installer no longer throws an exception when PHP is
compiled without support for MySQL yet with support for another DBMS.
* (bug 58640) Fixed a compatibility issue with PCRE 8.34 that caused pages
to appear blank or with missing text.
* (bug 47055) Changed FOR UPDATE handling in Postgresql
* (bug 57026) Avoid extra parsing in prepareContentForEdit()
== MediaWiki 1.22.0 ==
MediaWiki 1.22.0 is the stable branch and is recommended for use in production.
MediaWiki 1.22.0 is a large release that contains many new features and bug fixes.
* Breaking Changes in 1.22.0
* New features in 1.22.0
* Configuration changes in 1.22.0
* Bug fixes in 1.22.0
* API changes in 1.22.0
* Languages updated in 1.22.0
* Other changes in 1.22.0
=== Breaking Changes in 1.22.0 ===
* BREAKING CHANGE: (bug 41729) Display editsection links next to headings. Also
change their class name from .editsection to .mw-editsection and place them at
the end of the heading element instead of the beginning. Client-side code and
screen-scrapers will have to be adjusted to handle both cases (old HTML will
still be visible on cached page renders until they are purged); extensions
using the DoEditSectionLink or EditSectionLink hooks might need adjustments as
well.
* (bug 55818) BREAKING CHANGE: Removed undocumented 'Debug' hook in wfDebug.
This resolves an infinite loop when using $wgDebugFunctionEntry = true.
* BREAKING CHANGE: action=parse no longer returns all langlinks for the page
with prop=langlinks by default. The new effectivelanglinks parameter will
request that the LanguageLinks hook be called to determine the effective
language links.
* BREAKING CHANGE: list=allpages, list=langbacklinks, and prop=langlinks do not
apply the new LanguageLinks hook, and thus only consider language links
stored in the database.
* BREAKING CHANGE: Implementation of MediaWiki's JS and JSON value encoding
has changed:
** MediaWiki no longer supports PHP installations in which the native JSON
extension is missing or disabled.
** XmlJsCode objects can no longer be nested inside objects or arrays.
(For Xml::encodeJsCall(), this individually applies to each argument.)
** The sets of characters escaped by default, along with the precise escape
sequences used, have changed (except for the Xml::escapeJsString()
function, which is now deprecated).
* BREAKING CHANGE: The Services_JSON class has been removed. If necessary,
be sure to upgrade affected extensions at the same time (e.g. Collection).
* BREAKING CHANGE: Legacy skins Simple, MySkin, Chick, Standard and Nostalgia
were all removed. (Nostalgia was moved to an extension.) The SkinLegacy and
LegacyTemplate classes that supported them were removed as well and are now a
part of the Nostalgia extension.
* BREAKING CHANGE: The "ExternalAuth" authentication subsystem was removed, along
with its associated globals of $wgExternalAuthType, $wgExternalAuthConf,
$wgAutocreatePolicy and $wgAllowPrefChange. Affected users are encouraged to
use AuthPlugin for external authentication/authorization needs.
* BREAKING CHANGE: mw.util.tooltipAccessKeyRegexp: The match group for the
accesskey character is now $6 instead of $5.
* BREAKING CHANGE: meta keywords are no longer supported. A <meta name="keywords"
will no longer be output and OutputPage::addKeyword no longer exists.
* BREAKING CHANGE: The EditSectionLink hook was removed after being
deprecated since MediaWiki 1.14. Use DoEditSectionLink instead.
* (bug 50310) BREAKING CHANGE: wikibits: Drop support for mwCustomEditButtons.
It defaults to an empty array and emits mw.log.warn when accessed.
* BREAKING CHANGE: Special:Disambiguations has been removed from MediaWiki core.
Functions related to disambiguation pages are now handled by the Disambiguator
extension (https://www.mediawiki.org/wiki/Extension:Disambiguator) (bug
35981).
* BREAKING CHANGE: The 'mediawiki.legacy.wikiprintable' module has been removed.
The skins/common/wikiprintable.css file no longer exists. Return value of
Skin#commonPrintStylesheet is ignored. Please use the 'mediawiki.legacy.commonPrint'
module instead or base your skin on SkinTemplate.
* BREAKING CHANGE: The module 'mediawiki.legacy.IEFixes' has been removed as it was
unused. The file skins/common/IEFixes.js remains but is only used by wikibits.
The file never contained any re-usable components. To use it in a skin, load
'mediawiki.legacy.wikibits' (which IEFixes depends on) and that will import
IEFixes automatically if user agent conditions are met.
=== New features in 1.22.0 ===
* You can now install extensions using Composer.
See https://www.mediawiki.org/wiki/Composer
* (bug 44525) mediawiki.jqueryMsg can now parse (whitelisted) HTML elements and attributes.
* (bug 33454) Language::sprintfDate now has a timezone parameter, and supports
the "eIOPTZ" formatting characters.
* EditWarning: A warning is shown when an editor leaves the edit form without
saving (enabled by default, users can opt-out via the 'useeditwarning'
preference). This feature was moved from the Vector extension, and is now part
of core for all skins. Take care when upgrading that you don't use an older
version of the Vector extension as this feature may conflict.
* New 'mediawiki.ui' CSS module providing mw-ui-* styles for buttons and a
compact vertical form layout.
* HTMLForm supports a new display format 'vform' which applies this compact vertical
layout and button styling. Special:PasswordReset uses this format.
* New versions of login (Special:UserLogin) and create account
(Special:UserLogin/signup) forms using the "vform" compact vertical form layout.
These forms use new messages that assume a "Help logging in" link, see
https://www.mediawiki.org/wiki/Manual:Page_customizations;
https://www.mediawiki.org/wiki/Account_creation_user_experience/Strings lists the
message key changes.
* (bug 23343) Implemented ability to apply IP blocks to the contents of X-Forwarded-For headers
by adding a new configuration variable $wgApplyIpBlocksToXff (disabled by default).
* The new hook 'APIGetPossibleErrors' to modify the list of possible errors was
added.
* (bug 25592) LogEventsList::showLogExtract() will now ignore various
Pager-related WebRequest parameters by default, as this is overwhelmingly
likely to be what was intended by users of the method. If any caller wishes
to use these parameters, the new param 'useRequestParams' may be set to true.
* mw.util.addPortletLink: Tooltip is no longer required to be plain (without
an accesskey in it already). As such it now rountrips. Creating a link with a
message as tooltip, grabbing the title attribute and using it to create
another portlet will work as expected.
* (bug 6747) {{ROOTPAGENAME}} introduced, contains the name of the topmost
page without namespace.
* (bug 45535) introduced the new 'LanguageLinks' hook for manipulating the
language links associated with a page before display.
* Chosen (http://harvesthq.github.io/chosen/) was added as module 'jquery.chosen'
* HTMLForm will turn multiselect checkboxes into a Chosen interface when setting cssclass 'mw-chosen'
* rebuildLocalisationCache learned --lang option. Let you rebuild l10n caches
of the specified languages instead of all of them.
* New GetNewMessagesAlert hook allowing extensions to disable or modify the new
messages alert
* New wgUserNewMsgRevisionId JS global for logged in users. This will be null
if the user has no new talk page messages. Otherwise it will be set to the
revision ID of the oldest new talk page message. This will allow gadgets and
extensions to create their own new message alerts on the client side.
* mediawiki.log: Added log.warn wrapper (uses console.warn and console.trace).
* mediawiki.log: Implemented log.deprecate. This method defines a property and
uses ES5 getter/setter to emit a warning when they are used.
* $wgCascadingRestrictionLevels was added, allowing one to specify restriction levels
which can be cascading (previously 'sysop' was hard-coded as the only one).
* XHTML5 support has been improved. If you set $wgMimeType = 'application/xhtml+xml'
MediaWiki will try outputting markup acording to XHTML5 rules.
* Altered hook 'ProtectionForm::save', adding the reason page protection is
changed as third parameter.
* New hook 'TitleSquidURLs' for manipulating the list of URLs to be purged from
HTTP caches when a page is changed.
* Changed the patrolling system to always show the link for patrolling in case the
current revision is patrollable. This also removed the usage of the rcid URI parameters.
* Oracle DB backend now supports Database Resident Connection Pooling (DRCP).
Can be enabled by setting $wgDBOracleDRCP=true.
Requires Oracle DB 11gR1 or above, enabled DRCP inside the DB itself and a
propper connect string.
More about DRCP can be found at:
http://www.oracle-base.com/articles/11g/database-resident-connection-pool-11gr1.php
* Add a new parameter $patrolFooterShown to hook ArticleViewFooter so the hook
handlers can take further action based on the status of the patrol footer
* A new hook TitleQuickPermissions was added to allow overriding of quick
permissions in the Title class.
* LinkCache singleton can now be altered or cleared, letting one to specify
another instance that does not rely on a database backend.
* MediaWiki's PHPUnit tests can now use PHPUnit installed using composer --dev.
* (bug 43689) The lists of templates used on the page and hidden categories it
is a member of, shown below the edit form, are now collapsible (and collapsed
by default).
* Parser profiling data, formerly only available in the "NewPP limit report"
HTML comment, is now also displayed at the bottom of page previews.
* Added ParserLimitReportPrepare and ParserLimitReportFormat hooks, deprecated
ParserLimitReport hook.
* New user rights have been added to increase granularity in rights management
for extensions such as OAuth:
** editmyusercss controls whether a user may edit their own CSS subpages.
** editmyuserjs controls whether a user may edit their own JS subpages.
** viewmywatchlist controls whether a user may view their watchlist.
** editmywatchlist controls whether a user may edit their watchlist.
** viewmyprivateinfo controls whether a user may access their private
information (e.g. registered email address, real name).
** editmyprivateinfo controls whether a user may change their private
information.
** editmyoptions controls whether a user may change their preferences.
* Add new hook AbortTalkPageEmailNotification, this will be used to determine
whether to send the regular talk page email notification
* Action classes registered in $wgActions are now also supported in the form of
a callback (which returns an instance of Action) instead of providing the name
of a subclass of Action.
* (bug 46513) Vector: Add the collapsibleTabs script from the Vector extension.
* Added $wgRecentChangesFlags for defining new flags for RecentChanges and
watchlists.
* (bug 40518) mw.toolbar: Implemented mw.toolbar.addButtons for adding multiple
button objects in one call.
* Rights used for the default protection levels ('sysop' and 'autoconfirmed')
are now used just for that purpose, instead of overloading other rights. This
allows easy granting of the ability to edit sysop-protected pages without
also granting the ability to protect and unprotect.
* (bug 48256) Make brackets in section edit links accessible to CSS.
They are now wrapped in <span class="mw-editsection-bracket" />.
* (bug 8480) Allow handler specific parameters in galleries (like page number)
* jquery.client: Add detection for Opera 15 and Internet Explorer 11.
* Change tags (used by the AbuseFilter extension) are now shown on diff pages.
* Change tag lists (shown on recent changes, watchlist, user contributions,
history pages, diff pages) now include a link to Special:Tags to distinguish
them from edit summaries.
* Added a new method and hook, User::isEveryoneAllowed() and
UserIsEveryoneAllowed, for use in situations where a "does everyone have this
right?" check is used to avoid more expensive checks.
* (bug 14431) Display "(No difference)" instead of an empty diff (when comparing
revisions in the history or when previewing changes while editing).
* New hook 'IsUploadAllowedFromUrl' is added which can be used to intercept uploads by
URL, useful for blacklisting specific URLs
* (bug 21912) Watchlist token implementation has been refactored and
Special:ResetTokens was added to allow users to reset their tokens
instead of presenting them in Preferences.
* Special:PrefixIndex now lets you strip the searched prefix from the displayed
titles. Given a list of articles named Bug1, Bug2, you can now transclude the
list of bug numbers using: {{Special:PrefixIndex/Bug|stripprefix=1}}.
The special page form received a new checkbox matching that option.
* (bug 23580) Implement javascript callback interface "mw.hook".
* (bug 30713) New mw.hook "wikipage.content".
* (bug 40430) jquery.placeholder gets a new parameter to set the attribute value
to be used.
* $wgHTCPMulticastRouting renamed $wgHTCPRouting since it accepts unicast.
* $wgHTCPRouting rules can now be passed an array of hosts/ports to send purge
too. Can be used whenever several multicast group could be interested by a
specific purge.
* (bug 25931) Add Special:RandomInCategory.
* mediawiki.util: addPortletLink now supports passing a jQuery object as nextnode.
* <wbr> can now be used inside WikiText.
* WebResponse::setcookie is much more featureful. Callers using PHP's
setcookie() or setrawcookie() should begin using this instead.
* New hook WebResponseSetCookie, called from WebResponse::setcookie().
* New hook ResetSessionID, called when the session id is reset.
* Add a mode parameter to <gallery> tag with potential options of "traditional",
"nolines", "packed", "packed-overlay", or "packed-hover".
* (bug 47399) A success message is now displayed after changing the password.
* Make thumb.php give HTTP redirects for file redirects
* (bug 30607) Special:ListFiles can now show old versions of files. Additionally
Special:AllMyUploads was introduced so the user can get a list of all things
they have ever uploaded, even if it was subsequently overriden.
* Introduced Special:MyFiles and Special:AllMyFiles as an alias for Special:MyUploads
and Special:AllMyUploads respectively.
* IPv6 addresses in X-Forwarded-For headers are now normalised before checking
against allowed proxy lists.
* Add deferrable update support for callback/closure.
* Add TitleMove hook before page renames.
* Revision deletion backend code is moved out of SpecialRevisiondelete
* Added {{REVISIONSIZE}} variable to get the current size of a revision.
* Add support for the LESS stylesheet language to ResourceLoader. LESS is a
stylesheet language that compiles into CSS. ResourceLoader file modules may
include LESS style files; ResourceLoader will compile these files into CSS
before sending them to the client.
** The $wgResourceLoaderLESSVars configuration variable is an associative array
mapping variable names to string CSS values. These variables are considered
declared for all LESS files. Additional variables may be registered by
adding keys to the array.
** $wgResourceLoaderLESSFunctions is an associative array of custom LESS
function names to PHP callables. See <http://leafo.net/lessphp/docs/#custom_functions>
for more details regarding custom functions.
** $wgResourceLoaderLESSImportPaths is an array of file system paths. Files
referenced in LESS '@import' statements are looked up here first.
* ResourceLoader supports hashes as module cache invalidation trigger (instead
of or in addition to timestamps).
* Added $wgExtensionEntryPointListFiles for use in mergeMessageFileList.php.
* Added a hook, APIQuerySiteInfoStatisticsInfo, to allow extensions to modify
the output of the API query meta=siteinfo&siprop=statistics
* Primary keys have been added to both the archive table and the externallinks
tables.
* Added $wgEnableParserLimitReporting to control whether the NewPP limit report is
output in a HTML comment.
* The 'UnwatchArticle' and 'WatchArticle' hooks now support a Status object
instead of just a boolean return value to abort the hook.
* Added a hook, SpecialWatchlistGetNonRevisionTypes, to allow extensions
with custom recentchanges entries to hook into the Watchlist without
clobbering each other.
* A hidden, empty input field was added to the edit form, and any edit that fills
it in will be rejected. This prevents against the simplest form of spambots.
Previously in the "SimpleAntiSpam" extension by Ryan Schmidt.
* populateRevisionLength.php maintenance script updated to also populate
archive.ar_len field.
* (bug 43571) DatabaseMySQLBase learned to list views, optionally filtered by a
prefix. Also fixed PHPUnit test suite when using a MySQL backend containing
views.
=== Configuration changes in 1.22.0 ===
* $wgRedirectScript was removed. It was unused.
* Removed $wgLocalMessageCacheSerialized, it is now always true.
* $wgVectorUseIconWatch is now enabled by default.
* $wgCascadingRestrictionLevels was added.
* ftps, ssh, sftp, xmpp, sip, sips, tel, sms, bitcoin, magnet, urn, and geo
have been whitelisted inside of $wgUrlProtocols.
* $wgDocType and $wgDTD have been removed and are no longer used for the DOCTYPE.
* $wgHtml5 is no longer used by core. Setting it to false will no longer disable HTML5.
It is still set to true for extension compatibility but doing so in extensions is deprecated.
* $wgXhtmlDefaultNamespace is no longer used by core. Setting it will no longer change the
xmlns used by MediaWiki. Reliance on this variable by extensions is deprecated.
* $wgHandheldStyle was removed.
* $wgHandheldForIPhone was removed.
* $wgJsMimeType is no longer used by core. Most usage has been removed since
HTML output is now exclusively HTML5.
* $wgDBOracleDRCP added. True enables persistent connection with DRCP on Oracle.
* $wgLogAutopatrol added to allow disabling logging of autopatrol edits in the logging table.
default for $wgLogAutopatrol is true.
* The 'edit' right no longer allows for editing a user's own CSS and JS.
* New rights 'editmyusercss', 'editmyuserjs', 'viewmywatchlist',
'editmywatchlist', 'viewmyprivateinfo', 'editmyprivateinfo', and
'editmyoptions' restrict actions that were formerly allowed by default. They
have been added to the default for $wgGroupPermissions['*'].
* The 'editprotected' right no longer allows bypassing of all page protection
restrictions. Any group using it for this purpose will now need to have all
the individual rights listed in $wgRestrictionTypes for the same effect.
* The 'protect' and 'autoconfirmed' rights are no longer used for the default
page protection levels. The rights 'editprotected' and 'editsemiprotected'
are now used for this purpose instead.
* (bug 40866) wgOldChangeTagsIndex removed.
* $wgNoFollowDomainExceptions now only matches entire domains. For example,
an entry for 'bar.com' will still match 'foo.bar.com' but not 'foobar.com'.
* $wgCopyUploadTimeout and $wgCopyUploadAsyncTimeout added to change the timeout times for
fetching the file during upload by url.
* New key added to $wgGalleryOptions - $wgGalleryOptions['mode'] to set
default gallery mode.
* New hook 'GalleryGetModes' to allow extensions to make new gallery modes.
* The checkbox for staying in HTTPS displayed on the login form when $wgSecureLogin is
enabled has been removed. Instead, whether the user stays in HTTPS will be determined
based on the user's preferences, and whether they came from HTTPS or not.
* $wgRC2UDPAddress, $wgRC2UDPInterwikiPrefix, $wgRC2UDPOmitBots, $wgRC2UDPPort,
and $wgRC2UDPPrefix configuration options have been deprecated in favor of a
$wgRCFeeds configuration array. $wgRCFeeds makes both the format and
destination of recent change notifications customizable, and allows for
multiple destinations to be specified.
* (bug 53862) portal-url, currentevents-url and helppage have been removed from the
default Sidebar.
* The 'vector-simplesearch' preference is now enabled by default. Previously
it was only enabled if the Vector extension was installed.
* The precise format of metric datagrams produced by the UDP profiler and stats counter
may now be specified as $wgUDPProfilerFormatString and $wgStatsFormatString,
respectively.
* (bug 54597) $wgBlockOpenProxies, $wgProxyPorts, $wgProxyScriptPath, and
$wgProxyMemcExpiry have been removed, along with the open proxy scanner
script they were added for.
* Default value of $wgMaxShellMemory has been tripled (it's now 300 MB).
=== Bug fixes in 1.22.0 ===
* (bug 47271) $wgContentHandlerUseDB should be set to false during the upgrade
* Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
could still navigate to the page by entering the URL directly.
* (bug 47138) Fixed a fatal error when a blocked user tries to automatically
create an account on login due external authentication in some circumstances.
* (bug 23393) HTML <hN> headings containing line breaks are now handled
correctly.
* (bug 45803) Whitespace within == Headline == syntax and within <hN> headings
is now non-significant and not preserved in the HTML output.
* (bug 47218) Special:BlockList now handles correctly user names with spaces
when passed as subpage.
* Pager's properly validate which fields are allowed to be sorted on.
* mw.util.tooltipAccessKeyRegexp: The regex now matches "option-" as well.
Support for Mac "option" was added in 1.16, but the regex was never updated.
* (bug 46768) Usernames of blocking users now display correctly, even if numeric.
* (bug 39590) Self-transclusions now show the most up to date result always
after save instead of being a revision behind.
* A bias in wfRandomString() toward digits 1-7 has been corrected. Generated
strings will now start with digits 0 and 8-f as often as they should.
* (bug 45371) Removed Parser_LinkHooks and CoreLinkFunctions classes.
* (bug 41545) Allow <kbd>, <samp>, and <var> to be nested like allowed in html.
* PLURAL magic word no longer causes a PHP notice when no matching form exists.
* (bug 36641) Patrol page links no longer show on non-existent revisions.
* (bug 35810) Pages not linked from Special:RecentChanges or Special:NewPages
are patrollable now.
* (bug 30213) JavaScript for search suggestions is now disabled when the API
is disabled, and AJAX patrolling and watching are now disabled when use of
the write API is not allowed.
* (bug 48294) API: Fix chunk upload async mode.
* (bug 46749) Broken files tracking category removed from pages if an image
with that name is uploaded.
* (bug 14176) System messages that are empty were previously incorrectly treated
as non-existent, causing a fallback to the default. This stopped users from
overriding system messages to make them blank.
* (bug 48319) action=parse no longer returns an error if passed none of 'oldid',
'pageid', 'page', 'title', and 'text' (e.g. if only passed 'summary'). A
warning will instead be issued if 'title' is non-default, unless no props are
requested.
* Special:Recentchangeslinked will now include upload log entries
* (bug 41281) Fixed ugly output if file size could not be extracted for multi-page media.
* (bug 50315) list=logevents API module will now output log entries by anonymous users.
* (bug 38911) Handle headers with rowspan in jquery.tablesorter
* (bug 658) Converted the table of contents on wiki pages from <table> to <div>
and adjusted skin CSS accordingly. The CSS was carefully crafted to be
backwards-compatible in all reasonable cases (uses of the __TOC__ magic word,
the #toc CSS id and the .toc CSS class). However, particularly bad abuse of
the id or the class can possibly break.
* CSSJanus now supports rgb, hsl, rgba, and hsla color syntaxes.
* Special:Listfiles can no longer be sorted by image name when filtering
by user in miser mode.
* (bug 49074) CSSJanus: Handle values of border-radius correctly.
* Handle relative inclusions ({{../name}}) in main namespace with subpages
enabled correctly (previously MediaWiki tried to include Template:Parent/name
instead of just Parent/name).
* Added $wgAPIUselessQueryPages to allow extensions to flag their query pages
for non-inclusion in ApiQueryQueryPages.
* (bug 50870) mediawiki.notification: Notification area should remain visible
when scrolled down.
* (bug 13438) Special:MIMESearch no longer an expensive special page.
* (bug 48342) Fixed a fatal error when $wgValidateAllHtml is set to true and
the function apache_request_headers() function is not available.
* (bug 33399) LivePreview: Re-run wikipage content handlers
(jquery.makeCollapsible, jquery.tablesorter) after preview content is loaded.
* (bug 51891) Fixed PHP notice on Special:PagesWithProp when no properties
are defined.
* (bug 52006) Corrected documentation of $wgTranscludeCacheExpiry.
* (bug 52077) The APIEditBeforeSave hook is giving the content of the whole
revision as second argument now, rather than just the current section.
* (bug 49694) $wgSpamRegex is now also applied on the new section headline text
adding a new topic on a page
* (bug 41756) Improve treatment of multiple comments on a blank line.
* (bug 51064) Purge upstream caches when deleting file assets.
* (bug 39012) File types with a mime that we do not know the extension for
can no longer be uploaded as an extension that we do know the mime type
for.
* (bug 51742) Add data-sort-value for better sorting of hitcounts Special:Tags
* (bug 26811) On DB error pages, server hostnames are now hidden when both
$wgShowHostnames and $wgShowSQLErrors are false.
* (bug 6200) line breaks in <blockquote> are handled like they are in <div>
* (bug 14931) Default character set now set to 'utf8' when a new MySQL
database is created.
* (bug 47191) Fixed "Column 'si_title' cannot be part of FULLTEXT index"
MySQL error when installing using the binary character set option.
* (bug 45288) Support mysqli PHP extension
* (bug 56707) Correct tooltip of "Next n results" on query special pages.
* (bug 56770) mw.util.addPortletLink: Check length before access array index.
=== API changes in 1.22.0 ===
* (bug 25553) The JSON output formatter now leaves forward slashes unescaped
to improve human readability of URLs and similar strings. Also, a "utf8"
option is now provided to use UTF-8 encoding instead of hex escape codes
for most non-ASCII characters.
* (bug 46626) xmldoublequote parameter was removed. Because of a bug, the
parameter has had no effect since MediaWiki 1.16, and so its removal is
unlikely to impact existing clients.
* (bug 47216) action=query&meta=siteinfo&siprop=skins will now indicate which
skin is the default and which are unusable (e.g. listed in $wgSkipSkins).
* (bug 25325) Added support for wlshow filtering (bots/anon/minor/patrolled)
to action=feedwatchlist.
* WDDX formatted output will actually be formatted (and normal output will no
longer be), and will no longer choke on booleans.
* action=opensearch no longer silently ignores the format parameter.
* action=opensearch now supports format=jsonfm.
* list=usercontribs&ucprop=ids will now include the parent revision id.
* (bug 47219) Allow specifying change type of Wikipedia feed items
* prop=imageinfo now allows setting iiurlheight without setting iiurlwidth
* prop=info now adds the content model and page language of the title.
* New upload log entries will now contain information on the relevant
image (sha1 and timestamp).
* (bug 49239) action=parse now can parse in preview and section preview modes.
* (bug 49259) action=patrol now accepts revision ids.
* (bug 48129) list=blocks&bkip= now correctly handles IPv6 CIDR ranges and
honors $wgBlockCIDRLimit. Note any clients passing invalid values to bkip
will now receive an error, rather than the previous behavior listing all
user blocks.
* (bug 48201) action=parse&text=foo now assumes wikitext if no title is given,
rather than using the content model of the page "API".
* action=watch no longer silently ignores hook abort.
* (bug 50785) action=purge with forcelinkupdate=1 no longer queues refreshLinks
jobs in the job queue for link table updates of pages that use the given page
as a template. Instead, forcerecursivelinkupdate=1 is introduced and should
be used if that behaviour is desirable.
* The 'debugLog' property (enabled by $wgDebugToolbar) no longer sets the log
entry values through ApiResult::content but directly. This changes the JSON
output from an array of objects with content in '*' to an array of strings
with the content.
* (bug 51342) prop=imageinfo iicontinue now contains the dbkey, not the text
version of the title.
* (bug 52538) action=edit will now use empty text instead of the contents
of section 0 when passed prependtext or appendtext with section=new.
* Support for the 'gettoken' parameter to action=block and action=unblock,
deprecated since 1.20, has been removed.
* (bug 49090) Token-getting functions will fail when using jsonp callbacks.
* (bug 52699) action=upload returns normalized file name on warning
"exists-normalized" instead of filename to be uploaded to.
* (bug 53884) action=edit will now return an error when the specified section
does not exist in the page.
* Added meta=filerepoinfo API module for getting information about foreign
file repositories, and related ForeignAPIRepo methods getInfo and getApiUrl.
* The new query module list=allfileusages to enumerate file usages was added.
=== Languages updated in 1.22.0 ===
MediaWiki supports over 350 languages. Many localisations are updated
regularly. Below only new and removed languages are listed, as well as
changes to languages because of Bugzilla reports.
* (bug 47099) Plural rules were updated to those from CLDR 24 for Manx (gv).
* (bug 54514) Explicit plural forms now work for Russian.
* (bug 46422) Explicit plural forms for languages that use a custom
implementation for Language::convertPlural now work correctly.
* Batak Toba (bbc-latn) added.
* (bug 46751) Made Buryat (Russia) (буряад) (bxr) fallback to Russian.
=== Other changes in 1.22.0 ===
* redirect.php was removed. It was unused.
* ClickTracking integration was dropped from the mediaWiki.user.bucket
JavaScript function. The 'tracked' option is now ignored.
* Event namespace used by jquery.makeCollapsible has been changed from
'mw-collapse' to 'mw-collapsible' for consistency with the module name.
* The Quickbar feature of the legacy skin model and the last remnants of it
throughout the code base have been removed.
* Externaledit/externaldiff preference was removed. Very few users used this
feature, and improper configuration can actually prevent a user from editing
* Calling Linker methods using a skin will now output deprecation warnings.
* (bug 46680) "Return to" links are no longer tagged with rel="next".
* HipHop compiler (hphpc) support was removed. HipHop VM support (hhvm) was
added.
* A new Special:Redirect page was added, providing lookup by revision ID,
user ID, or file name. The old Special:Filepath page was reimplemented
to redirect through Special:Redirect.
* Monobook: Removed the old conditional stylesheets for Opera 6, 7 and 9.
* Support for XHTML 1.0 has been removed. MediaWiki now only outputs (X)HTML5.
* wikibits: User-agent related globals have been deprecated. The following
properties now default to false and emit mw.log.warn: is_gecko, is_chrome_mac,
is_chrome, webkit_version, is_safari_win, is_safari, webkit_match, is_ff2,
ff2_bugs, is_ff2_win, is_ff2_x11, opera95_bugs, opera7_bugs, opera6_bugs,
is_opera_95, is_opera_preseven, is_opera, and ie6_bugs.
* (bug 48276) MediaWiki will now flash a confirmation message upon successfully
editing a page.
* (bug 40785) mediawiki.legacy.ajax has been marked as deprecated. The following
properties now emit mw.log.warn when accessed: sajax_debug, sajax_init_object,
sajax_do_call and wfSupportsAjax.
* Methods Title::userCanEditCssSubpage and Title::userCanEditJsSubpage,
deprecated since 1.19, have been removed.
* (bug 50134) Hook functions are no longer required to return a value. When a
hook function does not return a value (or when it returns an explicit null),
processing continues. To abort the hook, a hook function must return an
explicit, boolean false or a string error message. Other falsey values are
tantamount to a 'return true' in earlier versions of MediaWiki.
* (bug 48256) The 'editsection-brackets' optional message was removed.
Section edit links' brackets can now be customized using CSS by
styling span.mw-editsection-bracket.
* The usePatrol function in ChangesList has been marked as deprecated.
* (bug 50785) A "null edit", that is, a save action in which no changes to the
page text are made and no revision recorded, will no longer send refreshLinks
jobs to the job table to update pages which use the edited page as a template.
* The LivePreviewPrepare and LivePreviewDone events triggered on "jQuery( mw )"
have been deprecated in favour of using mw.hook.
* The 'showjumplinks' user preference has been removed, jump links are now
always included.
* Methods RecentChange::notifyRC2UDP, RecentChange::sendToUDP, and
RecentChange::cleanupForIRC have been deprecated, as it is now the
responsibility of classes implementing the RCFeedFormatter and RCFeedEngine
interfaces to implement the formatting and delivery for recent change
notifications.
* SpecialPrefixindex methods namespacePrefixForm() and showPrefixChunk() have
been made protected. They were accepting form variance arguments, this is now
using properties in the SpecialPrefixindex class.
* (bug 49629) The hook ExtractThumbParamaters has been deprecated in favour
of media handler overriding MediaHandler::parseParamString.
* (bug 46512) The collapsibleNav feature from the Vector extension has been moved
to the Vector skin in core.
* SpecialRecentChanges::addRecentChangesJS() function has been renamed
to addModules() and made protected.
* Methods WatchAction::doWatch and WatchAction::doUnwatch now return a Status
object instead of a boolean.
* Information boxes (CSS classes errorbox, warningbox, successbox) have been
made more subtle.
* Code specific to the Math extension was marked as deprecated.
* mediawiki.util: mw.util.wikiGetlink has been renamed to getUrl. (The old name
still works, but is deprecated.)
== Compatibility ==
MediaWiki 1.22.0 requires PHP 5.3.2 or later.
MySQL is the recommended DBMS. PostgreSQL or SQLite can also be used, but
support for them is somewhat less mature. There is experimental support for
Oracle.
The supported versions are:
* MySQL 5.0.2 or later
* PostgreSQL 8.3 or later
* SQLite 3.3.7 or later
* Oracle 9.0.1 or later
== Upgrading ==
1.22.0 has several database changes since 1.21, and will not work without schema
updates. Note that due to changes to some very large tables like the revision
table, the schema update may take quite long (minutes on a medium sized site,
many hours on a large site).
If upgrading from before 1.11, and you are using a wiki as a commons
repository, make sure that it is updated as well. Otherwise, errors may arise
due to database schema changes.
If upgrading from before 1.7, you may want to run refreshLinks.php to ensure
new database fields are filled with data.
If you are upgrading from MediaWiki 1.4.x or earlier, you should upgrade to
1.5 first. The upgrade script maintenance/upgrade1_5.php has been removed
with MediaWiki 1.21.
Don't forget to always back up your database before upgrading!
See the file UPGRADE for more detailed upgrade instructions.
For notes on 1.21.x and older releases, see HISTORY.
== Online documentation ==
Documentation for both end-users and site administrators is available on
MediaWiki.org, and is covered under the GNU Free Documentation License (except
for pages that explicitly state that their contents are in the public domain):
https://www.mediawiki.org/wiki/Documentation
== Mailing list ==
A mailing list is available for MediaWiki user support and discussion:
https://lists.wikimedia.org/mailman/listinfo/mediawiki-l
A low-traffic announcements-only list is also available:
https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce
It's highly recommended that you sign up for one of these lists if you're
going to run a public MediaWiki, so you can be notified of security fixes.
== IRC help ==
There's usually someone online in #mediawiki on irc.freenode.net.

View file

@ -0,0 +1,17 @@
<?php
/**
* To use a profiler, copy this file to StartProfiler.php,
* and add something like this:
*
* $wgProfiler['class'] = 'Profiler';
*
* Or for a sampling profiler:
* if ( !mt_rand( 0, 100 ) ) {
* $wgProfiler['class'] = 'Profiler';
* } else {
* $wgProfiler['class'] = 'ProfilerStub';
* }
*
* Configuration of the profiler output can be done in LocalSettings.php
*/

298
sources/mediawiki/UPGRADE Normal file
View file

@ -0,0 +1,298 @@
This file provides an overview of the MediaWiki upgrade process. For help with
specific problems, check
* the documentation at http://www.mediawiki.org
* the mediawiki-l mailing list archive at
http://lists.wikimedia.org/pipermail/mediawiki-l/
* the bug tracker at https://bugzilla.wikimedia.org
for information and workarounds to common issues.
== Overview ==
Comprehensive documentation on upgrading to the latest version of the software
is available at http://www.mediawiki.org/wiki/Manual:Upgrading.
=== Consult the release notes ===
Before doing anything, stop and consult the release notes supplied with the new
version of the software. These detail bug fixes, new features and functionality,
and any particular points that may need to be noted during the upgrade
procedure.
=== Backup first ===
It is imperative that, prior to attempting an upgrade of the database schema,
you take a complete backup of your wiki database and files and verify it. While
the upgrade scripts are somewhat robust, there is no guarantee that things will
not fail, leaving the database in an inconsistent state.
http://www.mediawiki.org/wiki/Manual:Backing_up_a_wiki provides an overview of
the backup process. You should also refer to the documentation for your
database management system for information on backing up a database, and to
your operating system documentation for information on making copies of files.
=== Perform the file upgrade ===
Download the files for the new version of the software. These are available
as a compressed "tar" archive from the Wikimedia Download Service
(http://download.wikimedia.org/mediawiki).
You can also obtain the new files directly from our Git source code
repository.
Replace the existing MediaWiki files with the new. You should preserve the
LocalSettings.php file and the "extensions" and "images" directories.
Depending upon your configuration, you may also need to preserve additional
directories, including a custom upload directory ($wgUploadDirectory),
deleted file archives, and any custom skins.
=== Perform the database upgrade ===
As of 1.21, it is possible to separate schema changes (i.e. adding,
dropping, or changing tables, fields, or indices) from all other
database changes (e.g. populating fields). If you need this
capability, see "From the command line" below.
==== From the web ====
If you browse to the web-based installation script (usually at
/mw-config/index.php) from your wiki installation you can follow the script and
upgrade your database in place.
==== From the command line ====
From the command line, browse to the "maintenance" directory and run the
update.php script to check and update the schema. This will insert missing
tables, update existing tables, and move data around as needed. In most cases,
this is successful and nothing further needs to be done.
If you need to separate out the schema changes so they can be run
by someone with more privileges, then you can use the --schema option
to produce a text file with the necessary commands. You can use
--schema, --noschema, $wgAllowSchemaUpdates as well as proper database
permissions to enforce this separation.
=== Check configuration settings ===
The names of configuration variables, and their default values and purposes,
can change between release branches, e.g. $wgDisableUploads in 1.4 is replaced
with $wgEnableUploads in later versions. When upgrading, consult the release
notes to check for configuration changes which would alter the expected
behavior of MediaWiki.
=== Check installed extensions ===
Extensions usually need to be upgraded at the same time as the MediaWiki core.
In MediaWiki 1.14 some extensions were migrated into the core. Please see the
HISTORY section "Migrated extensions" and disable these extensions in your
LocalSettings.php
=== Test ===
It makes sense to test your wiki immediately following any kind of maintenance
procedure, and especially after upgrading; check that page views and edits work
normally and that special pages continue to function, etc. and correct errors
and quirks which reveal themselves.
You should also test any extensions, and upgrade these if necessary.
== Upgrading from 1.16 or earlier ==
If you have a Chinese or Japanese wiki ($wgLanguageCode is set to one
of "zh", "ja", or "yue") and you are using MySQL fulltext search, you
will probably want to update the search index.
In the "maintenance" directory, run the updateDoubleWidthSearch.php
script. This will update the searchindex table for those pages that
contain double-byte latin characters.
== Upgrading from 1.8 or earlier ==
MediaWiki 1.9 and later no longer keep default localized message text
in the database; 'MediaWiki:'-namespace pages that do not exist in the
database are simply transparently filled-in on demand.
The upgrade process will delete any 'MediaWiki:' pages which are left
in the default state (last edited by 'MediaWiki default'). This may
take a few moments, similar to the old initial setup.
Note that the large number of deletions may cause older edits to expire
from the list on Special:Recentchanges, although the deletions themselves
will be hidden by default. (Click "show bot edits" to list them.)
See RELEASE-NOTES for more details about new and changed options.
== Upgrading from 1.7 or earlier ==
$wgDefaultUserOptions now contains all the defaults, not only overrides.
If you're setting this as a complete array(), you may need to change it
to set only specific items as recommended in DefaultSettings.php.
== Upgrading from 1.6 or earlier ==
$wgLocalTZoffset was in hours, it is now using minutes.
== Upgrading from 1.5 or earlier ==
Major changes have been made to the schema from 1.4.x. The updater
has not been fully tested for all conditions, and might well break.
On a large site, the schema update might take a long time. It might
explode, or leave your database half-done or otherwise badly hurting.
Among other changes, note that Latin-1 encoding (ISO-8859-1) is
no longer supported. Latin-1 wikis will need to be upgraded to
UTF-8; an experimental command-line upgrade helper script,
'upgrade1_5.php', can do this -- run it prior to 'update.php' or
the web upgrader.
NOTE that upgrade1_5.php does not work properly with recent version
of MediaWiki. If upgrading a 1.4.x wiki, you should upgrade to 1.5
first. upgrade1_5.php has been removed from MediaWiki 1.21.
If you absolutely cannot make the UTF-8 upgrade work, you can try
doing it by hand: dump your old database, convert the dump file
using iconv as described here:
http://portal.suse.com/sdb/en/2004/05/jbartsh_utf-8.html
and then reimport it. You can also convert filenames using convmv,
but note that the old directory hashes will no longer be valid,
so you will also have to move them to new destinations.
Message changes:
* A number of additional UI messages have been changed from HTML to
wikitext, and will need to be manually fixed if customized.
=== Configuration changes from 1.4.x: ===
$wgDisableUploads has been replaced with $wgEnableUploads.
$wgWhitelistAccount has been replaced by the 'createaccount' permission
key in $wgGroupPermissions. To emulate the old effect of setting:
$wgWhitelistAccount['user'] = 0;
set:
$wgGroupPermissions['*']['createaccount'] = false;
$wgWhitelistEdit has been replaced by the 'edit' permission key.
To emulate the old effect of setting:
$wgWhitelistEdit = true;
set:
$wgGroupPermissions['*']['edit'] = false;
If $wgWhitelistRead is set, you must also disable the 'read' permission
for it to take affect on anonymous users:
$wgWhitelistRead = array( "Main Page", "Special:Userlogin" );
$wgGroupPermissions['*']['read'] = false;
Note that you can disable/enable several other permissions by modifying
this configuration array in your LocalSettings.php; see DefaultSettings.php
for the complete default permission set.
If using Memcached, you must enabled it differently now:
$wgUseMemCached = true;
should be replaced with:
$wgMainCacheType = CACHE_MEMCACHED;
== Upgrading from 1.4.2 or earlier ==
1.4.3 has added new fields to the sitestats table. These fields are
optional and help to speed Special:Statistics on large sites. If you
choose not to run the database upgrades, everything will continue to
work in 1.4.3.
You can apply the update by running maintenance/update.php, or
manually run the SQL commands from this file:
maintenance/archives/patch-ss_total_articles.sql
== Upgrading from 1.4rc1 or earlier betas ==
The logging table has been altered from 1.4beta4 to 1.4beta5
and again in 1.4.0 final. Copy in the new files and use the web
installer to upgrade, or the command-line maintenance/update.php.
If you cannot use the automated installers/updaters, you may
update the table by manually running the SQL commands in these
files:
maintenance/archives/patch-log_params.sql
maintenance/archives/patch-logging-title.sql
== Upgrading from 1.3 or earlier ==
This should generally go smoothly.
If you keep your LocalSettings.php, you may need to change the style paths
to match the newly rearranged skin modules. Change these lines:
$wgStylePath = "$wgScriptPath/stylesheets";
$wgStyleDirectory = "$IP/stylesheets";
$wgLogo = "$wgStylePath/images/wiki.png";
to this:
$wgStylePath = "$wgScriptPath/skins";
$wgStyleDirectory = "$IP/skins";
$wgLogo = "$wgStylePath/common/images/wiki.png";
As well as new messages, the processing of some messages has changed.
If you have customized them, please compare the new format using
Special:Allmessages or the relevant LanguageXX.php files:
* copyrightwarning
* dberrortext
* editingcomment (was named commentedit)
* editingsection (was named sectionedit)
* numauthors
* numedits
* numtalkauthors
* numtalkedits
* numwatchers
* protectedarticle
* searchresulttext
* showhideminor
* unprotectedarticle
Note that the 1.3 beta releases included a potential vulnerability if PHP
is configured with register_globals on and the includes directory is
served to the web. For general safety, turn register_globals *off* if you
don't _really_ need it for another package.
If your hosting provider turns it on and you can't turn it off yourself,
send them a kind note explaining that it can expose their servers and their
customers to attacks.
== Upgrading from 1.2 or earlier ==
If you've been using the MediaWiki: namespace for custom page templates,
note that things are a little different. The Template: namespace has been
added which is more powerful -- templates can include parameters for
instance.
If you were using custom MediaWiki: entries for text inclusions, they
will *not* automatically be moved to Template: entries at upgrade time.
Be sure to go through and check that everything is working properly;
you can move them manually or you can try using moveCustomMessages.php
in maintenance/archives to do it automatically, but this might break things.
Also, be sure to pick the correct character encoding -- some languages were
only available in Latin-1 on 1.2.x and are now available for Unicode as well.
If you want to upgrade an existing wiki from Latin-1 to Unicode you'll have
to dump the database to SQL, run it through iconv or another conversion tool,
and restore it. Sorry.
== Upgrading from 1.1 or earlier ==
This is less thoroughly tested, but should work.
You need to specify the *admin* database username and password to the
installer in order for it to successfully upgrade the database structure.
You may wish to manually change the GRANTs later.
If you have a very old database (earlier than organized MediaWiki releases
in late August 2003) you may need to manually run some of the update SQL
scripts in maintenance/archives before the installer is able to pick up
with remaining updates.

105
sources/mediawiki/api.php Normal file
View file

@ -0,0 +1,105 @@
<?php
/**
* This file is the entry point for all API queries.
*
* It begins by checking whether the API is enabled on this wiki; if not,
* it informs the user that s/he should set $wgEnableAPI to true and exits.
* Otherwise, it constructs a new ApiMain using the parameter passed to it
* as an argument in the URL ('?action=') and with write-enabled set to the
* value of $wgEnableWriteAPI as specified in LocalSettings.php.
* It then invokes "execute()" on the ApiMain object instance, which
* produces output in the format specified in the URL.
*
* Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
// So extensions (and other code) can check whether they're running in API mode
define( 'MW_API', true );
// Bail if PHP is too low
if ( !function_exists( 'version_compare' ) || version_compare( phpversion(), '5.3.2' ) < 0 ) {
// We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
require dirname( __FILE__ ) . '/includes/PHPVersionError.php';
wfPHPVersionError( 'api.php' );
}
// Initialise common code.
require __DIR__ . '/includes/WebStart.php';
wfProfileIn( 'api.php' );
$starttime = microtime( true );
// URL safety checks
if ( !$wgRequest->checkUrlExtension() ) {
return;
}
// Verify that the API has not been disabled
if ( !$wgEnableAPI ) {
header( $_SERVER['SERVER_PROTOCOL'] . ' 500 MediaWiki configuration Error', true, 500 );
echo 'MediaWiki API is not enabled for this site. Add the following line to your LocalSettings.php'
. '<pre><b>$wgEnableAPI=true;</b></pre>';
die( 1 );
}
// Set a dummy $wgTitle, because $wgTitle == null breaks various things
// In a perfect world this wouldn't be necessary
$wgTitle = Title::makeTitle( NS_MAIN, 'API' );
/* Construct an ApiMain with the arguments passed via the URL. What we get back
* is some form of an ApiMain, possibly even one that produces an error message,
* but we don't care here, as that is handled by the ctor.
*/
$processor = new ApiMain( RequestContext::getMain(), $wgEnableWriteAPI );
// Process data & print results
$processor->execute();
// Execute any deferred updates
DeferredUpdates::doUpdates();
// Log what the user did, for book-keeping purposes.
$endtime = microtime( true );
wfProfileOut( 'api.php' );
wfLogProfilingData();
// Log the request
if ( $wgAPIRequestLog ) {
$items = array(
wfTimestamp( TS_MW ),
$endtime - $starttime,
$wgRequest->getIP(),
$_SERVER['HTTP_USER_AGENT']
);
$items[] = $wgRequest->wasPosted() ? 'POST' : 'GET';
$module = $processor->getModule();
if ( $module->mustBePosted() ) {
$items[] = "action=" . $wgRequest->getVal( 'action' );
} else {
$items[] = wfArrayToCgi( $wgRequest->getValues() );
}
wfErrorLog( implode( ',', $items ) . "\n", $wgAPIRequestLog );
wfDebug( "Logged API request to $wgAPIRequestLog\n" );
}
// Shut down the database. foo()->bar() syntax is not supported in PHP4: we won't ever actually
// get here to worry about whether this should be = or =&, but the file has to parse properly.
$lb = wfGetLBFactory();
$lb->shutdown();

View file

@ -0,0 +1,24 @@
<?php
/**
* Version of api.php to used in web server requiring .php5 extension
* to execute scripts with PHP5 engine.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
require 'api.php';

1
sources/mediawiki/cache/.htaccess vendored Executable file
View file

@ -0,0 +1 @@
Deny from all

View file

@ -0,0 +1,11 @@
{
"require": {
"php": ">=5.3.2"
},
"suggest": {
"ext-fileinfo": "*",
"ext-mbstring": "*",
"ext-wikidiff2": "*",
"ext-apc": "*"
}
}

View file

@ -0,0 +1,16 @@
[July 22nd 2008]
The 'docs' directory contain various text files that should help you understand
the most important parts of the code of MediaWiki. More in-depth documentation
can be found at http://www.mediawiki.org/wiki/Manual:Code.
API documentation is automatically generated and updated daily at:
http://svn.wikimedia.org/doc/
You can get a fresh version using 'make doc' or mwdocgen.php in the
../maintenance/ directory.
For end user / administrators, most of the documentation is located online at:
http://www.mediawiki.org/wiki/Help:Contents
http://www.mediawiki.org/wiki/Manual:Contents

View file

@ -0,0 +1,2 @@
This directory is for the auto-generated phpunit code coverage.
Run 'make coverage' in the tests/phpunit subdirectory to build.

View file

@ -0,0 +1,184 @@
The ContentHandler facility adds support for arbitrary content types on wiki pages, instead of relying on wikitext
for everything. It was introduced in MediaWiki 1.21.
Each kind of content ("content model") supported by MediaWiki is identified by unique name. The content model determines
how a page's content is rendered, compared, stored, edited, and so on.
Built-in content types are:
* wikitext - wikitext, as usual
* javascript - user provided javascript code
* css - user provided css code
* text - plain text
In PHP, use the corresponding CONTENT_MODEL_XXX constant.
A page's content model is available using the Title::getContentModel() method. A page's default model is determined by
ContentHandler::getDefaultModelFor($title) as follows:
* The global setting $wgNamespaceContentModels specifies a content model for the given namespace.
* The hook ContentHandlerDefaultModelFor may be used to override the page's default model.
* Pages in NS_MEDIAWIKI and NS_USER default to the CSS or JavaScript model if they end in .js or .css, respectively.
Pages in NS_MEDIAWIKI default to the wikitext model otherwise.
* The hook TitleIsCssOrJsPage may be used to force a page to use the CSS or JavaScript model.
This is a compatibility feature. The ContentHandlerDefaultModelFor hook should be used instead if possible.
* The hook TitleIsWikitextPage may be used to force a page to use the wikitext model.
This is a compatibility feature. The ContentHandlerDefaultModelFor hook should be used instead if possible.
* Otherwise, the wikitext model is used.
Note that is currently no mechanism to convert a page from one content model to another, and there is no guarantee that
revisions of a page will all have the same content model. Use Revision::getContentModel() to find it.
== Architecture ==
Two class hierarchies are used to provide the functionality associated with the different content models:
* Content interface (and AbstractContent base class) define functionality that acts on the concrete content of a page, and
* ContentHandler base class provides functionality specific to a content model, but not acting on concrete content.
The most important function of ContentHandler is to act as a factory for the appropriate implementation of Content. These
Content objects are to be used by MediaWiki everywhere, instead of passing page content around as text. All manipulation
and analysis of page content must be done via the appropriate methods of the Content object.
For each content model, a subclass of ContentHandler has to be registered with $wgContentHandlers. The ContentHandler
object for a given content model can be obtained using ContentHandler::getForModelID( $id ). Also Title, WikiPage and
Revision now have getContentHandler() methods for convenience.
ContentHandler objects are singletons that provide functionality specific to the content type, but not directly acting
on the content of some page. ContentHandler::makeEmptyContent() and ContentHandler::unserializeContent() can be used to
create a Content object of the appropriate type. However, it is recommended to instead use WikiPage::getContent() resp.
Revision::getContent() to get a page's content as a Content object. These two methods should be the ONLY way in which
page content is accessed.
Another important function of ContentHandler objects is to define custom action handlers for a content model, see
ContentHandler::getActionOverrides(). This is similar to what WikiPage::getActionOverrides() was already doing.
== Serialization ==
With the ContentHandler facility, page content no longer has to be text based. Objects implementing the Content interface
are used to represent and handle the content internally. For storage and data exchange, each content model supports
at least one serialization format via ContentHandler::serializeContent( $content ). The list of supported formats for
a given content model can be accessed using ContentHandler::getSupportedFormats().
Content serialization formats are identified using MIME type like strings. The following formats are built in:
* text/x-wiki - wikitext
* text/javascript - for js pages
* text/css - for css pages
* text/plain - for future use, e.g. with plain text messages.
* text/html - for future use, e.g. with plain html messages.
* application/vnd.php.serialized - for future use with the api and for extensions
* application/json - for future use with the api, and for use by extensions
* application/xml - for future use with the api, and for use by extensions
In PHP, use the corresponding CONTENT_FORMAT_XXX constant.
Note that when using the API to access page content, especially action=edit, action=parse and action=query&prop=revisions,
the model and format of the content should always be handled explicitly. Without that information, interpretation of
the provided content is not reliable. The same applies to XML dumps generated via maintenance/dumpBackup.php or
Special:Export.
Also note that the API will provide encapsulated, serialized content - so if the API was called with format=json, and
contentformat is also json (or rather, application/json), the page content is represented as a string containing an
escaped json structure. Extensions that use JSON to serialize some types of page content may provide specialized API
modules that allow access to that content in a more natural form.
== Compatibility ==
The ContentHandler facility is introduced in a way that should allow all existing code to keep functioning at least
for pages that contain wikitext or other text based content. However, a number of functions and hooks have been
deprecated in favor of new versions that are aware of the page's content model, and will now generate warnings when
used.
Most importantly, the following functions have been deprecated:
* Revisions::getText() and Revisions::getRawText() is deprecated in favor Revisions::getContent()
* WikiPage::getText() is deprecated in favor WikiPage::getContent()
Also, the old Article::getContent() (which returns text) is superceded by Article::getContentObject(). However, both
methods should be avoided since they do not provide clean access to the page's actual content. For instance, they may
return a system message for non-existing pages. Use WikiPage::getContent() instead.
Code that relies on a textual representation of the page content should eventually be rewritten. However,
ContentHandler::getContentText() provides a stop-gap that can be used to get text for a page. Its behavior is controlled
by $wgContentHandlerTextFallback; per default it will return the text for text based content, and null for any other
content.
For rendering page content, Content::getParserOutput() should be used instead of accessing the parser directly.
ContentHandler::makeParserOptions() can be used to construct appropriate options.
Besides some functions, some hooks have also been replaced by new versions (see hooks.txt for details).
These hooks will now trigger a warning when used:
* ArticleAfterFetchContent was replaced by ArticleAfterFetchContentObject
* ArticleInsertComplete was replaced by PageContentInsertComplete
* ArticleSave was replaced by PageContentSave
* ArticleSaveComplete was replaced by PageContentSaveComplete
* ArticleViewCustom was replaced by ArticleContentViewCustom (also consider a custom implementation of the view action)
* EditFilterMerged was replaced by EditFilterMergedContent
* EditPageGetDiffText was replaced by EditPageGetDiffContent
* EditPageGetPreviewText was replaced by EditPageGetPreviewContent
* ShowRawCssJs was deprecated in favor of custom rendering implemented in the respective ContentHandler object.
== Database Storage ==
Page content is stored in the database using the same mechanism as before. Non-text content is serialized first. The
appropriate serialization and deserialization is handled by the Revision class.
Each revision's content model and serialization format is stored in the revision table (resp. in the archive table, if
the revision was deleted). The page's (current) content model (that is, the content model of the latest revision) is also
stored in the page table.
Note however that the content model and format is only stored if it differs from the page's default, as determined by
ContentHandler::getDefaultModelFor( $title ). The default values are represented as NULL in the database, to preserve
space.
Storage of content model and format can be disabled altogether by setting $wgContentHandlerUseDB = false. In that case,
the page's default model (and the model's default format) will be used everywhere. Attempts to store a revision of a page
using a model or format different from the default will result in an error.
== Globals ==
There are some new globals that can be used to control the behavior of the ContentHandler facility:
* $wgContentHandlers associates content model IDs with the names of the appropriate ContentHandler subclasses.
* $wgNamespaceContentModels maps namespace IDs to a content model that should be the default for that namespace.
* $wgContentHandlerUseDB determines whether each revision's content model should be stored in the database.
Defaults is true.
* $wgContentHandlerTextFallback determines how the compatibility method ContentHandler::getContentText() will behave for
non-text content:
'ignore' causes null to be returned for non-text content (default).
'serialize' causes the serialized form of any non-text content to be returned (scary).
'fail' causes an exception to be thrown for non-text content (strict).
== Caveats ==
There are some changes in behavior that might be surprising to users:
* Javascript and CSS pages are no longer parsed as wikitext (though pre-save transform is still applied). Most
importantly, this means that links, including categorization links, contained in the code will not work.
* With $wgContentHandlerUseDB = false, pages can not be moved in a way that would change the
default model. E.g. [[MediaWiki:foo.js]] can not be moved to [[MediaWiki:foo bar]], but can still be moved to
[[User:John/foo.js]]. Also, in this mode, changing the default content model for a page (e.g. by changing
$wgNamespaceContentModels) may cause it to become inaccessible.
* action=edit will fail for pages with non-text content, unless the respective ContentHandler implementation has
provided a specialized handler for the edit action. This is true for the API as well.
* action=raw will fail for all non-text content. This seems better than serving content in other formats to an
unsuspecting recipient. This will also cause client-side diffs to fail.
* File pages provide their own action overrides that do not combine gracefully with any custom handlers defined by a
ContentHandler. If for example a File page used a content model with a custom revert action, this would be overridden by
WikiFilePage's handler for the revert action.

View file

@ -0,0 +1,198 @@
Some information about database access in MediaWiki.
By Tim Starling, January 2006.
------------------------------------------------------------------------
Database layout
------------------------------------------------------------------------
For information about the MediaWiki database layout, such as a
description of the tables and their contents, please see:
http://www.mediawiki.org/wiki/Manual:Database_layout
https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/core.git;a=blob_plain;f=maintenance/tables.sql;hb=HEAD
------------------------------------------------------------------------
API
------------------------------------------------------------------------
To make a read query, something like this usually suffices:
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( /* ...see docs... */ );
foreach ( $res as $row ) {
...
}
For a write query, use something like:
$dbw = wfGetDB( DB_MASTER );
$dbw->insert( /* ...see docs... */ );
We use the convention $dbr for read and $dbw for write to help you keep
track of whether the database object is a slave (read-only) or a master
(read/write). If you write to a slave, the world will explode. Or to be
precise, a subsequent write query which succeeded on the master may fail
when replicated to the slave due to a unique key collision. Replication
on the slave will stop and it may take hours to repair the database and
get it back online. Setting read_only in my.cnf on the slave will avoid
this scenario, but given the dire consequences, we prefer to have as
many checks as possible.
We provide a query() function for raw SQL, but the wrapper functions
like select() and insert() are usually more convenient. They take care
of things like table prefixes and escaping for you. If you really need
to make your own SQL, please read the documentation for tableName() and
addQuotes(). You will need both of them.
------------------------------------------------------------------------
Basic query optimisation
------------------------------------------------------------------------
MediaWiki developers who need to write DB queries should have some
understanding of databases and the performance issues associated with
them. Patches containing unacceptably slow features will not be
accepted. Unindexed queries are generally not welcome in MediaWiki,
except in special pages derived from QueryPage. It's a common pitfall
for new developers to submit code containing SQL queries which examine
huge numbers of rows. Remember that COUNT(*) is O(N), counting rows in a
table is like counting beans in a bucket.
------------------------------------------------------------------------
Replication
------------------------------------------------------------------------
The largest installation of MediaWiki, Wikimedia, uses a large set of
slave MySQL servers replicating writes made to a master MySQL server. It
is important to understand the issues associated with this setup if you
want to write code destined for Wikipedia.
It's often the case that the best algorithm to use for a given task
depends on whether or not replication is in use. Due to our unabashed
Wikipedia-centrism, we often just use the replication-friendly version,
but if you like, you can use wfGetLB()->getServerCount() > 1 to
check to see if replication is in use.
=== Lag ===
Lag primarily occurs when large write queries are sent to the master.
Writes on the master are executed in parallel, but they are executed in
serial when they are replicated to the slaves. The master writes the
query to the binlog when the transaction is committed. The slaves poll
the binlog and start executing the query as soon as it appears. They can
service reads while they are performing a write query, but will not read
anything more from the binlog and thus will perform no more writes. This
means that if the write query runs for a long time, the slaves will lag
behind the master for the time it takes for the write query to complete.
Lag can be exacerbated by high read load. MediaWiki's load balancer will
stop sending reads to a slave when it is lagged by more than 30 seconds.
If the load ratios are set incorrectly, or if there is too much load
generally, this may lead to a slave permanently hovering around 30
seconds lag.
If all slaves are lagged by more than 30 seconds, MediaWiki will stop
writing to the database. All edits and other write operations will be
refused, with an error returned to the user. This gives the slaves a
chance to catch up. Before we had this mechanism, the slaves would
regularly lag by several minutes, making review of recent edits
difficult.
In addition to this, MediaWiki attempts to ensure that the user sees
events occurring on the wiki in chronological order. A few seconds of lag
can be tolerated, as long as the user sees a consistent picture from
subsequent requests. This is done by saving the master binlog position
in the session, and then at the start of each request, waiting for the
slave to catch up to that position before doing any reads from it. If
this wait times out, reads are allowed anyway, but the request is
considered to be in "lagged slave mode". Lagged slave mode can be
checked by calling wfGetLB()->getLaggedSlaveMode(). The only
practical consequence at present is a warning displayed in the page
footer.
=== Lag avoidance ===
To avoid excessive lag, queries which write large numbers of rows should
be split up, generally to write one row at a time. Multi-row INSERT ...
SELECT queries are the worst offenders should be avoided altogether.
Instead do the select first and then the insert.
=== Working with lag ===
Despite our best efforts, it's not practical to guarantee a low-lag
environment. Lag will usually be less than one second, but may
occasionally be up to 30 seconds. For scalability, it's very important
to keep load on the master low, so simply sending all your queries to
the master is not the answer. So when you have a genuine need for
up-to-date data, the following approach is advised:
1) Do a quick query to the master for a sequence number or timestamp 2)
Run the full query on the slave and check if it matches the data you got
from the master 3) If it doesn't, run the full query on the master
To avoid swamping the master every time the slaves lag, use of this
approach should be kept to a minimum. In most cases you should just read
from the slave and let the user deal with the delay.
------------------------------------------------------------------------
Lock contention
------------------------------------------------------------------------
Due to the high write rate on Wikipedia (and some other wikis),
MediaWiki developers need to be very careful to structure their writes
to avoid long-lasting locks. By default, MediaWiki opens a transaction
at the first query, and commits it before the output is sent. Locks will
be held from the time when the query is done until the commit. So you
can reduce lock time by doing as much processing as possible before you
do your write queries.
Often this approach is not good enough, and it becomes necessary to
enclose small groups of queries in their own transaction. Use the
following syntax:
$dbw = wfGetDB( DB_MASTER );
$dbw->begin( __METHOD__ );
/* Do queries */
$dbw->commit( __METHOD__ );
Use of locking reads (e.g. the FOR UPDATE clause) is not advised. They
are poorly implemented in InnoDB and will cause regular deadlock errors.
It's also surprisingly easy to cripple the wiki with lock contention. If
you must use them, define a new flag for $wgAntiLockFlags which allows
them to be turned off, because we'll almost certainly need to do so on
the Wikimedia cluster.
Instead of locking reads, combine your existence checks into your write
queries, by using an appropriate condition in the WHERE clause of an
UPDATE, or by using unique indexes in combination with INSERT IGNORE.
Then use the affected row count to see if the query succeeded.
------------------------------------------------------------------------
Supported DBMSs
------------------------------------------------------------------------
MediaWiki is written primarily for use with MySQL. Queries are optimized
for it and its schema is considered the canonical version. However,
MediaWiki does support the following other DBMSs to varying degrees.
* PostgreSQL
* SQLite
* Oracle
* MSSQL
More information can be found about each of these databases (known issues,
level of support, extra configuration) in the "databases" subdirectory in
this folder.
------------------------------------------------------------------------
Use of GROUP BY
------------------------------------------------------------------------
MySQL supports GROUP BY without checking anything in the SELECT clause.
Other DBMSs (especially Postgres) are stricter and require that all the
non-aggregate items in the SELECT clause appear in the GROUP BY. For
this reason, it is highly discouraged to use SELECT * with GROUP BY
queries.

View file

@ -0,0 +1,112 @@
This document describes the state of Postgres support in MediaWiki.
== Overview ==
Support for PostgreSQL has been available since version 1.7
of MediaWiki, and is fairly well maintained. The main code
is very well integrated, while extensions are very hit and miss.
Still, it is probably the most supported database after MySQL.
Much of the work in making MediaWiki database-agnostic came
about through the work of creating Postgres support.
== Required versions ==
The current minimum version of PostgreSQL for MediaWiki is 8.1.
It is expected that this will be raised to 8.3 at some point,
as 8.1 and 8.2 are nearing end of life.
== Database schema ==
Postgres has its own schema file at maintenance/postgres/tables.sql.
The goal is to keep this file as close as possible to the canonical
schema at maintenance/tables.sql, but without copying over
all the usage comments. General notes on the conversion:
* The use of a true TIMESTAMP rather than the text string that
MySQL uses is highly encouraged. There are still places in the
code (especially extensions) which make assumptions about the
textual nature of timestamp fields, but these can almost always
be programmed around.
* Although Postgres has a true BOOLEAN type, boolean columns
are always mapped to SMALLINT, as the code does not always treat
the column as a boolean (which is limited to accepting true,
false, 0, 1, t, or f)
* The default data type for all VARCHAR, CHAR, and VARBINARY
columns should simply be TEXT. The only exception is
when VARBINARY is used to store true binary data, such as
the math_inputhash column, in which case BYTEA should be used.
* All integer variants should generally be mapped to INTEGER.
There is small-to-no advantage in using SMALLINT versus
INTEGER in Postgres, and the possibility of running out of
room outweighs such concerns. The columns that are BIGINT
in other schemas should be INTEGER as well, as none of them
so far are even remotely likely to reach the 32 billion
limit of an INTEGER.
* Blobs (blob, tinyblog, mediumblob) should be mapped to TEXT
whenever possible, and to BYTEA if they are known to contain
binary data.
* All length modifiers on data types should be removed. If
they are on an INTEGER, it's probably an error, and if on
any text-based field, simply using TEXT is preferred.
* Sequences should be explicitly named rather than using
SERIAL, as the code can depend on having a specific name.
* Foreign keys should be used when possible. This makes things
both easier and harder in the code, but most of the major
problems have now been overcome. Always add an explicit ON DELETE
clause, and consider carefully what choice to use (all things
considered, prefer CASCADE).
* The use of CIDR should be done very carefully, because the code
will sometimes want to store things such as an empty string or
other non-IP value in the column. When in doubt, use TEXT.
* Indexes should be created using the original MySQL tables.sql
as a guide, but keeping in mind the ability of Postgres to use
partial indexes, functional indexes, and bitmaps. The index names
should be logical but are not too important, as they are never
referenced directly by the code (unlike sequence names). Most of
the indexes in the file as of this writing are there due to production
testing of expensive queries on a busy wiki.
== Keeping in sync with tables.sql ==
The script maintenance/postgres/compare_schemas.pl should be
periodically run. It will parse both "tables.sql" files and
produce any differences found. Such differences should be fixed
or exceptions specifically carved out by editing the script
itself. This script has also been very useful in finding problems
in maintenance/tables.sql itself, as it is very strict in the
format it expects things to be in. :)
== MySQL differences ==
The major differences between MySQL and Postgres are represented as
methods in the Database class. For example, implicitGroupby() is
true for MySQL and false for Postgres. This means that in those
places where the code does not add all the non-aggregate items
from the SELECT clause to the GROUP BY, we can add them in, but in
a conditional manner with the above method, as simply adding them
all in to the main query may cause performance problems with
MySQL.
== Getting help ==
In addition to the normal venues (MediaWiki mailing lists
and IRC channels), the #postgresql channel on irc.freenode.net
is a friendly and expert resource if you should encounter a
problem with your Postgres-enabled MediaWiki.

View file

@ -0,0 +1,12 @@
SQLite shares the MySQL schema file at maintenance/tables.sql, with a set of
compatibility regexes to convert MySQL syntax to SQLite syntax:
* BINARY() and VARBINARY() fields are converted to BLOB
* the UNSIGNED modifier is removed
* "INT" fields are converted to "INTEGER"
* ENUM is converted to BLOB
* the BINARY collation modifier is removed
* AUTO_INCREMENT is converted to AUTOINCREMENT
* Any table options are removed
* Truncated indexes are upgraded to full-width indexes
* FULLTEXT indexes are converted to ordinary indexes

View file

@ -0,0 +1,36 @@
deferred.txt
A few of the database updates required by various functions here can be
deferred until after the result page is displayed to the user. For example,
updating the view counts, updating the linked-to tables after a save, etc. PHP
does not yet have any way to tell the server to actually return and disconnect
while still running these updates (as a Java servelet could), but it might have
such a feature in the future.
We handle these by creating a deferred-update object and putting those objects
on a global list, then executing the whole list after the page is displayed. We
don't do anything smart like collating updates to the same table or such
because the list is almost always going to have just one item on it, if that,
so it's not worth the trouble.
Since 1.6 there is a 'job queue' in the jobs table, which is used to update
link tables of transcluding pages after edits; this may be extended in the
future to more general background tasks.
Job queue items are fetched out of the queue and run either at a random rate
during regular page views (by default) or by a batch process which can be run
via maintenance/runJobs.php.
Currently there are a few different types of jobs:
refreshLinks
Used to refresh the database tables that store the links between pages.
When a page is changed, all pages using that page are also cleared by
inserting a new job for all those pages. Each job refreshes only one page.
htmlCacheUpdate
Clear caches when a template is changed to ensure that changes can be seen.
Each job clears $wgUpdateRowsPerJob pages (500 by default).
enotifNotify
Used when $wgEnotifUseJobQ is true to send mail using the job queue.

View file

@ -0,0 +1,106 @@
design.txt
This is a brief overview of the new design.
More thorough and up-to-date information is available on the documentation
wiki at http://www.mediawiki.org/
Primary classes:
User
Encapsulates the state of the user viewing/using the site. Can be queried
for things like the user's settings, name, etc. Handles the details of
getting and saving to the "user" table of the database, and dealing with
sessions and cookies.
OutputPage
Encapsulates the entire HTML page that will be sent in response to any
server request. It is used by calling its functions to add text, headers,
etc., in any order, and then calling output() to send it all. It could be
easily changed to send incrementally if that becomes useful, but I prefer
the flexibility. This should also do the output encoding. The system
allocates a global one in $wgOut.
Title
Represents the title of an article, and does all the work of translating
among various forms such as plain text, URL, database key, etc. For
convenience, and for historical reasons, it also represents a few features
of articles that don't involve their text, such as access rights.
See also title.txt.
Article
Encapsulates access to the "page" table of the database. The object
represents a an article, and maintains state such as text (in Wikitext
format), flags, etc.
Revision
Encapsulates individual page revision data and access to the
revision/text/blobs storage system. Higher-level code should never touch
text storage directly; this class mediates it.
Skin
Encapsulates a "look and feel" for the wiki. All of the functions that
render HTML, and make choices about how to render it, are here, and are
called from various other places when needed (most notably,
OutputPage::addWikiText()). The StandardSkin object is a complete
implementation, and is meant to be subclassed with other skins that may
override some of its functions. The User object contains a reference to a
skin (according to that user's preference), and so rather than having a
global skin object we just rely on the global User and get the skin with
$wgUser->getSkin().
See also skin.txt.
Language
Represents the language used for incidental text, and also has some
character encoding functions and other locale stuff. The current user
interface language is instantiated as $wgLang, and the local content
language as $wgContLang; be sure to use the *correct* language object
depending upon the circumstances.
See also language.txt.
Parser
Class used to transform wikitext to html.
LinkCache
Keeps information on existence of articles. See linkcache.txt.
Naming/coding conventions:
These are meant to be descriptive, not dictatorial; I won't presume to tell
you how to program, I'm just describing the methods I chose to use for myself.
If you do choose to follow these guidelines, it will probably be easier for
you to collaborate with others on the project, but if you want to contribute
without bothering, by all means do so (and don't be surprised if I reformat
your code).
- I have the code indented with tabs to save file size and so that users can
set their tab stops to any depth they like. I use 4-space tab stops, which
work well. I also use K&R brace matching style. I know that's a religious
issue for some, so if you want to use a style that puts opening braces on
the next line, that's OK too, but please don't use a style where closing
braces don't align with either the opening brace on its own line or the
statement that opened the block--that's confusing as hell.
- Certain functions and class members are marked with /* private */, rather
than being marked as such. This is a hold-over from PHP 4, which didn't
support proper visibilities. You should not access things marked in this
manner outside the class/inheritance line as this code is subjected to be
updated in a manner that enforces this at some time in the near future, and
things will break. New code should use the standard method of setting
visibilities as normal.
- Globals are particularly evil in PHP; it sets a lot of them automatically
from cookies, query strings, and such, leading to namespace conflicts; when
a variable name is used in a function, it is silently declared as a new
local masking the global, so you'll get weird error because you forgot the
global declaration; lack of static class member variables means you have to
use globals for them, etc. Evil, evil.
I think I've managed to pare down the number of globals we use to a scant
few dozen or so, and I've prefixed them all with "wg" so you can spot errors
better (odds are, if you see a "wg" variable being used in a function that
doesn't declare it global, that's probably an error).
Other conventions: Top-level functions are wfFuncname(), names of session
variables are wsName, cookies wcName, and form field values wpName ("p" for
"POST").

View file

@ -0,0 +1,208 @@
This document is intended to provide useful advice for parties seeking to
redistribute MediaWiki to end users. It's targeted particularly at maintainers
for Linux distributions, since it's been observed that distribution packages of
MediaWiki often break. We've consistently had to recommend that users seeking
support use official tarballs instead of their distribution's packages, and
this often solves whatever problem the user is having. It would be nice if
this could change.
== Background: why web applications are different ==
MediaWiki is intended to be usable on any web host that provides support for
PHP and a database. Many users of low-end shared hosting have very limited
access to their machine: often only FTP access to some subdirectory of the web
root. Support for these users entails several restrictions, such as:
1) We cannot require installation of any files outside the web root. Few of
our users have access to directories like /usr or /etc.
2) We cannot require the ability to run any utility on the command line.
Many shared hosts have exec() and similar PHP functions disabled.
3) We cannot assume that the software has write access anywhere useful. The
user account that MediaWiki (including its installer) runs under is often
different from the account the user used to upload the files, and we might be
restricted by PHP settings such as safe mode or open_basedir.
4) We cannot assume that the software even has read access anywhere useful.
Many shared hosts run all users' web applications under the same user, so
they can't rely on Unix permissions, and must forbid reads to even standard
directories like /tmp lest users read each others' files.
5) We cannot assume that the user has the ability to install or run any
programs not written as web-accessible PHP scripts.
Since anything that works on cheap shared hosting will work if you have shell
or root access too, MediaWiki's design is based around catering to the lowest
common denominator. Although we support higher-end setups as well (like
Wikipedia!), the way many things work by default is tailored toward shared
hosting. These defaults are unconventional from the point of view of normal
(non-web) applications -- they might conflict with distributors' policies, and
they certainly aren't ideal for someone who's installing MediaWiki as root.
== Directory structure ==
Because of constraint (1) above, MediaWiki does not conform to normal
Unix filesystem layout. Hopefully we'll offer direct support for standard
layouts in the future, but for now *any change to the location of files is
unsupported*. Moving things and leaving symlinks will *probably* not break
anything, but it is *strongly* advised not to try any more intrusive changes to
get MediaWiki to conform more closely to your filesystem hierarchy. Any such
attempt will almost certainly result in unnecessary bugs.
The standard recommended location to install MediaWiki, relative to the web
root, is /w (so, e.g., /var/www/w). Rewrite rules can then be used to enable
"pretty URLs" like /wiki/Article instead of /w/index.php?title=Article. (This
is the convention Wikipedia uses.) In theory, it should be possible to enable
the appropriate rewrite rules by default, if you can reconfigure the web
server, but you'd need to alter LocalSettings.php too. See
<http://www.mediawiki.org/wiki/Manual:Short_URL> for details on short URLs.
If you really must mess around with the directory structure, note that the
following files *must* all be web-accessible for MediaWiki to function
correctly:
* api.php, img_auth.php, index.php, load.php, opensearch_desc.php, thumb.php,
profileinfo.php, redirect.php, trackback.php. These are the entry points for
normal usage. This list may be incomplete and is subject to change.
* mw-config/index.php: Used for web-based installation (sets up the database,
prompts for the name of the wiki, etc.).
* images/: Used for uploaded files. This could be somewhere else if
$wgUploadDirectory and $wgUploadPath are changed appropriately.
* skins/*/: Subdirectories of skins/ contain CSS and JavaScript files that
must be accessible to web browsers. The PHP files and Skin.sample in skins/
don't need to be accessible. This could be somewhere else if
$wgStyleDirectory and $wgStylePath are changed appropriately.
* extensions/: Many extensions include CSS and JavaScript files in their
extensions directory, and will break if they aren't web-accessible. Some
extensions might theoretically provide additional entry points as well, at
least in principle.
But all files should keep their position relative to the web-visible
installation directory no matter what. If you must move includes/ somewhere in
/usr/share, provide a symlink from /var/www/w. If you don't, you *will* break
something. You have been warned.
== Configuration ==
MediaWiki is configured using LocalSettings.php. This is a PHP file that's
generated when the user visits mw-config/index.php to install the software, and
which the user can edit by hand thereafter. It's just a plain old PHP file,
and can contain any PHP statements. It usually sets global variables that are
used for configuration, and includes files used by any extensions.
Distributors can easily add extra statements to the autogenerated
LocalSettings.php by changing mw-config/overrides.php (see that file for details
and examples).
There's a new maintenance/install.php script which could be used for performing
an install through the command line.
Some configuration options that distributors might be in a position to set
intelligently:
* $wgEmergencyContact: An e-mail address that can be used to contact the wiki
administrator. By default, "wikiadmin@ServerName".
* $wgPasswordSender: The e-mail address to use when sending password e-mails.
By default, "MediaWiki Mail <apache@ServerName>".
(with ServerName guessed from the http request)
* $wgSMTP: Can be configured to use SMTP for mail sending instead of PHP
mail().
== Updates ==
The correct way for updating a wiki is to update the files and then run from
command line the maintenance/update.php script (with appropriate parameters if
files were moved). It will perform all the needed steps to update the database
schema and contents to the version from whatever old one it has.
Any package manager which replaces the files but doesn't update the db is leaving
an inconsistent wiki that may produce blank pages (php errors) when new features
using the changed schema would be used.
Since MediaWiki 1.17 it is possible to upgrade using the installer by providing
an arbitrary secret value stored as $wgUpgradeKey in LocalSettings (older versions
needed to rename LocalSettings.php in order to upgrade using the installer).
== Documentation ==
MediaWiki's official documentation is split between two places: the source
code, and <http://www.mediawiki.org/>. The source code documentation is written
exclusively by developers, and so is likely to be reliable (at worst,
outdated). However, it can be pretty sparse. mediawiki.org documentation is
often much more thorough, but it's maintained by a wiki that's open to
anonymous edits, so its quality is sometimes sketchy -- don't assume that
anything there is officially endorsed!
== Upstream ==
MediaWiki is a project hosted and led by the Wikimedia Foundation, the
not-for-profit charity that operates Wikipedia. Wikimedia employs the lead
developer and several other paid developers, but commit access is given out
liberally and there are multiple very active volunteer developers as well. A
list of developers can be found at <http://www.mediawiki.org/wiki/Developers>.
MediaWiki's bug tracker is at <https://bugzilla.wikimedia.org>. However, most
developers follow the bug tracker little or not at all. The best place to
post if you want to get developers' attention is the wikitech-l mailing list
<https://lists.wikimedia.org/mailman/listinfo/wikitech-l>. Posts to wikitech-l
will inevitably be read by multiple experienced MediaWiki developers. There's
also an active IRC chat at <irc://irc.freenode.net/mediawiki>, where there are
usually several developers at reasonably busy times of day.
Unfortunately, we don't have a very good system for patch review. Patches
should be submitted on Bugzilla (as unified diffs produced with "svn diff"
against the latest trunk revision), but many patches languish without review
until they bitrot into uselessness. You might want to get a developer to
commit to reviewing your patch before you put too much effort into it.
Reasonably straightforward patches shouldn't be too hard to get accepted if
there's an interested developer, however -- posting to Bugzilla and then
dropping a note on wikitech-l if nobody responds is a good tactic.
All redistributors of MediaWiki should be subscribed to mediawiki-announce
<https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce>. It's
extremely low-traffic, with an average of less than one post per month. All
new releases are announced here, including critical security updates.
== Useful software to install ==
There are several other pieces of software that MediaWiki can make good use of.
Distributors might choose to install these automatically with MediaWiki and
perhaps configure it to use them (see Configuration section of this document):
* APC (Alternative PHP Cache), XCache, or similar: Will greatly speed up the
execution of MediaWiki, and all other PHP applications, at some cost in
memory usage. Will be used automatically for the most part.
* clamav: Can be used for virus scanning of uploaded files. Enable with
"$wgAntivirus = 'clamav';".
* DjVuLibre: Allows processing of DjVu files. To enable this, set
"$wgDjvuDump = 'djvudump'; $wgDjvuRenderer = 'ddjvu'; $wgDjvuTxt = 'djvutxt';".
* HTML Tidy: Fixes errors in HTML at runtime. Can be enabled with
"$wgUseTidy = true;".
* ImageMagick: For resizing images. "$wgUseImageMagick = true;" will enable
it. PHP's GD can also be used, but ImageMagick is preferable.
* Squid: Can provide a drastic speedup and a major cut in resource
consumption, but enabling it may interfere with other applications. It might
be suitable for a separate mediawiki-squid package. For setup details, see:
<http://www.mediawiki.org/wiki/Manual:Squid_caching>
* rsvg or other SVG rasterizer: ImageMagick can be used for SVG support, but
is not ideal. Wikipedia (as of the time of this writing) uses rsvg. To
enable, set "$wgSVGConverter = 'rsvg';" (or other as appropriate).
* texvc: Included with MediaWiki. Instructions for compiling and
installing it are in the math/ directory.
MediaWiki uses some standard GNU utilities as well, such as diff and diff3. If
these are present in /usr/bin or some other reasonable location, they will be
configured automatically on install.
MediaWiki also has a "job queue" that handles background processing. Because
shared hosts often don't provide access to cron, the job queue is run on every
page view by default. This means the background tasks aren't really done in
the background. Busy wikis can set $wgJobRunRate to 0 and run
maintenance/runJobs.php periodically out of cron. Distributors probably
shouldn't set this up as a default, however, since the extra cron job is
unnecessary overhead for a little-used wiki.
== Web server configuration ==
MediaWiki includes several .htaccess files to restrict access to some
directories. If the web server is not configured to support these files, and
the relevant directories haven't been moved someplace inaccessible anyway (e.g.
symlinked in /usr/share with the web server configured to not follow symlinks),
then it might be useful to deny web access to those directories in the web
server's configuration.

View file

@ -0,0 +1,19 @@
<?php
die("Not a valid entry point\n");
/**
* This file does not hold any code. It is only there so we can generate
* the doxygen documentation main page.
*
* @file
*/
/**
* @mainpage Introduction
*
* Welcome on MediaWiki autogenerated documentation system.
*
* If you are looking to use, install or configure your wiki, you probably
* want to look at the main site: https://www.mediawiki.org/
*
* @note this page is generated from docs/doxygen_first_page.php
*/

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.1.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.1/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.1/"
targetNamespace="http://www.mediawiki.org/xml/export-0.1/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision data -->
<element name="revision" type="mw:RevisionType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="string" minOccurs="0"/>
<element name="text" type="string"/>
</sequence>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
</complexType>
</schema>

View file

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.2.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.2/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.2/"
targetNamespace="http://www.mediawiki.org/xml/export-0.2/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="string" minOccurs="0"/>
<element name="text" type="string"/>
</sequence>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="string" minOccurs="0"/>
<!-- Filename. (Using underscores, not spaces. No 'Image:' namespace marker.) -->
<element name="filename" type="string"/>
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI"/>
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
</schema>

View file

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.3.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.3/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.3/"
targetNamespace="http://www.mediawiki.org/xml/export-0.3/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1"/>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have two titles differing only by case. -->
<!-- Not yet implemented as of MediaWiki 1.5 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
</extension>
</simpleContent>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="string" minOccurs="0"/>
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="string" minOccurs="0"/>
<!-- Filename. (Using underscores, not spaces. No 'Image:' namespace marker.) -->
<element name="filename" type="string"/>
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI"/>
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
</schema>

View file

@ -0,0 +1,216 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.4.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.4/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.4/"
targetNamespace="http://www.mediawiki.org/xml/export-0.4/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1"/>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted"/>
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- flag if the current revision is a redirect -->
<element name="redirect" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
<element name="logitem" type="mw:LogItemType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN"/>
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="string" minOccurs="0"/>
<!-- Filename. (Using underscores, not spaces. No 'Image:' namespace marker.) -->
<element name="filename" type="string"/>
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI"/>
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View file

@ -0,0 +1,219 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.5.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.5/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.5/"
targetNamespace="http://www.mediawiki.org/xml/export-0.5/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1"/>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted"/>
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- flag if the current revision is a redirect -->
<element name="redirect" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
<element name="logitem" type="mw:LogItemType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN"/>
<attribute name="bytes" use="optional" type="nonNegativeInteger"/>
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="string" minOccurs="0"/>
<!-- Filename. (Using underscores, not spaces. No 'Image:' namespace marker.) -->
<element name="filename" type="string"/>
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI"/>
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View file

@ -0,0 +1,226 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
Version 0.6 adds a separate namespace tag, and resolves the
redirect target and adds a separate sha1 tag for each revision.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.6.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.6/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.6/"
targetNamespace="http://www.mediawiki.org/xml/export-0.6/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1"/>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted"/>
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- Namespace in canonical form -->
<element name="ns" type="positiveInteger"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- flag if the current revision is a redirect -->
<element name="redirect" type="string" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
<element name="logitem" type="mw:LogItemType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="sha1" type="string" />
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN"/>
<attribute name="bytes" use="optional" type="nonNegativeInteger"/>
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="string" minOccurs="0"/>
<!-- Filename. (Using underscores, not spaces. No 'Image:' namespace marker.) -->
<element name="filename" type="string"/>
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI"/>
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View file

@ -0,0 +1,272 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
Version 0.6 adds a separate namespace tag, and resolves the
redirect target and adds a separate sha1 tag for each revision.
Version 0.7 adds a unique identity constraint for both page and
revision identifiers. See also bug 4220.
Fix type for <ns> from "positiveInteger" to "nonNegativeInteger" to allow 0
Moves <logitem> to its right location.
Add parentid to revision.
Fix type for <id> within <contributor> to "nonNegativeInteger"
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.7.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.7/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.7/"
targetNamespace="http://www.mediawiki.org/xml/export-0.7/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd" />
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType">
<!-- Page ID contraint, see bug 4220 -->
<unique name="PageIDConstraint">
<selector xpath="mw:page" />
<field xpath="mw:id" />
</unique>
<!-- Revision ID contraint, see bug 4220 -->
<unique name="RevIDConstraint">
<selector xpath="mw:page/mw:revision" />
<field xpath="mw:id" />
</unique>
</element>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1" />
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded" />
<element name="logitem" type="mw:LogItemType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="version" type="string" use="required" />
<attribute ref="xml:lang" use="required" />
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted" />
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="RedirectType">
<simpleContent>
<extension base="string">
<attribute name="title" type="string" />
</extension>
</simpleContent>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string" />
<!-- Namespace in canonical form -->
<element name="ns" type="nonNegativeInteger" />
<!-- optional page ID number -->
<element name="id" type="positiveInteger" />
<!-- flag if the current revision is a redirect -->
<element name="redirect" type="mw:RedirectType" minOccurs="0" maxOccurs="1" />
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0" />
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="parentid" type="positiveInteger" minOccurs="0" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="minor" minOccurs="0" maxOccurs="1" />
<element name="comment" type="mw:CommentType" minOccurs="0" maxOccurs="1" />
<element name="sha1" type="string" />
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="mw:CommentType" minOccurs="0" />
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:LogTextType" minOccurs="0" maxOccurs="1" />
<element name="logtitle" type="string" minOccurs="0" maxOccurs="1" />
<element name="params" type="mw:LogParamsType" minOccurs="0" maxOccurs="1" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN" />
<attribute name="bytes" use="optional" type="nonNegativeInteger" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogTextType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogParamsType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0" />
<element name="id" type="nonNegativeInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0" />
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="string" minOccurs="0" />
<!-- Filename. (Using underscores, not spaces. No 'File:' namespace marker.) -->
<element name="filename" type="string" />
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI" />
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View file

@ -0,0 +1,289 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
Version 0.6 adds a separate namespace tag, and resolves the
redirect target and adds a separate sha1 tag for each revision.
Version 0.7 adds a unique identity constraint for both page and
revision identifiers. See also bug 4220.
Fix type for <ns> from "positiveInteger" to "nonNegativeInteger" to allow 0
Moves <logitem> to its right location.
Add parentid to revision.
Fix type for <id> within <contributor> to "nonNegativeInteger"
Version 0.8 adds support for a <model> and a <format> tag for
each revision. See contenthandler.txt.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.8.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.8/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.8/"
targetNamespace="http://www.mediawiki.org/xml/export-0.8/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd" />
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType">
<!-- Page ID contraint, see bug 4220 -->
<unique name="PageIDConstraint">
<selector xpath="mw:page" />
<field xpath="mw:id" />
</unique>
<!-- Revision ID contraint, see bug 4220 -->
<unique name="RevIDConstraint">
<selector xpath="mw:page/mw:revision" />
<field xpath="mw:id" />
</unique>
</element>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1" />
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded" />
<element name="logitem" type="mw:LogItemType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="version" type="string" use="required" />
<attribute ref="xml:lang" use="required" />
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted" />
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="RedirectType">
<simpleContent>
<extension base="string">
<attribute name="title" type="string" />
</extension>
</simpleContent>
</complexType>
<simpleType name="ContentModelType">
<restriction base="string">
<pattern value="[a-zA-Z][-+./a-zA-Z0-9]*" />
</restriction>
</simpleType>
<simpleType name="ContentFormatType">
<restriction base="string">
<pattern value="[a-zA-Z][-+.a-zA-Z0-9]*/[a-zA-Z][-+.a-zA-Z0-9]*" />
</restriction>
</simpleType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string" />
<!-- Namespace in canonical form -->
<element name="ns" type="nonNegativeInteger" />
<!-- optional page ID number -->
<element name="id" type="positiveInteger" />
<!-- flag if the current revision is a redirect -->
<element name="redirect" type="mw:RedirectType" minOccurs="0" maxOccurs="1" />
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0" />
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="parentid" type="positiveInteger" minOccurs="0" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="minor" minOccurs="0" maxOccurs="1" />
<element name="comment" type="mw:CommentType" minOccurs="0" maxOccurs="1" />
<element name="text" type="mw:TextType" />
<element name="sha1" type="string" />
<element name="model" type="mw:ContentModelType" />
<element name="format" type="mw:ContentFormatType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="mw:CommentType" minOccurs="0" />
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:LogTextType" minOccurs="0" maxOccurs="1" />
<element name="logtitle" type="string" minOccurs="0" maxOccurs="1" />
<element name="params" type="mw:LogParamsType" minOccurs="0" maxOccurs="1" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN" />
<attribute name="bytes" use="optional" type="nonNegativeInteger" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogTextType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogParamsType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0" />
<element name="id" type="nonNegativeInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0" />
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="string" minOccurs="0" />
<!-- Filename. (Using underscores, not spaces. No 'File:' namespace marker.) -->
<element name="filename" type="string" />
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI" />
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View file

@ -0,0 +1,157 @@
<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.8/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.8/ http://www.mediawiki.org/xml/export-0.8.xsd" version="0.8" xml:lang="en">
<!-- Optional global configuration info -->
<siteinfo>
<!-- Site name, as set in $wgSitename -->
<sitename>DemoWiki</sitename>
<!-- Forgot where you got this set? -->
<base>http://example.com/wiki/Main_Page</base>
<!-- Source software version -->
<generator>MediaWiki 1.20</generator>
<!-- Title case sensitivity options of the wiki this data came from -->
<!-- May be 'first-letter', 'case-sensitive', or 'case-insensitive' -->
<case>first-letter</case>
<!-- Defined namespace keys on the source wiki. -->
<namespaces>
<namespace key="-2" case="first-letter">Media</namespace>
<namespace key="-1" case="first-letter">Special</namespace>
<namespace key="0" case="first-letter" />
<namespace key="1" case="first-letter">Talk</namespace>
<namespace key="2" case="first-letter">User</namespace>
<namespace key="3" case="first-letter">User talk</namespace>
<namespace key="4" case="first-letter">DemoWiki</namespace>
<namespace key="5" case="first-letter">DemoWIki talk</namespace>
<namespace key="6" case="first-letter">File</namespace>
<namespace key="7" case="first-letter">File talk</namespace>
<namespace key="8" case="first-letter">MediaWiki</namespace>
<namespace key="9" case="first-letter">MediaWiki talk</namespace>
<namespace key="10" case="first-letter">Template</namespace>
<namespace key="11" case="first-letter">Template talk</namespace>
<namespace key="12" case="first-letter">Help</namespace>
<namespace key="13" case="first-letter">Help talk</namespace>
<namespace key="14" case="first-letter">Category</namespace>
<namespace key="15" case="first-letter">Category talk</namespace>
</namespaces>
</siteinfo>
<!-- The rest of the data will be a series of page records -->
<page>
<!-- Titles are listed here in text form, with namespace prefix -->
<!-- if any, and spaces rather than the underscores used in URLs. -->
<title>Page title</title>
<!-- Namespace in canonical form -->
<ns>0</ns>
<!-- The page's immutable page_id number in the source database. -->
<!-- Page ID numbers are kept across page moves, but may change -->
<!-- if a page is deleted and recreated. -->
<id>1</id>
<!-- Tag wether this article is a redirect and its target -->
<!-- This corresponds to the page_is_redirect in the page table -->
<redirect title="Target" />
<!-- If restricted, the ACL is listed here raw. -->
<restrictions>edit=sysop:move=sysop</restrictions>
<!-- With a series of revision records... -->
<!-- Remember this is XML; if you must use a regex-based extractor -->
<!-- in place of a standard XML parser, be very careful. -->
<!-- * Don't forget to decode character entities! -->
<!-- * If using a 'loose' XML parser, ensure that whitespace is -->
<!-- preserved in the <text> elements. -->
<revision>
<!-- Unique revision ID number (rev_id) in the source database. -->
<!-- This number uniquely identifies the revision on that wiki. -->
<id>100</id>
<!-- revision id of the parent revision -->
<parentid>99</parentid>
<timestamp>2001-01-15T13:15:00Z</timestamp>
<contributor>
<username>Foobar</username>
<id>42</id>
</contributor>
<minor />
<comment>I have just one thing to say!</comment>
<text xml:space="preserve" bytes="25">A bunch of [[text]] here.</text>
<sha1>5x0ux8iwjrbmfzgv6pkketxgkcnpr7h</sha1>
<model>wikitext</model>
<format>text/x-wiki</format>
</revision>
<revision>
<id>99</id>
<timestamp>2001-01-15T13:10:27Z</timestamp>
<contributor>
<ip>10.0.0.2</ip>
</contributor>
<comment>new!</comment>
<text xml:space="preserve" bytes="24">An earlier [[revision]].</text>
<sha1>etaxt3shcge6igz1biwy3d4um2pnle4</sha1>
<model>wikitext</model>
<format>text/x-wiki</format>
</revision>
</page>
<page>
<title>Talk:Page title</title>
<ns>1</ns>
<id>2</id>
<revision>
<id>101</id>
<timestamp>2001-01-15T14:03:00Z</timestamp>
<contributor><ip>10.0.0.2</ip></contributor>
<comment>hey</comment>
<text xml:space="preserve" bytes="47">WHYD YOU LOCK PAGE??!!! i was editing that jerk</text>
<sha1>ml80vmyjlixdstnywwihx003exfzq9j</sha1>
<model>wikitext</model>
<format>text/x-wiki</format>
</revision>
</page>
<page>
<title>File:Some image.jpg</title>
<ns>6</ns>
<id>3</id>
<revision>
<id>102</id>
<timestamp>2001-01-15T20:34:12Z</timestamp>
<contributor><username>Foobar</username><id>42</id></contributor>
<comment>My awesomeest image!</comment>
<text xml:space="preserve" bytes="52">This is an awesome little imgae. I lurves it. {{PD}}</text>
<sha1>mehom37npwkpzhaiwu3wyr0egalumki</sha1>
<model>wikitext</model>
<format>text/x-wiki</format>
</revision>
<upload>
<timestamp>2001-01-15T20:34:12Z</timestamp>
<contributor><username>Foobar</username><id>42</id></contributor>
<comment>My awesomeest image!</comment>
<filename>Some_image.jpg</filename>
<src>http://upload.wikimedia.org/commons/2/22/Some_image.jpg</src>
<size>12345</size>
</upload>
</page>
<!-- or a series of logitem records, but normaly page and logitem never exist both in one file -->
<logitem>
<id>15</id>
<timestamp>2008-10-23T03:20:32Z</timestamp>
<contributor>
<username>Wikimedian</username>
<id>12345</id>
</contributor>
<comment>content was: 'I think this was a silly edit'</comment>
<type>delete</type>
<action>delete</action>
<logtitle>Silly page name</logtitle>
<params xml:space="preserve" />
</logitem>
</mediawiki>

View file

@ -0,0 +1,67 @@
globals.txt
Globals are evil. The original MediaWiki code relied on globals for processing
context far too often. MediaWiki development since then has been a story of
slowly moving context out of global variables and into objects. Storing
processing context in object member variables allows those objects to be reused
in a much more flexible way. Consider the elegance of:
# Generate the article HTML as if viewed by a web request
$article = new Article( Title::newFromText( $t ) );
$article->view();
versus
# Save current globals
$oldTitle = $wgTitle;
$oldArticle = $wgArticle;
# Generate the HTML
$wgTitle = Title::newFromText( $t );
$wgArticle = new Article;
$wgArticle->view();
# Restore globals
$wgTitle = $oldTitle
$wgArticle = $oldArticle
Some of the current MediaWiki developers have an idle fantasy that some day,
globals will be eliminated from MediaWiki entirely, replaced by an application
object which would be passed to constructors. Whether that would be an
efficient, convenient solution remains to be seen, but certainly PHP 5 makes
such object-oriented programming models easier than they were in previous
versions.
For the time being though, MediaWiki programmers will have to work in an
environment with some global context. At the time of writing, 418 globals were
initialised on startup by MediaWiki. 304 of these were configuration settings,
which are documented in DefaultSettings.php. There is no comprehensive
documentation for the remaining 114 globals, however some of the most important
ones are listed below. They are typically initialised either in index.php or in
Setup.php.
For a description of the classes, see design.txt.
$wgTitle
Title object created from the request URL.
$wgOut
OutputPage object for HTTP response.
$wgUser
User object for the user associated with the current request.
$wgLang
Language object selected by user preferences.
$wgContLang
Language object associated with the wiki being viewed.
$wgParser
Parser object. Parser extensions register their hooks here.
$wgRequest
WebRequest object, to get request data
$wgMemc, $messageMemc, $parserMemc
Object caches

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,4 @@
This directory is for the auto-generated doxygen documentation.
Run 'php mwdocgen.php' in the maintenance subdirectory to build the docs.
Get Doxygen from http://www.doxygen.org/

View file

@ -0,0 +1,5 @@
language.txt
The Language object handles all readable text produced by the software.
See http://www.mediawiki.org/wiki/Localisation#General_use_.28for_developers.29

View file

@ -0,0 +1,24 @@
linkcache.txt
The LinkCache class maintains a list of article titles and the information about
whether or not the article exists in the database. This is used to mark up links
when displaying a page. If the same link appears more than once on any page,
then it only has to be looked up once. In most cases, link lookups are done in
batches with the LinkBatch class, or the equivalent in Parser::replaceLinkHolders(),
so the link cache is mostly useful for short snippets of parsed text (such as
the site notice), and for links in the navigation areas of the skin.
The link cache was formerly used to track links used in a document for the
purposes of updating the link tables. This application is now deprecated.
To create a batch, you can use the following code:
$pages = array( 'Main Page', 'Project:Help', /* ... */ );
$titles = array();
foreach( $pages as $page ){
$titles[] = Title::newFromText( $page );
}
$batch = new LinkBatch( $titles );
$batch->execute();

View file

@ -0,0 +1,95 @@
magicword.txt
Magic Words are some phrases used in the wikitext. They are used for two things:
* Variables (like {{PAGENAME}}, {{SERVER}}, ...): part of wikitext, that looks
like templates but that don't accept any parameter.
* Parser functions (like {{fullurl:...}}, {{#special:...}}): behaves like
functions and accepts parameters.
The localized arrays keys are the internal name, and the values are an array,
whose include their case-sensitivity and their alias forms. The first form
defined is used by the program, for example, when moving a page and its old name
should include #REDIRECT.
They can be added in several arrays:
* By adding a file to $wgExtensionMessagesFiles and defining there $magicWords.
This array is associative with the language code in the first dimension key
and then a "normal" array of magic words.
* Localized arrays (languages/messages/LanguageXX.php) include their different
names to be used by the users.
To add a new variable, you should use the "MagicWordwgVariableIDs" hook to add
the internal name to the $magicWords array. You'll need to define the value of
the variable with the "ParserGetVariableValueSwitch" hook.
For example to add a new variable:
Create a file called ExtensionName.i18n.magic.php with the following contents:
----
<?php
$magicWords = array();
$magicWords['en'] = array(
// Case sensitive.
'mag_custom' => array( 1, 'CUSTOM' ),
);
$magicWords['es'] = array(
'mag_custom' => array( 1, 'ADUANERO' ),
);
----
$wgExtensionMessagesFiles['ExtensionNameMagic'] = __DIR__ . '/ExtensionName.i18n.magic.php';
$wgHooks['MagicWordwgVariableIDs'][] = 'wfAddCustomMagicWordID';
$wgHooks['ParserGetVariableValueSwitch'][] = 'wfGetCustomMagicWordValue';
function wfAddCustomMagicWordID( &$magicWords ) {
$magicWords[] = 'mag_custom';
return true;
}
function wfGetCustomMagicWordValue( &$parser, &$varCache, &$index, &$ret ){
if( $index == 'mag_custom' ){
$ret = $varCache['mag_custom'] = "Custom value";
}
return true;
}
And to add a new parser function:
Create a file called ExtensionName.i18n.magic.php with the following contents:
----
<?php
$magicWords = array();
$magicWords['en'] = array(
// Case insensitive.
'mag_custom' => array( 0, 'custom' ),
);
$magicWords['es'] = array(
'mag_custom' => array( 0, 'aduanero' ),
);
----
$wgExtensionMessagesFiles['ExtensionNameMagic'] = __DIR__ . '/ExtensionName.i18n.magic.php';
$wgHooks['ParserFirstCallInit'][] = 'wfRegisterCustomMagicWord';
function wfRegisterCustomMagicWord( &$parser ){
$parser->setFunctionHook( 'mag_custom', 'wfGetCustomMagicWordValue' );
return true;
}
function wfGetCustomMagicWordValue( &$parser, $var1, $var2 ){
return "custom: var1 is $var1, var2 is $var2";
}
Note: the 'ParserFirstCallInit' hook is only aviable since 1.12. To work with
an older version, you'll need to use an extension function.
Online documentation (contains more informations):
Magic words: http://www.mediawiki.org/wiki/Manual:Magic_words
Variables: http://www.mediawiki.org/wiki/Manual:Variable
Parser functions: http://www.mediawiki.org/wiki/Manual:Parser_functions

View file

@ -0,0 +1,57 @@
Prior to version 1.16, maintenance scripts were a hodgepodge of code that
had no cohesion or formal method of action. Beginning in 1.16, maintenance
scripts have been cleaned up to use a unified class.
1. Directory structure
2. How to run a script
3. How to write your own
1. DIRECTORY STRUCTURE
The /maintenance directory of a MediaWiki installation contains several
subdirectories, all of which have unique purposes.
2. HOW TO RUN A SCRIPT
Ridiculously simple, just call 'php someScript.php' that's in the top-
level /maintenance directory.
Example:
php clearCacheStats.php
The following parameters are available to all maintenance scripts
--help : Print a help message
--quiet : Quiet non-error output
--dbuser : The database user to use for the script (if needed)
--dbpass : Same as above (if needed)
--conf : Location of LocalSettings.php, if not default
--wiki : For specifying the wiki ID
--batch-size : If the script supports batch operations, do this many per batch
3. HOW TO WRITE YOUR OWN
Make a file in the maintenance directory called myScript.php or something.
In it, write the following:
==BEGIN==
<?php
require_once 'Maintenance.php';
class DemoMaint extends Maintenance {
public function __construct() {
parent::__construct();
}
public function execute() {
}
}
$maintClass = "DemoMaint";
require_once RUN_MAINTENANCE_IF_MAIN;
==END==
That's it. In the execute() method, you have access to all of the normal
MediaWiki functions, so you can get a DB connection, use the cache, etc.
For full docs on the Maintenance class, see the auto-generated docs at
http://svn.wikimedia.org/doc/classMaintenance.html

View file

@ -0,0 +1,251 @@
MediaWiki has optional support for memcached, a "high-performance,
distributed memory object caching system". For general information
on it, see: http://www.danga.com/memcached/
Memcached is likely more trouble than a small site will need, but
for a larger site with heavy load, like Wikipedia, it should help
lighten the load on the database servers by caching data and objects
in memory.
== Installation ==
Packages are available for Fedora, Debian, Ubuntu and probably other
Linux distributions. If you there's no package available for your
distribution, you can compile it from source.
== Compilation ==
* PHP must be compiled with --enable-sockets
* libevent: http://www.monkey.org/~provos/libevent/
(as of 2003-08-11, 0.7a is current)
* optionally, epoll-rt patch for Linux kernel:
http://www.xmailserver.org/linux-patches/nio-improve.html
* memcached: http://www.danga.com/memcached/download.bml
(as of this writing, 1.1.9 is current)
Memcached and libevent are under BSD-style licenses.
The server should run on Linux and other Unix-like systems... you
can run multiple servers on one machine or on multiple machines on
a network; storage can be distributed across multiple servers, and
multiple web servers can use the same cache cluster.
********************* W A R N I N G ! ! ! ! ! ***********************
Memcached has no security or authentication. Please ensure that your
server is appropriately firewalled, and that the port(s) used for
memcached servers are not publicly accessible. Otherwise, anyone on
the internet can put data into and read data from your cache.
An attacker familiar with MediaWiki internals could use this to steal
passwords and email addresses, or to make themselves a sysop and
install malicious javascript on the site. There may be other types
of vulnerability, no audit has been done -- so be safe and keep it
behind a firewall.
********************* W A R N I N G ! ! ! ! ! ***********************
== Setup ==
If you installed memcached using a distro, the daemon should be started
automatically using /etc/init.d/memcached.
To start the daemon manually, use something like:
memcached -d -l 127.0.0.1 -p 11211 -m 64
(to run in daemon mode, accessible only via loopback interface,
on port 11211, using up to 64MB of memory)
In your LocalSettings.php file, set:
$wgMainCacheType = CACHE_MEMCACHED;
$wgMemCachedServers = array( "127.0.0.1:11211" );
The wiki should then use memcached to cache various data. To use
multiple servers (physically separate boxes or multiple caches
on one machine on a large-memory x86 box), just add more items
to the array. To increase the weight of a server (say, because
it has twice the memory of the others and you want to spread
usage evenly), make its entry a subarray:
$wgMemCachedServers = array(
"127.0.0.1:11211", # one gig on this box
array("192.168.0.1:11211", 2 ) # two gigs on the other box
);
== PHP client for memcached ==
MediaWiki uses a fork of Ryan T. Dean's pure-PHP memcached client.
The newer PECL module is supported.
MediaWiki uses three object for object caching:
* $wgMemc, controlled by $wgMainCacheType
* $parserMemc, controlled by $wgParserCacheType
* $messageMemc, controlled by $wgMessageCacheType
If you set CACHE_NONE to one of the three control variable, (default
value for $wgMainCacheType), MediaWiki still create a MemCacheClient,
but requests to it are no-ops and we always fall through to the
database. If the cache daemon can't be contacted, it should also
disable itself fairly smoothly.
By default, $wgMemc is used but when it is $parserMemc or $messageMemc
this is mentionned below.
== Keys used ==
(incomplete, out of date)
Date Formatter:
key: $wgDBname:dateformatter
ex: wikidb:dateformatter
stores: a single instance of the DateFormatter class
cleared by: nothing
expiry: one hour
Difference Engine:
key: $wgDBname:diff:version:{MW_DIFF_VERSION}:oldid:$old:newid:$new
ex: wikidb:diff:version:1.11a:oldid:1:newid:2
stores: body of a difference
cleared by: nothing
expiry: one week
Interwiki:
key: $wgDBname:interwiki:$prefix
ex: wikidb:interwiki:w
stores: object from the interwiki table of the database
expiry: $wgInterwikiExpiry
cleared by: nothing
Lag time of the databases:
key: $wgDBname:lag_times
ex: wikidb:lag_times
stores: array mapping the database id to its lag time
expriy: 5 secondes
cleared by: nothing
Localisation:
key: $wgDBname:localisation:$lang
ex: wikidb:localisation:de
stores: array of localisation settings
set in: Language::loadLocalisation()
expiry: none
cleared by: Language::loadLocalisation()
Message Cache:
stored in: $messageMemc
key: $wgDBname:messages, $wgDBname:messages-hash, $wgDBname:messages-status
ex: wikidb:messages, wikidb:messages-hash, wikidb:messages-status
stores: an array where the keys are DB keys and the values are messages
set in: wfMessage(), Article::editUpdates() and Title::moveTo()
expriy: $wgMsgCacheExpiry
cleared by: nothing
Newtalk:
key: $wgDBname:newtalk:ip:$ip
ex: wikidb:newtalk:ip:123.45.67.89
stores: integer, 0 or 1
set in: User::loadFromDatabase()
cleared by: User::saveSettings() # ?
expiry: 30 minutes
Parser Cache:
stored in: $parserMemc
controlled by: $wgEnableParserCache
key: $wgDBname:pcache:idhash:$pageid-$renderkey!$hash
$pageid: id of the page
$renderkey: 1 if action=render, 0 otherwise
$hash: hash of user options applied to the page, see ParserOptions::optionsHash()
ex: wikidb:pcache:idhash:1-0!1!0!!en!2
stores: ParserOutput object
modified by: WikiPage::doEditUpdates() or PoolWorkArticleView::doWork()
expiry: $wgParserCacheExpireTime or less if it contains short lived functions
key: $wgDBname:pcache:idoptions:$pageid
stores: CacheTime object with an additional list of used options for the hash,
serves as ParserCache pointer.
modified by: ParserCache::save()
expiry: The same as the ParserCache entry it points to.
Ping limiter:
controlled by: $wgRateLimits
key: $wgDBname:limiter:action:$action:ip:$ip,
$wgDBname:limiter:action:$action:user:$id,
mediawiki:limiter:action:$action:ip:$ip and
mediawiki:limiter:action:$action:subnet:$sub
ex: wikidb:limiter:action:edit:ip:123.45.67.89,
wikidb:limiter:action:edit:user:1012
mediawiki:limiter:action:edit:ip:123.45.67.89 and
mediawiki:limiter:action:$action:subnet:123.45.67
stores: number of action made by user/ip/subnet
cleared by: nothing
expiry: expiry set for the action and group in $wgRateLimits
Proxy Check: (deprecated)
key: $wgDBname:proxy:ip:$ip
ex: wikidb:proxy:ip:123.45.67.89
stores: 1 if the ip is a proxy
cleared by: nothing
expiry: $wgProxyMemcExpiry
Revision text:
key: $wgDBname:revisiontext:textid:$id
ex: wikidb:revisiontext:textid:1012
stores: text of a revision
cleared by: nothing
expriry: $wgRevisionCacheExpiry
Sessions:
controlled by: $wgSessionsInObjectCache
key: $wgBDname:session:$id
ex: wikidb:session:38d7c5b8d3bfc51egf40c69bc40f8be3
stores: $SESSION, useful when using a multi-sever wiki
expriy: one hour
cleared by: session_destroy()
Sidebar:
stored in: $parserMemc
controlled by: $wgEnableSidebarCache
key: $wgDBname:sidebar
ex: wikidb:sidebar
stores: the html output of the sidebar
expriy: $wgSidebarCacheExpiry
cleared by: MessageCache::replace()
Special:Allpages:
key: $wgDBname:allpages:ns:$ns
ex: wikidb:allpages:ns:0
stores: array of pages in a namespace
expiry: one hour
cleared by: nothing
Special:Recentchanges (feed):
stored in: $messageMemc
key: $wgDBname:rcfeed:$format:$limit:$hideminor:$target and
rcfeed:$format:timestamp
ex: wikidb:rcfeed:rss:50:: and rcfeed:rss:timestamp
stores: xml output of feed
expiry: one day
clear by: maintenance/rebuildrecentchanges.php script, or
calling Special:Recentchanges?action=purge&feed=rss,
Special:Recentchanges?action=purge&feed=atom,
but note need $wgGroupPermissions[...]['purge'] permission.
Statistics:
controlled by: $wgStatsMethod
key: $wgDBname:stats:$key
ex: wikibd:stats:request_with_session
stores: counter for statistics (see maintenance/showCacheStats.php script)
expiry: none (?)
cleared by: maintenance/clearCacheStats.php script
User:
key: $wgDBname:user:id:$sId
ex: wikidb:user:id:51
stores: instance of class User
set in: User::saveToCache()
cleared by: User::saveSettings(), User::clearSharedCache()
... more to come ...

View file

@ -0,0 +1,28 @@
09 Oct 2003:
1455 UTC:
Released version 0.1.2
Fixed bug in get_multi; when debugging was enabled but no keys were fetched,
script execution would halt (uninitialized $val)
08 Oct 2003:
1848 UTC:
Released version 0.1.1
1825 UTC:
Fixed bug in memcached::memcached; was attempting to initialize
memcached::_dead_sock (function) instead of memcached::_dead_hosts
(oops!)
1812 UTC:
Fixed memcached::enable_compression;
thanks to Justin Matlock <jmat@shutdown.net> for pointing it out
07 Oct 2003:
1635 UTC:
Fixed call to memcached::_dead_sock in memcached::delete
Added documentation for class variable $_buckets
06 Oct 2003:
2039 UTC:
Initial release of memcached-client-php; version 0.1

View file

@ -0,0 +1,258 @@
Ryan Gilfether <hotrodder@rocketmail.com>
http://www.gilfether.com
This module is Copyright (c) 2003 Ryan Gilfether.
All rights reserved.
You may distribute under the terms of the GNU General Public License
This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
See the memcached website: http://www.danga.com/memcached/
// Takes one parameter, a array of options. The most important key is
// options["servers"], but that can also be set later with the set_servers()
// method. The servers must be an array of hosts, each of which is
// either a scalar of the form <10.0.0.10:11211> or an array of the
// former and an integer weight value. (the default weight if
// unspecified is 1.) It's recommended that weight values be kept as low
// as possible, as this module currently allocates memory for bucket
// distribution proportional to the total host weights.
// $options["debug"] turns the debugging on if set to true
MemCachedClient::MemCachedClient($options);
// sets up the list of servers and the ports to connect to
// takes an array of servers in the same format as in the constructor
MemCachedClient::set_servers($servers);
// Retrieves a key from the memcache. Returns the value (automatically
// unserialized, if necessary) or FALSE if it fails.
// The $key can optionally be an array, with the first element being the
// hash value, if you want to avoid making this module calculate a hash
// value. You may prefer, for example, to keep all of a given user's
// objects on the same memcache server, so you could use the user's
// unique id as the hash value.
// Possible errors set are:
// MC_ERR_GET
MemCachedClient::get($key);
// just like get(), but takes an array of keys, returns FALSE on error
// Possible errors set are:
// MC_ERR_NOT_ACTIVE
MemCachedClient::get_multi($keys)
// Unconditionally sets a key to a given value in the memcache. Returns true
// if it was stored successfully.
// The $key can optionally be an arrayref, with the first element being the
// hash value, as described above.
// returns TRUE on success else FALSE
// Possible errors set are:
// MC_ERR_NOT_ACTIVE
// MC_ERR_GET_SOCK
// MC_ERR_SOCKET_WRITE
// MC_ERR_SOCKET_READ
// MC_ERR_SET
MemCachedClient::set($key, $value, $exptime);
// Like set(), but only stores in memcache if the key doesn't already exist.
// returns TRUE on success else FALSE
// Possible errors set are:
// MC_ERR_NOT_ACTIVE
// MC_ERR_GET_SOCK
// MC_ERR_SOCKET_WRITE
// MC_ERR_SOCKET_READ
// MC_ERR_SET
MemCachedClient::add($key, $value, $exptime);
// Like set(), but only stores in memcache if the key already exists.
// returns TRUE on success else FALSE
// Possible errors set are:
// MC_ERR_NOT_ACTIVE
// MC_ERR_GET_SOCK
// MC_ERR_SOCKET_WRITE
// MC_ERR_SOCKET_READ
// MC_ERR_SET
MemCachedClient::replace($key, $value, $exptime);
// removes the key from the MemCache
// $time is the amount of time in seconds (or Unix time) until which
// the client wishes the server to refuse "add" and "replace" commands
// with this key. For this amount of item, the item is put into a
// delete queue, which means that it won't possible to retrieve it by
// the "get" command, but "add" and "replace" command with this key
// will also fail (the "set" command will succeed, however). After the
// time passes, the item is finally deleted from server memory.
// The parameter $time is optional, and, if absent, defaults to 0
// (which means that the item will be deleted immediately and further
// storage commands with this key will succeed).
// returns TRUE on success else returns FALSE
// Possible errors set are:
// MC_ERR_NOT_ACTIVE
// MC_ERR_GET_SOCK
// MC_ERR_SOCKET_WRITE
// MC_ERR_SOCKET_READ
// MC_ERR_DELETE
MemCachedClient::delete($key, $time = 0);
// Sends a command to the server to atomically increment the value for
// $key by $value, or by 1 if $value is undefined. Returns FALSE if $key
// doesn't exist on server, otherwise it returns the new value after
// incrementing. Value should be zero or greater. Overflow on server
// is not checked. Be aware of values approaching 2**32. See decr.
// Possible errors set are:
// MC_ERR_NOT_ACTIVE
// MC_ERR_GET_SOCK
// MC_ERR_SOCKET_WRITE
// MC_ERR_SOCKET_READ
// returns new value on success, else returns FALSE
// ONLY WORKS WITH NUMERIC VALUES
MemCachedClient::incr($key[, $value]);
// Like incr, but decrements. Unlike incr, underflow is checked and new
// values are capped at 0. If server value is 1, a decrement of 2
// returns 0, not -1.
// Possible errors set are:
// MC_ERR_NOT_ACTIVE
// MC_ERR_GET_SOCK
// MC_ERR_SOCKET_WRITE
// MC_ERR_SOCKET_READ
// returns new value on success, else returns FALSE
// ONLY WORKS WITH NUMERIC VALUES
MemCachedClient::decr($key[, $value]);
// disconnects from all servers
MemCachedClient::disconnect_all();
// if $do_debug is set to true, will print out
// debugging info, else debug is turned off
MemCachedClient::set_debug($do_debug);
// remove all cached hosts that are no longer good
MemCachedClient::forget_dead_hosts();
// When a function returns FALSE, an error code is set.
// This funtion will return the error code.
// See error_string()
// returns last error code set
MemCachedClient::error()
// Returns a string describing the error set in error()
// See error()
// returns a string describing the error code given
MemCachedClient::error_string()
// Resets the error number and error string
MemCachedClient::error_clear()
Error codes are as follows:
MC_ERR_NOT_ACTIVE // no active servers
MC_ERR_SOCKET_WRITE // socket_write() failed
MC_ERR_SOCKET_READ // socket_read() failed
MC_ERR_SOCKET_CONNECT // failed to connect to host
MC_ERR_DELETE // delete() did not recieve DELETED command
MC_ERR_HOST_FORMAT // sock_to_host() invalid host format
MC_ERR_HOST_DEAD // sock_to_host() host is dead
MC_ERR_GET_SOCK // get_sock() failed to find a valid socket
MC_ERR_SET // _set() failed to receive the STORED response
MC_ERR_LOADITEM_HEADER // _load_items failed to receive valid data header
MC_ERR_LOADITEM_END // _load_items failed to receive END response
MC_ERR_LOADITEM_BYTES // _load_items bytes read larger than bytes available
MC_ERR_GET // failed to get value associated with key
// Turns compression on or off; 0=off, 1=on
MemCacheClient::set_compression($setting)
EXAMPLE:
<?php
require 'MemCachedClient.inc.php';
// set the servers, with the last one having an integer weight value of 3
$options["servers"] = array("10.0.0.15:11000","10.0.0.16:11001",array("10.0.0.17:11002", 3));
$options["debug"] = false;
$memc = new MemCachedClient($options);
/***********************
* STORE AN ARRAY
***********************/
$myarr = array("one","two", 3);
$memc->set("key_one", $myarr);
$val = $memc->get("key_one");
print $val[0]."\n"; // prints 'one'
print $val[1]."\n"; // prints 'two'
print $val[2]."\n"; // prints 3
print "\n";
/***********************
* STORE A CLASS
***********************/
class tester
{
var $one;
var $two;
var $three;
}
$t = new tester;
$t->one = "one";
$t->two = "two";
$t->three = 3;
$memc->set("key_two", $t);
$val = $memc->get("key_two");
print $val->one."\n";
print $val->two."\n";
print $val->three."\n";
print "\n";
/***********************
* STORE A STRING
***********************/
$memc->set("key_three", "my string");
$val = $memc->get("key_three");
print $val; // prints 'my string'
$memc->delete("key_one");
$memc->delete("key_two");
$memc->delete("key_three");
$memc->disconnect_all();
print "\n";
/***********************
* STORE A BINARY FILE
***********************/
// first read the file and save it in memcache
$fp = fopen( "./image.jpg", "rb" ) ;
if ( !$fp )
{
print "Could not open ./file.dat!\n" ;
exit ;
}
$data = fread( $fp, filesize( "./image.jpg" ) ) ;
fclose( $fp ) ;
print "Data length is " . strlen( $data ) . "\n" ;
$memc->set( "key", $data ) ;
// now open a file for writing and write the data
// retrieved from memcache
$fp = fopen("./test.jpg","wb");
$data = $memc->get( "key" ) ;
print "Data length is " . strlen( $data ) . "\n" ;
fwrite($fp,$data,strlen( $data ));
fclose($fp);
?>

View file

@ -0,0 +1 @@
HTML documentation is under http://phpca.cytherianage.net/memcached/doc/

View file

@ -0,0 +1,9 @@
The most up-to-date schema for the tables in the database
will always be "tables.sql" in the maintenance directory,
which is called from the installation script.
That file has been commented with details of the usage for
each table and field.
Historical information and some other notes are available at
http://www.mediawiki.org/wiki/Manual:Database_layout

View file

@ -0,0 +1,55 @@
scripts.txt
MediaWiki primary scripts are in the root directory of the software. Users
should only use these scripts to access the wiki. There are also some .php that
aren't primary scripts but helper files and won't work if they are accessed
directly by the web.
Primary scripts:
index.php
Main access point. It handles the most of requests.
See http://www.mediawiki.org/wiki/Manual:Index.php
api.php
Script to provide an API for bots to fetch content and informations about
the site and also modify it. See http://www.mediawiki.org/wiki/API
for more informations.
img_auth.php
Script that only serve images to logged in users. To configure the wiki
to use that script, see http://www.mediawiki.org/wiki/Manual:Image_Authorisation.
load.php
Used by ResourceLoader to serve minified, concatenated and gzipped CSS and JS.
opensearch_desc.php
Returns a OpenSearch description document (see http://www.opensearch.org/)
that points to the search engines of the wiki.
profileinfo.php
Allow users to see the profiling information that are stored in the
database.
To save the profiling information in the database (required to use this
script), you have to modify StartProfiler.php to use the Profiler class and
not the stub profiler which is enabled by default.
You will also need to set $wgProfileToDatabase to true in LocalSettings.php
to force the profiler to save the informations in the database and apply the
maintenance/archives/patch-profiling.sql patch to the database.
To enable the profileinfo.php itself, you'll need to set $wgDBadminuser
and $wgDBadminpassword in your LocalSettings.php, as well as $wgEnableProfileInfo
See also http://www.mediawiki.org/wiki/How_to_debug#Profiling.
thumb.php
Script used to resize images if it is configured to be done when the web
browser requests the image and not when generating the page. This script can
be used as a 404 handler to generate image thumbs when they don't exist.
There is also a file with a .php5 extension for each script. They can be used if
the web server needs a .php5 to run the file with the PHP 5 engine and runs .php
scripts with PHP 4. To use these files, you have to modify $wgScriptExtension to
'.php5' is LocalSettings.php but it is already done by the config script if you
used mw-config/index.php5 for installation.

View file

@ -0,0 +1,92 @@
skin.txt
MediaWiki includes four core skins:
* Vector: The default skin. Introduced in the 1.16 release (2010), it has been
set as the default in MediaWiki since the 1.17 release (2011), replacing
Monobook.
* Monobook: Named after the black-and-white photo of a book in the page
background. Introduced in the 2004 release of 1.3, it had been been the
default skin since then, before being replaced by Vector.
* Modern: An attractive blue/grey theme with sidebar and top bar. Derived from
Monobook.
* Cologne Blue: A lightweight skin with minimal formatting. The oldest of the
currently bundled skins, largely rewritten in 2012 while keeping its
appearance.
Several legacy skins were removed in the 1.22 release, as the burden of
supporting them became too heavy to bear. Those were:
* Standard (a.k.a. Classic): The old default skin written by Lee Crocker during
the phase 3 rewrite, in 2002.
* Nostalgia: A skin which looks like Wikipedia did in its first year (2001).
This skin is now used for the old Wikipedia snapshot at
http://nostalgia.wikipedia.org/
* Chick: A lightweight Monobook skin with no sidebar. The sidebar links were
given at the bottom of the page instead.
* Simple: A lightweight skin with a simple white-background sidebar and no top
bar.
* MySkin: Essentially Monobook without the CSS. The idea was that it could be
customised using user-specific or site-wide CSS (see below).
== Custom CSS/JS ==
It is possible to customise the site CSS and JavaScript without editing any
server-side source files. This is done by editing some pages on the wiki:
* [[MediaWiki:Common.css]] -- for skin-independent CSS
* [[MediaWiki:Common.js]] -- for skin-independent JavaScript
* [[MediaWiki:Vector.css]], [[MediaWiki:Monobook.css]], etc. -- for
skin-dependent CSS
* [[MediaWiki:Vector.js]], [[MediaWiki:Monobook.js]], etc. -- for
skin-dependent JavaScript
These can also be customised on a per-user basis, by editing
[[User:<name>/vector.css]], [[User:<name>/vector.js]], etc.
This feature has led to a wide variety of "user styles" becoming available:
http://www.mediawiki.org/wiki/Manual:Gallery_of_user_styles
If you want a different look for your wiki, that gallery is a good place to start.
== Drop-in custom skins ==
If you put a file in MediaWiki's skins directory, ending in .php, the name of
the file will automatically be added as a skin name, and the file will be
expected to contain a class called Skin<name> with the skin class. You can then
make that skin the default by adding to LocalSettings.php:
$wgDefaultSkin = '<name>';
You can also disable dropped-in or core skins using:
$wgSkipSkins[] = '<name>';
This technique is used by the more ambitious MediaWiki site operators, to
create complex custom skins for their wikis. It should be preferred over
editing the core Monobook skin directly.
See http://www.mediawiki.org/wiki/Manual:Skinning for more information.
== Extension skins ==
It is now possible (since MediaWiki 1.12) to write a skin as a standard
MediaWiki extension, enabled via LocalSettings.php. This is done by adding
it to $wgValidSkinNames, for example:
$wgValidSkinNames['mycoolskin'] = 'MyCoolSkin';
and then registering a class in $wgAutoloadClasses called SkinMycoolSkin, which
derives from Skin. This technique is apparently not yet used (as of 2008)
outside the DumpHTML extension.

View file

@ -0,0 +1,67 @@
title.txt
The MediaWiki software's "Title" class represents article titles, which are used
for many purposes: as the human-readable text title of the article, in the URL
used to access the article, the wikitext link to the article, the key into the
article database, and so on. The class in instantiated from one of these forms
and can be queried for the others, and for other attributes of the title. This
is intended to be an immutable "value" class, so there are no mutator functions.
To get a new instance, call Title::newFromText(). Once instantiated, the
non-static accessor methods can be used, such as getText(), getDBkey(),
getNamespace(), etc. Note that Title::newFromText() may return false if the text
is illegal according to the rules below.
The prefix rules: a title consists of an optional interwiki prefix (such as "m:"
for meta or "de:" for German), followed by an optional namespace, followed by
the remainder of the title. Both interwiki prefixes and namespace prefixes have
the same rules: they contain only letters, digits, space, and underscore, must
start with a letter, are case insensitive, and spaces and underscores are
interchangeable. Prefixes end with a ":". A prefix is only recognized if it is
one of those specifically allowed by the software. For example, "de:name" is a
link to the article "name" in the German Wikipedia, because "de" is recognized
as one of the allowable interwikis. The title "talk:name" is a link to the
article "name" in the "talk" namespace of the current wiki, because "talk" is a
recognized namespace. Both may be present, and if so, the interwiki must
come first, for example, "m:talk:name". If a title begins with a colon as its
first character, no prefixes are scanned for, and the colon is just removed.
Note that because of these rules, it is possible to have articles with colons in
their names. "E. Coli 0157:H7" is a valid title, as is "2001: A Space Odyssey",
because "E. Coli 0157" and "2001" are not valid interwikis or namespaces.
It is not possible to have an article whose bare name includes a namespace or
interwiki prefix.
An initial colon in a title listed in wiki text may however suppress special
handling for interlanguage links, image links, and category links. It is also
used to indicate the main namespace in template inclusions.
Once prefixes have been stripped, the rest of the title processed this way:
* Spaces and underscores are treated as equivalent and each is converted to the
other in the appropriate context (underscore in URL and database keys, spaces
in plain text).
* Multiple consecutive spaces are converted to a single space.
* Leading or trailing space is removed.
* If $wgCapitalLinks is enabled (the default), the first letter is capitalised,
using the capitalisation function of the content language object.
* The unicode characters LRM (U+200E) and RLM (U+200F) are silently stripped.
* Invalid UTF-8 sequences or instances of the replacement character (U+FFFD) are
considered illegal.
* A percent sign followed by two hexadecimal characters is illegal
* Anything that looks like an XML/HTML character reference is illegal
* Any character not matched by the $wgLegalTitleChars regex is illegal
* Zero-length titles (after whitespace stripping) are illegal
All titles except special pages must be less than 255 bytes when encoded with
UTF-8, because that is the size of the database field. Special page titles may
be up to 512 bytes.
Note that Unicode Normal Form C (NFC) is enforced by MediaWiki's user interface
input functions, and so titles will typically be in this form.
getArticleID() needs some explanation: for "internal" articles, it should return
the "page_id" field if the article exists, else it returns 0. For all external
articles it returns 0. All of the IDs for all instances of Title created during
a request are cached, so they can be looked up quickly while rendering wiki text
with lots of internal links. See linkcache.txt.

View file

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html>
<head>
<title>CSS Child selector emulation for IE 6</title>
<style>
/** Common rules **/
body { background-color: #CCC; }
table { border:1px black solid; }
caption {
background-color: #98fb98;
border:1px solid #40FF40;
}
/** "old" rules" **/
table.global th,
table.global td
{
border: 1px red solid;
background-color:white;
padding:1em;
}
table.global th
{
background-color: #ffc0cb;
}
/** second table. Try to emulate child selector */
table.childemu th,
table.childemu td {
border: 1px red solid;
background-color:white;
padding:1em;
}
table.childemu th
{
background-color: #ffc0cb;
}
/** Reset style applied in childemu classes */
/** TODO: find the real default value!! */
table.childemu tr * th,
table.childemu tr * td {
border: none;
background-color: transparent;
padding: 0;
}
/** child selector emulation */
</style>
</head>
<body>
<p>
The following table show how nested tables inherit colors from the wikitable class (here it was renamed "global").
</p>
<table class="global">
<caption>Global table</caption>
<tr>
<th>TH: should have pink bg</th>
</tr>
<tr>
<td>TD: white bg</td>
</tr>
<tr>
<td>
<table class="nested">
<caption>Nested table</caption>
<tr>
<th>Nested TH: transparent</th>
<td>Nested TD: transparent</td>
</tr>
</table>
</td>
</tr>
</table>
<p>
With child selector we could limit the wikitable styling to the direct childs of the table. Unfortunately, Internet Explorer 6.0 does not support child selector. See <a href="https://bugzilla.wikimedia.org/show_bug.cgi?id=33752">our bug #33752</a>.
</p>
<table class="childemu">
<caption>Global table</caption>
<tr>
<th>TH: should have pink bg</th>
</tr>
<tr>
<td>TD: white bg</td>
</tr>
<tr>
<td>
<table class="nested">
<caption>Nested table</caption>
<tr>
<th>Nested TH: transparent</th>
<td>Nested TD: transparent</td>
</tr>
</table>
</td>
</tr>
</table>
<p><strong>NOTE:</strong>The nested caption keep the green background. The nested table keep the black border. This is because those declarations are global so we did not reset them.</p>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<link rel="stylesheet" href="../../skins/common/shared.css">
</head>
<body style="font-size: small;">
<h2>Messages</h2>
<p class="success">Success message</p>
<p class="warning">Warning message</p>
<p class="error">Error message</p>
<h2>Messages box</h2>
<p class="visualClear successbox">Success message</p>
<p class="visualClear warningbox">Warning message</p>
<p class="visualClear errorbox">Error message</p>
<br class="visualClear"/>
<h2>Various</h2>
<span class="comment">span.comment</span>
<a class="feedlink">a.feedlink</a>
<table class="wikitable">
<tr><th colspan="2">table.wikitable</th></tr>
<tr><td>cell</td><td>cell</td></tr>
<tr><td>cell</td><td>cell</td></tr>
</table>
<table class="mw-datatable">
<tr><th colspan="2">table.mw-datatable</th></tr>
<tr><td>line with hover</td><td>line with hover</td></tr>
<tr><td>line with hover</td><td>line with hover</td></tr>
</table>
</body>
</html>

View file

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<link rel="stylesheet" href="../../resources/mediawiki.action/mediawiki.action.history.diff.css">
</head>
<body style="background-color: #C0C0C0;">
<p>
This show various styles for our diff action, the background being hardcoded to gray (<code>#C0C0C0</code>) The reference style sheet is:</p>
<p>
<code><a href="../../resources/mediawiki.action/mediawiki.action.history.diff.css">resources/mediawiki.action/mediawiki.action.history.diff.css</a></code>.
</p>
<p>
This file might help us fix our diff colors which have been a recurring issues among the community for a loooong time.</p>
<p>
First, show the diff mostly like it would be chown on a wiki</p>
<table class="diff">
<tr>
<td class="diff-marker">-</td>
<td class="diff-deletedline"><div>
Some content <span class="diffchange diffchange-inline">deleted / replaced</span>
</div></td>
<td class="diff-marker">+</td>
<td class="diff-addedline"><div>
Some content <span class="diffchange diffchange-inline">added / replacement</span>
</div></td>
</tr>
</table>
<p>
Below are some basic lines being applied one or two classes. Mainly for debugging purposes</p>
<table class="diff">
<tr><th>Diff</th></tr>
<tr><td class="diff-addedline"><code>diff-addedline</code>: added line</td></tr>
<tr><td class="diff-deletedline"><code>diff-deletedline</code>: deleted line</td></tr>
<tr><td class="diff-context"><code>diff-context</code>: context</td></tr>
<tr><th>Same as above with a <code>&lt;span&gt;</code> child element having the <code>diffchange</code> class</th></tr>
<tr><td class="diffchange">Diffchange</td></tr>
<tr><td class="diff-addedline">
<span class="diffchange">Added line + diffchange</span>
</td></tr>
<tr><td class="diff-deletedline">
<span class="diffchange">Deleted line + diffchange</span>
</td></tr>
<tr><td class="diff-context">
<span class="diffchange">Context + diffchange</span>
</td></tr>
</table>
</body>
</html>

View file

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<style>
pre {
border: 1px dashed #AAA;
background-color: #E0E0E0;
color: #000000;
margin: 1em 10%;
padding: 0.5em;
}
blockquote {
font-style: italic;
}
</style>
</head>
<body>
<p>
This page let you test the rendering font-family declaration for monospaced fonts. TODO: add some references here :-)
</p>
<p>
Erwin Dokter had the following explanation on <a href="https://bugzilla.wikimedia.org/33496">Bugzilla #33496</a>:
</p>
<blockquote>
By default, a (Windows) browser has it's default font-sizes set at 16px for
serif and sans-serif, and 13px for monospace. Except in IE, where you cannot
set any font-sizes... it uses 16px for all fonts.
<br/>
Vector has a base font-size of 0.8em, and most browsers *correctly* scale down
all fonts, including the monospace font, accordingly. So monospace is shown at
0.8 x 13px = 10px (which is perceived as 'too small'). But when you assign any
font besides just "monospace", those browsers will no longer treat it as
monospace and use 0.8 x 16px = 13px instead.
</blockquote>
<p>
Below are various rendering:
</p>
<pre style='
font-family: monospace;'>
font-family: monospace;
</pre>
<pre style='
font-family: "Courier New";'>
font-family: "Courier New";
</pre>
<pre style='
font-family: Courier;'>
font-family: Courier;
</pre>
<pre style='
font-family: monospace, monospace;'>
font-family: monospace, monospace;
</pre>
<pre style='
font-family: monospace, "Courier New";'>
font-family: monospace, "Courier New";
</pre>
<pre style='
font-family: monospace, Courier;'>
font-family: monospace, Courier;
</pre>
<pre style='
font-family: monospace, Verdana;'>
font-family: monospace, Verdana;
</pre>
<pre style='
font-family: monospace, DOESNOTEXISTREALLY;'>
font-family: monospace, DOESNOTEXISTREALLY;
</pre>

View file

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<style>
/** This is just for coloring: */
table { border: 1px solid #CC0; }
td { border: 1px solid #CCC; }
table {
width: 100%;
table-layout: fixed;
}
#first {
width: 300px;
}
</style>
</head>
<body>
<p>
This play with table-layout:fixed; and applying the width to colgroup or col element. Firefox only recongize the width if it is applied on col element!</p>
<p>
On a perfect browser, both tables should look the same</p>
<dl>
<dt>colgroup</dt>
<dd>300 px width is applied to the first colgroup element</dd>
</dl>
<div style="width: 400px;">
<table>
<colgroup id="first" /></colgroup>
<colgroup id="second"/></colgroup>
<colgroup id="third" /></colgroup>
<tr>
<td>Very long?</td>
<td>#</td>
<td>$</td>
</tr>
</table>
</div>
<dl>
<dt>col</dt>
<dd>Each colgroup has an additional col element. The first col element is applied the 300 px width</dd>
</dl>
<div style="width: 400px;">
<table>
<colgroup><col id="first" /></colgroup>
<colgroup><col id="second"/></colgroup>
<colgroup><col id="third" /></colgroup>
<tr>
<td>Very long?</td>
<td>#</td>
<td>$</td>
</tr>
</table>
</div>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,119 @@
<?php
if ( ! defined( 'MEDIAWIKI' ) )
die();
/**#@+
* A parser extension that adds two tags, <ref> and <references> for adding
* citations to pages
*
* @file
* @ingroup Extensions
*
* @link http://www.mediawiki.org/wiki/Extension:Cite/Cite.php Documentation
*
* @bug 4579
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
*/
$wgHooks['ParserFirstCallInit'][] = 'wfCite';
$wgHooks['BeforePageDisplay'][] = 'wfCiteBeforePageDisplay';
$wgExtensionCredits['parserhook'][] = array(
'path' => __FILE__,
'name' => 'Cite',
'author' => array( 'Ævar Arnfjörð Bjarmason', 'Marius Hoch' ),
'descriptionmsg' => 'cite-desc',
'url' => 'https://www.mediawiki.org/wiki/Extension:Cite/Cite.php'
);
$wgParserTestFiles[] = __DIR__ . "/citeParserTests.txt";
$wgExtensionMessagesFiles['Cite'] = __DIR__ . "/Cite.i18n.php";
$wgAutoloadClasses['Cite'] = __DIR__ . "/Cite_body.php";
$wgSpecialPageGroups['Cite'] = 'pagetools';
define( 'CITE_DEFAULT_GROUP', '' );
/**
* The emergency shut-off switch. Override in local settings to disable
* groups; or remove all references from this file to enable unconditionally
*/
$wgAllowCiteGroups = true;
/**
* An emergency optimisation measure for caching cite <references /> output.
*/
$wgCiteCacheReferences = false;
/**
* Enables experimental popups
*/
$wgCiteEnablePopups = false;
/**
* Performs the hook registration.
* Note that several extensions (and even core!) try to detect if Cite is
* installed by looking for wfCite().
*
* @param $parser Parser
*
* @return bool
*/
function wfCite( $parser ) {
return Cite::setHooks( $parser );
}
// Resources
$citeResourceTemplate = array(
'localBasePath' => __DIR__ . '/modules',
'remoteExtPath' => 'Cite/modules'
);
$wgResourceModules['ext.cite'] = $citeResourceTemplate + array(
'scripts' => 'ext.cite.js',
'styles' => 'ext.cite.css',
'messages' => array(
'cite_references_link_accessibility_label',
'cite_references_link_many_accessibility_label',
),
);
$wgResourceModules['ext.cite.popups'] = $citeResourceTemplate + array(
'scripts' => 'ext.cite.popups.js',
'position' => 'bottom',
'dependencies' => array(
'jquery.tooltip',
),
);
$wgResourceModules['jquery.tooltip'] = $citeResourceTemplate + array(
'styles' => 'jquery.tooltip/jquery.tooltip.css',
'scripts' => 'jquery.tooltip/jquery.tooltip.js',
'position' => 'bottom',
);
/* Add RTL fix for the cite <sup> elements */
$wgResourceModules['ext.rtlcite'] = $citeResourceTemplate + array(
'styles' => 'ext.rtlcite.css',
'position' => 'top',
);
/**
* @param $out OutputPage
* @param $sk Skin
* @return bool
*/
function wfCiteBeforePageDisplay( $out, &$sk ) {
global $wgCiteEnablePopups;
$out->addModules( 'ext.cite' );
if ( $wgCiteEnablePopups ) {
$out->addModules( 'ext.cite.popups' );
}
/* RTL support quick-fix module */
$out->addModuleStyles( 'ext.rtlcite' );
return true;
}
/**#@-*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,420 @@
<?php
/**
* Aliases for Special:Cite
*
* @file
* @ingroup Extensions
*/
// @codingStandardsIgnoreFile
$specialPageAliases = array();
/** English (English) */
$specialPageAliases['en'] = array(
'Cite' => array( 'Cite' ),
);
/** Arabic (العربية) */
$specialPageAliases['ar'] = array(
'Cite' => array( 'استشهاد' ),
);
/** Egyptian Spoken Arabic (مصرى) */
$specialPageAliases['arz'] = array(
'Cite' => array( 'استشهاد' ),
);
/** Assamese (অসমীয়া) */
$specialPageAliases['as'] = array(
'Cite' => array( 'উদ্ধৃতি' ),
);
/** Bikol Central (Bikol Central) */
$specialPageAliases['bcl'] = array(
'Cite' => array( 'Sambitón' ),
);
/** Bulgarian (български) */
$specialPageAliases['bg'] = array(
'Cite' => array( 'Цитиране' ),
);
/** Banjar (Bahasa Banjar) */
$specialPageAliases['bjn'] = array(
'Cite' => array( 'Juhut' ),
);
/** Breton (brezhoneg) */
$specialPageAliases['br'] = array(
'Cite' => array( 'Menegiñ' ),
);
/** Bosnian (bosanski) */
$specialPageAliases['bs'] = array(
'Cite' => array( 'Citiraj' ),
);
/** буряад (буряад) */
$specialPageAliases['bxr'] = array(
'Cite' => array( 'Сайт' ),
);
/** Catalan (català) */
$specialPageAliases['ca'] = array(
'Cite' => array( 'Citau', 'Citeu' ),
);
/** Min Dong Chinese (Mìng-dĕ̤ng-ngṳ̄) */
$specialPageAliases['cdo'] = array(
'Cite' => array( '註' ),
);
/** Chechen (нохчийн) */
$specialPageAliases['ce'] = array(
'Cite' => array( 'Дош' ),
);
/** Czech (česky) */
$specialPageAliases['cs'] = array(
'Cite' => array( 'Citovat' ),
);
/** Danish (dansk) */
$specialPageAliases['da'] = array(
'Cite' => array( 'Citer' ),
);
/** German (Deutsch) */
$specialPageAliases['de'] = array(
'Cite' => array( 'Zitierhilfe', 'Zitieren' ),
);
/** Zazaki (Zazaki) */
$specialPageAliases['diq'] = array(
'Cite' => array( 'Sita' ),
);
/** Lower Sorbian (dolnoserbski) */
$specialPageAliases['dsb'] = array(
'Cite' => array( 'Citěrowańska_pomoc' ),
);
/** Greek (Ελληνικά) */
$specialPageAliases['el'] = array(
'Cite' => array( 'Παραπομπή' ),
);
/** Esperanto (Esperanto) */
$specialPageAliases['eo'] = array(
'Cite' => array( 'Citi' ),
);
/** Spanish (español) */
$specialPageAliases['es'] = array(
'Cite' => array( 'Citar' ),
);
/** Estonian (eesti) */
$specialPageAliases['et'] = array(
'Cite' => array( 'Tsiteerimine' ),
);
/** Persian (فارسی) */
$specialPageAliases['fa'] = array(
'Cite' => array( 'یادکرد' ),
);
/** Finnish (suomi) */
$specialPageAliases['fi'] = array(
'Cite' => array( 'Viittaus' ),
);
/** French (français) */
$specialPageAliases['fr'] = array(
'Cite' => array( 'Citer' ),
);
/** Franco-Provençal (arpetan) */
$specialPageAliases['frp'] = array(
'Cite' => array( 'Citar' ),
);
/** Galician (galego) */
$specialPageAliases['gl'] = array(
'Cite' => array( 'Cita', 'Citar' ),
);
/** Swiss German (Alemannisch) */
$specialPageAliases['gsw'] = array(
'Cite' => array( 'Zitierhilf' ),
);
/** Hebrew (עברית) */
$specialPageAliases['he'] = array(
'Cite' => array( 'ציטוט' ),
);
/** Croatian (hrvatski) */
$specialPageAliases['hr'] = array(
'Cite' => array( 'Citiraj' ),
);
/** Upper Sorbian (hornjoserbsce) */
$specialPageAliases['hsb'] = array(
'Cite' => array( 'Citowanska_pomoc' ),
);
/** 湘语 (湘语) */
$specialPageAliases['hsn'] = array(
'Cite' => array( '建脚注' ),
);
/** Haitian (Kreyòl ayisyen) */
$specialPageAliases['ht'] = array(
'Cite' => array( 'Site' ),
);
/** Hungarian (magyar) */
$specialPageAliases['hu'] = array(
'Cite' => array( 'Hivatkozás', 'Irodalomjegyzék' ),
);
/** Interlingua (interlingua) */
$specialPageAliases['ia'] = array(
'Cite' => array( 'Citation' ),
);
/** Indonesian (Bahasa Indonesia) */
$specialPageAliases['id'] = array(
'Cite' => array( 'Kutip' ),
);
/** Igbo (Igbo) */
$specialPageAliases['ig'] = array(
'Cite' => array( 'Dépùtà' ),
);
/** Ido (Ido) */
$specialPageAliases['io'] = array(
'Cite' => array( 'Citar' ),
);
/** Italian (italiano) */
$specialPageAliases['it'] = array(
'Cite' => array( 'Cita' ),
);
/** Japanese (日本語) */
$specialPageAliases['ja'] = array(
'Cite' => array( '引用' ),
);
/** Korean (한국어) */
$specialPageAliases['ko'] = array(
'Cite' => array( '인용' ),
);
/** Colognian (Ripoarisch) */
$specialPageAliases['ksh'] = array(
'Cite' => array( 'Zitteere' ),
);
/** Cornish (kernowek) */
$specialPageAliases['kw'] = array(
'Cite' => array( 'Devynna' ),
);
/** Ladino (Ladino) */
$specialPageAliases['lad'] = array(
'Cite' => array( 'MostrarManaderos' ),
);
/** Luxembourgish (Lëtzebuergesch) */
$specialPageAliases['lb'] = array(
'Cite' => array( 'Zitéierhellëf' ),
);
/** Literary Chinese (文言) */
$specialPageAliases['lzh'] = array(
'Cite' => array( '引文' ),
);
/** Macedonian (македонски) */
$specialPageAliases['mk'] = array(
'Cite' => array( 'Навод' ),
);
/** Malayalam (മലയാളം) */
$specialPageAliases['ml'] = array(
'Cite' => array( 'അവലംബം' ),
);
/** Marathi (मराठी) */
$specialPageAliases['mr'] = array(
'Cite' => array( 'संदर्भद्या' ),
);
/** Malay (Bahasa Melayu) */
$specialPageAliases['ms'] = array(
'Cite' => array( 'Petik' ),
);
/** Maltese (Malti) */
$specialPageAliases['mt'] = array(
'Cite' => array( 'Iċċita' ),
);
/** Nahuatl (Nāhuatl) */
$specialPageAliases['nah'] = array(
'Cite' => array( 'Tlahtoa', 'Citar' ),
);
/** Norwegian Bokmål (norsk bokmål) */
$specialPageAliases['nb'] = array(
'Cite' => array( 'Siteringshjelp' ),
);
/** Low German (Plattdüütsch) */
$specialPageAliases['nds'] = array(
'Cite' => array( 'Ziteerhelp' ),
);
/** Low Saxon (Netherlands) (Nedersaksies) */
$specialPageAliases['nds-nl'] = array(
'Cite' => array( 'Siteerhulpe' ),
);
/** Dutch (Nederlands) */
$specialPageAliases['nl'] = array(
'Cite' => array( 'Citeren' ),
);
/** Norwegian Nynorsk (norsk nynorsk) */
$specialPageAliases['nn'] = array(
'Cite' => array( 'Siter' ),
);
/** Occitan (occitan) */
$specialPageAliases['oc'] = array(
'Cite' => array( 'Citar' ),
);
/** Polish (polski) */
$specialPageAliases['pl'] = array(
'Cite' => array( 'Cytuj' ),
);
/** Pashto (پښتو) */
$specialPageAliases['ps'] = array(
'Cite' => array( 'درک' ),
);
/** Portuguese (português) */
$specialPageAliases['pt'] = array(
'Cite' => array( 'Citar' ),
);
/** Brazilian Portuguese (português do Brasil) */
$specialPageAliases['pt-br'] = array(
'Cite' => array( 'Citar' ),
);
/** Romanian (română) */
$specialPageAliases['ro'] = array(
'Cite' => array( 'Citează' ),
);
/** Russian (русский) */
$specialPageAliases['ru'] = array(
'Cite' => array( 'Цитата' ),
);
/** Sanskrit (संस्कृतम्) */
$specialPageAliases['sa'] = array(
'Cite' => array( 'उद्धृत' ),
);
/** Sicilian (sicilianu) */
$specialPageAliases['scn'] = array(
'Cite' => array( 'Cita' ),
);
/** Slovak (slovenčina) */
$specialPageAliases['sk'] = array(
'Cite' => array( 'Citovať' ),
);
/** Slovenian (slovenščina) */
$specialPageAliases['sl'] = array(
'Cite' => array( 'Navedi' ),
);
/** Albanian (shqip) */
$specialPageAliases['sq'] = array(
'Cite' => array( 'Citim' ),
);
/** Swedish (svenska) */
$specialPageAliases['sv'] = array(
'Cite' => array( 'Citera' ),
);
/** Swahili (Kiswahili) */
$specialPageAliases['sw'] = array(
'Cite' => array( 'Taja', 'Hakikisha' ),
);
/** Tetum (tetun) */
$specialPageAliases['tet'] = array(
'Cite' => array( 'Sita' ),
);
/** Thai (ไทย) */
$specialPageAliases['th'] = array(
'Cite' => array( 'อ้างอิง' ),
);
/** Tagalog (Tagalog) */
$specialPageAliases['tl'] = array(
'Cite' => array( 'Sipiin' ),
);
/** Turkish (Türkçe) */
$specialPageAliases['tr'] = array(
'Cite' => array( 'KaynakGöster' ),
);
/** Urdu (اردو) */
$specialPageAliases['ur'] = array(
'Cite' => array( 'حوالہ' ),
);
/** vèneto (vèneto) */
$specialPageAliases['vec'] = array(
'Cite' => array( 'Cita' ),
);
/** Vietnamese (Tiếng Việt) */
$specialPageAliases['vi'] = array(
'Cite' => array( 'Ghi_chú' ),
);
/** Yiddish (ייִדיש) */
$specialPageAliases['yi'] = array(
'Cite' => array( 'ציטירן' ),
);
/** Cantonese (粵語) */
$specialPageAliases['yue'] = array(
'Cite' => array( '引用' ),
);
/** Simplified Chinese (中文(简体)‎) */
$specialPageAliases['zh-hans'] = array(
'Cite' => array( '引用' ),
);
/** Traditional Chinese (中文(繁體)‎) */
$specialPageAliases['zh-hant'] = array(
'Cite' => array( '引用' ),
);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,90 @@
<?php
if ( !defined( 'MEDIAWIKI' ) ) die();
/**
* A special page extension that adds a special page that generates citations
* for pages.
*
* @file
* @ingroup Extensions
*
* @link http://www.mediawiki.org/wiki/Extension:Cite/Special:Cite.php Documentation
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
*/
$wgExtensionCredits['specialpage'][] = array(
'path' => __FILE__,
'name' => 'Cite',
'author' => 'Ævar Arnfjörð Bjarmason',
'descriptionmsg' => 'cite_article_desc',
'url' => 'https://www.mediawiki.org/wiki/Extension:Cite/Special:Cite.php'
);
$dir = __DIR__ . '/';
# Internationalisation file
$wgExtensionMessagesFiles['SpecialCite'] = $dir . 'SpecialCite.i18n.php';
$wgExtensionMessagesFiles['SpecialCiteAliases'] = $dir . 'SpecialCite.alias.php';
$wgHooks['SkinTemplateBuildNavUrlsNav_urlsAfterPermalink'][] = 'wfSpecialCiteNav';
$wgHooks['SkinTemplateToolboxEnd'][] = 'wfSpecialCiteToolbox';
$wgSpecialPages['Cite'] = 'SpecialCite';
$wgAutoloadClasses['SpecialCite'] = $dir . 'SpecialCite_body.php';
// Resources
$citeResourceTemplate = array(
'localBasePath' => __DIR__ . '/modules',
'remoteExtPath' => 'Cite/modules'
);
$wgResourceModules['ext.specialcite'] = $citeResourceTemplate + array(
'styles' => 'ext.specialcite.css',
'scripts' => array(),
'position' => 'bottom',
);
/**
* @param $skintemplate SkinTemplate
* @param $nav_urls
* @param $oldid
* @param $revid
* @return bool
*/
function wfSpecialCiteNav( &$skintemplate, &$nav_urls, &$oldid, &$revid ) {
// check whether were in the right namespace, the $revid has the correct type and is not empty
// (what would mean that the current page doesnt exist)
$title = $skintemplate->getTitle();
if ( $title->isContentPage() && $revid !== 0 && !empty( $revid ) )
$nav_urls['cite'] = array(
'args' => array( 'page' => $title->getPrefixedDBkey(), 'id' => $revid )
);
return true;
}
/**
* add the cite link to the toolbar
*
* @param $skin Skin
*
* @return bool
*/
function wfSpecialCiteToolbox( &$skin ) {
if ( isset( $skin->data['nav_urls']['cite'] ) ) {
echo Html::rawElement(
'li',
array( 'id' => 't-cite' ),
Linker::link(
SpecialPage::getTitleFor( 'Cite' ),
wfMessage( 'cite_article_link' )->text(), // @todo Should be escaped()?
# Used message keys: 'tooltip-cite-article', 'accesskey-cite-article'
Linker::tooltipAndAccessKeyAttribs( 'cite-article' ),
$skin->data['nav_urls']['cite']['args']
)
);
}
return true;
}

View file

@ -0,0 +1,180 @@
<?php
class SpecialCite extends SpecialPage {
function __construct() {
parent::__construct( 'Cite' );
}
function execute( $par ) {
global $wgUseTidy;
// Having tidy on causes whitespace and <pre> tags to
// be generated around the output of the CiteOutput
// class TODO FIXME.
$wgUseTidy = false;
$this->setHeaders();
$this->outputHeader();
$page = $par !== null ? $par : $this->getRequest()->getText( 'page' );
$title = Title::newFromText( $page );
$cform = new CiteForm( $title );
$cform->execute();
if ( $title && $title->exists() ) {
$id = $this->getRequest()->getInt( 'id' );
$cout = new CiteOutput( $title, $id );
$cout->execute();
}
}
}
class CiteForm {
/**
* @var Title
*/
var $mTitle;
function __construct( &$title ) {
$this->mTitle =& $title;
}
function execute() {
global $wgOut, $wgScript;
$wgOut->addHTML(
Xml::openElement( 'form',
array(
'id' => 'specialcite',
'method' => 'get',
'action' => $wgScript
) ) .
Html::hidden( 'title', SpecialPage::getTitleFor( 'Cite' )->getPrefixedDBkey() ) .
Xml::openElement( 'label' ) .
wfMessage( 'cite_page' )->escaped() . ' ' .
Xml::element( 'input',
array(
'type' => 'text',
'size' => 30,
'name' => 'page',
'value' => is_object( $this->mTitle ) ? $this->mTitle->getPrefixedText() : ''
),
''
) .
' ' .
Xml::element( 'input',
array(
'type' => 'submit',
'value' => wfMessage( 'cite_submit' )->escaped()
),
''
) .
Xml::closeElement( 'label' ) .
Xml::closeElement( 'form' )
);
}
}
class CiteOutput {
/**
* @var Title
*/
var $mTitle;
/**
* @var Article
*/
var $mArticle;
var $mId;
/**
* @var Parser
*/
var $mParser;
/**
* @var ParserOptions
*/
var $mParserOptions;
var $mSpTitle;
function __construct( $title, $id ) {
global $wgHooks, $wgParser;
$this->mTitle = $title;
$this->mArticle = new Article( $title );
$this->mId = $id;
$wgHooks['ParserGetVariableValueVarCache'][] = array( $this, 'varCache' );
$this->genParserOptions();
$this->genParser();
$wgParser->setHook( 'citation', array( $this, 'CiteParse' ) );
}
function execute() {
global $wgOut, $wgParser, $wgHooks;
$wgHooks['ParserGetVariableValueTs'][] = array( $this, 'timestamp' );
$msg = wfMessage( 'cite_text' )->inContentLanguage()->plain();
if ( $msg == '' ) {
# With MediaWiki 1.20 the plain text files were deleted and the text moved into SpecialCite.i18n.php
# This code is kept for b/c in case an installation has its own file "cite_text-xx"
# for a previously not supported language.
global $wgContLang, $wgContLanguageCode;
$dir = dirname( __FILE__ ) . DIRECTORY_SEPARATOR;
$code = $wgContLang->lc( $wgContLanguageCode );
if ( file_exists( "${dir}cite_text-$code" ) ) {
$msg = file_get_contents( "${dir}cite_text-$code" );
} elseif( file_exists( "${dir}cite_text" ) ){
$msg = file_get_contents( "${dir}cite_text" );
}
}
$ret = $wgParser->parse( $msg, $this->mTitle, $this->mParserOptions, false, true, $this->getRevId() );
$wgOut->addModules( 'ext.specialcite' );
$wgOut->addHTML( $ret->getText() );
}
function genParserOptions() {
global $wgUser;
$this->mParserOptions = ParserOptions::newFromUser( $wgUser );
$this->mParserOptions->setDateFormat( MW_DATE_DEFAULT );
$this->mParserOptions->setEditSection( false );
}
function genParser() {
$this->mParser = new Parser;
$this->mSpTitle = SpecialPage::getTitleFor( 'Cite' );
}
function CiteParse( $in, $argv ) {
$ret = $this->mParser->parse( $in, $this->mSpTitle, $this->mParserOptions, false );
return $ret->getText();
}
function varCache() {
return false;
}
function timestamp( &$parser, &$ts ) {
if ( isset( $parser->mTagHooks['citation'] ) ) {
$ts = wfTimestamp( TS_UNIX, $this->mArticle->getTimestamp() );
}
return true;
}
function getRevId() {
if ( $this->mId ) {
return $this->mId;
} else {
return $this->mTitle->getLatestRevID();
}
}
}

View file

@ -0,0 +1,449 @@
# Force the test runner to ensure the extension is loaded
!! hooks
ref
references
!! endhooks
!! test
Simple <ref>, no <references/>
!! input
Wikipedia rocks!<ref>Proceeds of Rockology, vol. XXI</ref>
!! result
<p>Wikipedia rocks!<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
<br /><strong class="error mw-ext-cite-error">Cite error: <code>&lt;ref&gt;</code> tags exist, but no <code>&lt;references/&gt;</code> tag was found</strong>
</p>
!! end
!! test
Simple <ref>, with <references/>
!! input
Wikipedia rocks!<ref>Proceeds of Rockology, vol. XXI</ref>
<references/>
!! result
<p>Wikipedia rocks!<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">Proceeds of Rockology, vol. XXI</span>
</li>
</ol>
!! end
!! article
Template:Simple template
!! text
A ''simple'' template.
!! endarticle
!! test
<ref> with a simple template
!! input
Templating<ref>{{simple template}}</ref>
<references/>
!! result
<p>Templating<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">A <i>simple</i> template.</span>
</li>
</ol>
!! end
!! test
<ref> with a <nowiki>
!! input
Templating<ref><nowiki>{{simple template}}</nowiki></ref>
<references/>
!! result
<p>Templating<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">{{simple template}}</span>
</li>
</ol>
!! end
!! test
<ref> in a <nowiki>
!! input
Templating<nowiki><ref>{{simple template}}</ref></nowiki>
<references/>
!! result
<p>Templating&lt;ref&gt;{{simple template}}&lt;/ref&gt;
</p><p><br />
</p>
!! end
!! test
<ref> in a <!--comment-->
!! input
Templating<!--<ref>{{simple template}}</ref>-->
<references/>
!! result
<p>Templating
</p><p><br />
</p>
!! end
!! test
<!--comment--> in a <ref> (bug 5384)
!! input
Templating<ref>Text<!--comment--></ref>
<references/>
!! result
<p>Templating<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">Text</span>
</li>
</ol>
!! end
!! test
<references> after <gallery> (bug 6164)
!! input
<ref>one</ref>
<gallery>Image:Foobar.jpg</gallery>
<references/>
!! result
<p><sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
</p>
<ul class="gallery mw-gallery-traditional">
<li class="gallerybox" style="width: 155px"><div style="width: 155px">
<div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg" width="120" height="14" /></a></div></div>
<div class="gallerytext">
</div>
</div></li>
</ul>
<ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">one</span>
</li>
</ol>
!! end
!! test
{{REVISIONID}} on page with <ref> (bug 6299)
!! input
{{REVISIONID}}<ref>elite</ref>
!! result
<p>1337<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
<br /><strong class="error mw-ext-cite-error">Cite error: <code>&lt;ref&gt;</code> tags exist, but no <code>&lt;references/&gt;</code> tag was found</strong>
</p>
!! end
!! test
{{REVISIONID}} on page without <ref> (bug 6299 sanity check)
!! input
{{REVISIONID}}
!! result
<p>1337
</p>
!! end
!! test
Blank ref followed by ref with content
!! input
<ref name="blank"/>
<ref name="blank">content</ref>
<references/>
!! result
<p><sup id="cite_ref-blank_1-0" class="reference"><a href="#cite_note-blank-1">[1]</a></sup>
</p><p><sup id="cite_ref-blank_1-1" class="reference"><a href="#cite_note-blank-1">[1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-blank-1"><span class="mw-cite-backlink">↑ <sup><a href="#cite_ref-blank_1-0">1.0</a></sup> <sup><a href="#cite_ref-blank_1-1">1.1</a></sup></span> <span class="reference-text">content</span>
</li>
</ol>
!! end
!! test
Regression: non-blank ref "0" followed by ref with content
!! input
<ref name="blank">0</ref>
<ref name="blank">content</ref>
<references/>
!! result
<p><sup id="cite_ref-blank_1-0" class="reference"><a href="#cite_note-blank-1">[1]</a></sup>
</p><p><sup id="cite_ref-blank_1-1" class="reference"><a href="#cite_note-blank-1">[1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-blank-1"><span class="mw-cite-backlink">↑ <sup><a href="#cite_ref-blank_1-0">1.0</a></sup> <sup><a href="#cite_ref-blank_1-1">1.1</a></sup></span> <span class="reference-text">0</span>
</li>
</ol>
!! end
!! test
Regression sanity check: non-blank ref "1" followed by ref with content
!! input
<ref name="blank">1</ref>
<ref name="blank">content</ref>
<references/>
!! result
<p><sup id="cite_ref-blank_1-0" class="reference"><a href="#cite_note-blank-1">[1]</a></sup>
</p><p><sup id="cite_ref-blank_1-1" class="reference"><a href="#cite_note-blank-1">[1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-blank-1"><span class="mw-cite-backlink">↑ <sup><a href="#cite_ref-blank_1-0">1.0</a></sup> <sup><a href="#cite_ref-blank_1-1">1.1</a></sup></span> <span class="reference-text">1</span>
</li>
</ol>
!! end
!! test
Ref names containing a number
!! input
<ref name="test123test">One</ref>
<ref name="123test">Two</ref>
<ref name="test123">Three</ref>
<references />
!! result
<p><sup id="cite_ref-test123test_1-0" class="reference"><a href="#cite_note-test123test-1">[1]</a></sup>
<sup id="cite_ref-123test_2-0" class="reference"><a href="#cite_note-123test-2">[2]</a></sup>
<sup id="cite_ref-test123_3-0" class="reference"><a href="#cite_note-test123-3">[3]</a></sup>
</p>
<ol class="references">
<li id="cite_note-test123test-1"><span class="mw-cite-backlink"><a href="#cite_ref-test123test_1-0">↑</a></span> <span class="reference-text">One</span>
</li>
<li id="cite_note-123test-2"><span class="mw-cite-backlink"><a href="#cite_ref-123test_2-0">↑</a></span> <span class="reference-text">Two</span>
</li>
<li id="cite_note-test123-3"><span class="mw-cite-backlink"><a href="#cite_ref-test123_3-0">↑</a></span> <span class="reference-text">Three</span>
</li>
</ol>
!! end
!! test
Erroneous refs
!! input
<ref name="0">Zero</ref>
<ref>Also zero, but differently! (Normal ref)</ref>
<ref />
<ref name="foo" name="bar" />
<ref name="blankwithnoreference" />
<references name="quasit" />
<references />
!! result
<p><strong class="error mw-ext-cite-error">Cite error: Invalid <code>&lt;ref&gt;</code> tag;
name cannot be a simple integer. Use a descriptive title</strong>
</p><p><sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
</p><p><strong class="error mw-ext-cite-error">Cite error: Invalid <code>&lt;ref&gt;</code> tag;
refs with no content must have a name</strong>
</p><p><sup id="cite_ref-bar_2-0" class="reference"><a href="#cite_note-bar-2">[2]</a></sup>
</p><p><sup id="cite_ref-blankwithnoreference_3-0" class="reference"><a href="#cite_note-blankwithnoreference-3">[3]</a></sup>
</p><p><strong class="error mw-ext-cite-error">Cite error: Invalid <code>&lt;references&gt;</code> tag;
parameter "group" is allowed only.
Use <code>&lt;references /&gt;</code>, or <code>&lt;references group="..." /&gt;</code></strong>
</p>
<ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">Also zero, but differently! (Normal ref)</span>
</li>
<li id="cite_note-bar"><span class="mw-cite-backlink"><a href="#cite_ref-bar_0">↑</a></span> <strong class="error mw-ext-cite-error">Cite error: Invalid <code>&lt;ref&gt;</code> tag;
no text was provided for refs named <code>bar</code></strong></li>
<li id="cite_note-blankwithnoreference"><span class="mw-cite-backlink"><a href="#cite_ref-blankwithnoreference_0">↑</a></span> <strong class="error mw-ext-cite-error">Cite error: Invalid <code>&lt;ref&gt;</code> tag;
no text was provided for refs named <code>blankwithnoreference</code></strong></li>
</ol>
!! end
!! test
Simple <ref>, with <references/> in group
!! input
Wikipedia rocks!<ref>Proceeds of Rockology, vol. XXI</ref>
Wikipedia rocks!<ref group=note>Proceeds of Rockology, vol. XXI</ref>
<references/>
<references group=note/>
!! result
<p>Wikipedia rocks!<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
Wikipedia rocks!<sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[note 1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">Proceeds of Rockology, vol. XXI</span>
</li>
</ol>
<ol class="references">
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">Proceeds of Rockology, vol. XXI</span>
</li>
</ol>
!! end
!! test
Simple <ref>, with <references/> in group, with groupname in chinese
!! input
AAA<ref group="参">ref a</ref>BBB<ref group="注">note b</ref>CCC<ref group="参">ref c</ref>
;refs
<references group="参" />
;notes
<references group="注" />
!! result
<p>AAA<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[参 1]</a></sup>BBB<sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[注 1]</a></sup>CCC<sup id="cite_ref-3" class="reference"><a href="#cite_note-3">[参 2]</a></sup>
</p>
<dl><dt>refs
</dt></dl>
<ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">ref a</span>
</li>
<li id="cite_note-3"><span class="mw-cite-backlink"><a href="#cite_ref-3">↑</a></span> <span class="reference-text">ref c</span>
</li>
</ol>
<dl><dt>notes
</dt></dl>
<ol class="references">
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">note b</span>
</li>
</ol>
!! end
!! test
<ref> defined in <references>
!! input
<ref name="foo"/>
<references>
<ref name="foo">BAR</ref>
</references>
!! result
<p><sup id="cite_ref-foo_1-0" class="reference"><a href="#cite_note-foo-1">[1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-foo-1"><span class="mw-cite-backlink"><a href="#cite_ref-foo_1-0">↑</a></span> <span class="reference-text">BAR</span>
</li>
</ol>
!! end
!! test
<ref> defined in <references> called with #tag
!! input
<ref name="foo"/>
{{#tag:references|
<ref name="foo">BAR</ref>
}}
!! result
<p><sup id="cite_ref-foo_1-0" class="reference"><a href="#cite_note-foo-1">[1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-foo-1"><span class="mw-cite-backlink"><a href="#cite_ref-foo_1-0">↑</a></span> <span class="reference-text">BAR</span>
</li>
</ol>
!! end
!! test
<ref> defined in <references> error conditions
!! input
<ref name="foo" group="2"/>
<references group="2">
<ref name="foo"/>
<ref name="unused">BAR</ref>
<ref name="foo" group="1">bad group</ref>
<ref>BAR BAR</ref>
</references>
!! result
<p><sup id="cite_ref-foo_1-0" class="reference"><a href="#cite_note-foo-1">[2 1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-foo"><span class="mw-cite-backlink"><a href="#cite_ref-foo_0">↑</a></span> <strong class="error mw-ext-cite-error">Cite error: Invalid <code>&lt;ref&gt;</code> tag;
no text was provided for refs named <code>foo</code></strong></li>
</ol>
<p><strong class="error mw-ext-cite-error">Cite error: <code>&lt;ref&gt;</code> tag with name "unused" defined in <code>&lt;references&gt;</code> is not used in prior text.</strong><br />
<strong class="error mw-ext-cite-error">Cite error: <code>&lt;ref&gt;</code> tag in <code>&lt;references&gt;</code> has conflicting group attribute "1".</strong><br />
<strong class="error mw-ext-cite-error">Cite error: <code>&lt;ref&gt;</code> tag defined in <code>&lt;references&gt;</code> has no name attribute.</strong>
</p>
!! end
!! article
MediaWiki:cite_link_label_group-klingon
!! text
wa' cha' wej loS vagh jav Soch chorgh Hut wa'maH
!! endarticle
!! test
<ref> with custom group link
!! input
Wikipedia rocks!<ref group="klingon">Proceeds of Rockology, vol. XXI</ref>
<references group="klingon"/>
!! result
<p>Wikipedia rocks!<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[wa']</a></sup>
</p>
<ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">Proceeds of Rockology, vol. XXI</span>
</li>
</ol>
!! end
!! test
Bug 31374 regression check: nested strip items
!! input
{{#tag:ref|note<ref>reference</ref>|group=Note}}
<references group=Note />
<references />
!! result
<p><sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[Note 1]</a></sup>
</p>
<ol class="references">
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">note<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup></span>
</li>
</ol>
<ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">reference</span>
</li>
</ol>
!! end
!! test
Bug 13073 regression check: wrapped <references>
!! input
<ref>
foo
</ref>
<div><references /></div>
!! result
<p><sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
</p>
<div><ol class="references">
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">
foo</span>
</li>
</ol></div>
!! end

View file

@ -0,0 +1,16 @@
/**
* Per http://developer.yahoo.com/blogs/ydn/clip-hidden-content-better-accessibility-53456.html
* and https://en.wikipedia.org/w/index.php?oldid=572888139#Scrolling_past_the_bottom_of_the_page...
*/
.cite-accessibility-label {
position: absolute !important;
/* Workaround a Webkit/Blink bug about positioning within columns as many wikis format references with several columns */
top: -99999px;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
padding: 0 !important;
border: 0 !important;
height: 1px !important;
width: 1px !important;
overflow: hidden;
}

View file

@ -0,0 +1,38 @@
/**
* Main JavaScript for the Cite extension. The main purpose of this file
* is to add accessibility attributes to the citation links as that can
* hardly be done server side (bug 38141).
*
* @author Marius Hoch <hoo@online.de>
*/
( function ( mw, $ ) {
'use strict';
mw.hook( 'wikipage.content' ).add( function ( $content ) {
var accessibilityLabelOne = mw.msg( 'cite_references_link_accessibility_label' ),
accessibilityLabelMany = mw.msg( 'cite_references_link_many_accessibility_label' );
$content.find( '.mw-cite-backlink' ).each( function () {
var $links = $( this ).find( 'a' ),
label;
if ( $links.length > 1 ) {
// This citation is used multiple times. Let's only set the accessibility label on the first link, the
// following ones should then be self-explaining. This is needed to make sure this isn't getting
// too wordy.
label = accessibilityLabelMany;
} else {
label = accessibilityLabelOne;
}
// We can't use aria-label over here as that's not supported consistently across all screen reader / browser
// combinations. We have to use visually hidden spans for the accessibility labels instead.
$links.eq( 0 ).prepend(
$( '<span>' )
.addClass( 'cite-accessibility-label' )
// Also make sure we have at least one space between the accessibility label and the visual one
.text( label + ' ' )
);
} );
} );
} )( mediaWiki, jQuery );

View file

@ -0,0 +1,13 @@
( function ( mw, $ ) {
'use strict';
mw.hook( 'wikipage.content' ).add( function ( $content ) {
$content.find( '.biblio-cite-link,sup.reference a' ).tooltip( {
bodyHandler: function () {
return $content.find( '#' + this.hash.substr( 1 ) + ' > .reference-text' )
.html();
},
showURL: false
} );
} );
} )( mediaWiki, jQuery );

View file

@ -0,0 +1,6 @@
/* Isolation to fix references in case of RTL words at the end of a reference */
sup.reference {
unicode-bidi: -moz-isolate;
unicode-bidi: -webkit-isolate;
unicode-bidi: isolate;
}

View file

@ -0,0 +1,14 @@
.mw-specialcite-bibliographic {
border: 1px solid grey;
background: #E6E8FA;
width: 90%;
padding: 15px 30px 15px 30px;
margin: 10px auto;
}
.mw-specialcite-styles {
border: 1px solid grey;
width: 90%;
padding: 15px 30px 15px 30px;
margin: 10px auto;
}

View file

@ -0,0 +1,9 @@
#tooltip {
position: absolute;
z-index: 3000;
border: 1px solid #111;
background-color: #eee;
padding: 5px;
opacity: 0.85;
}
#tooltip h3, #tooltip div { margin: 0; }

View file

@ -0,0 +1,294 @@
/*
* jQuery Tooltip plugin 1.3
*
* http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/
* http://docs.jquery.com/Plugins/Tooltip
*
* Copyright (c) 2006 - 2008 Jörn Zaefferer
*
* $Id: jquery.tooltip.js 5741 2008-06-21 15:22:16Z joern.zaefferer $
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*/
;(function($) {
// the tooltip element
var helper = {},
// the current tooltipped element
current,
// the title of the current element, used for restoring
title,
// timeout id for delayed tooltips
tID,
// IE 5.5 or 6
IE = $.browser.msie && /MSIE\s(5\.5|6\.)/.test(navigator.userAgent),
// flag for mouse tracking
track = false;
$.tooltip = {
blocked: false,
defaults: {
delay: 200,
fade: false,
showURL: true,
extraClass: "",
top: 15,
left: 15,
id: "tooltip"
},
block: function() {
$.tooltip.blocked = !$.tooltip.blocked;
}
};
$.fn.extend({
tooltip: function(settings) {
settings = $.extend({}, $.tooltip.defaults, settings);
createHelper(settings);
return this.each(function() {
$.data(this, "tooltip", settings);
this.tOpacity = helper.parent.css("opacity");
// copy tooltip into its own expando and remove the title
this.tooltipText = this.title;
$(this).removeAttr("title");
// also remove alt attribute to prevent default tooltip in IE
this.alt = "";
})
.mouseover(save)
.mouseout(hide)
.click(hide);
},
fixPNG: IE ? function() {
return this.each(function () {
var image = $(this).css('backgroundImage');
if (image.match(/^url\(["']?(.*\.png)["']?\)$/i)) {
image = RegExp.$1;
$(this).css({
'backgroundImage': 'none',
'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
}).each(function () {
var position = $(this).css('position');
if (position != 'absolute' && position != 'relative')
$(this).css('position', 'relative');
});
}
});
} : function() { return this; },
unfixPNG: IE ? function() {
return this.each(function () {
$(this).css({'filter': '', backgroundImage: ''});
});
} : function() { return this; },
hideWhenEmpty: function() {
return this.each(function() {
$(this)[ $(this).html() ? "show" : "hide" ]();
});
},
url: function() {
return this.attr('href') || this.attr('src');
}
});
function createHelper(settings) {
// there can be only one tooltip helper
if( helper.parent )
return;
// create the helper, h3 for title, div for url
helper.parent = $('<div id="' + settings.id + '"><h3></h3><div class="body"></div><div class="url"></div></div>')
// add to document
.appendTo(document.body)
// hide it at first
.hide();
// apply bgiframe if available
if ( $.fn.bgiframe )
helper.parent.bgiframe();
// save references to title and url elements
helper.title = $('h3', helper.parent);
helper.body = $('div.body', helper.parent);
helper.url = $('div.url', helper.parent);
}
function settings(element) {
return $.data(element, "tooltip");
}
// main event handler to start showing tooltips
function handle(event) {
// show helper, either with timeout or on instant
if( settings(this).delay )
tID = setTimeout(show, settings(this).delay);
else
show();
// if selected, update the helper position when the mouse moves
track = !!settings(this).track;
$(document.body).bind('mousemove', update);
// update at least once
update(event);
}
// save elements title before the tooltip is displayed
function save() {
// if this is the current source, or it has no title (occurs with click event), stop
if ( $.tooltip.blocked || this == current || (!this.tooltipText && !settings(this).bodyHandler) )
return;
// save current
current = this;
title = this.tooltipText;
if ( settings(this).bodyHandler ) {
helper.title.hide();
var bodyContent = settings(this).bodyHandler.call(this);
if (bodyContent.nodeType || bodyContent.jquery) {
helper.body.empty().append(bodyContent)
} else {
helper.body.html( bodyContent );
}
helper.body.show();
} else if ( settings(this).showBody ) {
var parts = title.split(settings(this).showBody);
helper.title.html(parts.shift()).show();
helper.body.empty();
for(var i = 0, part; (part = parts[i]); i++) {
if(i > 0)
helper.body.append("<br/>");
helper.body.append(part);
}
helper.body.hideWhenEmpty();
} else {
helper.title.html(title).show();
helper.body.hide();
}
// if element has href or src, add and show it, otherwise hide it
if( settings(this).showURL && $(this).url() )
helper.url.html( $(this).url().replace('http://', '') ).show();
else
helper.url.hide();
// add an optional class for this tip
helper.parent.addClass(settings(this).extraClass);
// fix PNG background for IE
if (settings(this).fixPNG )
helper.parent.fixPNG();
handle.apply(this, arguments);
}
// delete timeout and show helper
function show() {
tID = null;
if ((!IE || !$.fn.bgiframe) && settings(current).fade) {
if (helper.parent.is(":animated"))
helper.parent.stop().show().fadeTo(settings(current).fade, current.tOpacity);
else
helper.parent.is(':visible') ? helper.parent.fadeTo(settings(current).fade, current.tOpacity) : helper.parent.fadeIn(settings(current).fade);
} else {
helper.parent.show();
}
update();
}
/**
* callback for mousemove
* updates the helper position
* removes itself when no current element
*/
function update(event) {
if($.tooltip.blocked)
return;
if (event && event.target.tagName == "OPTION") {
return;
}
// stop updating when tracking is disabled and the tooltip is visible
if ( !track && helper.parent.is(":visible")) {
$(document.body).unbind('mousemove', update)
}
// if no current element is available, remove this listener
if( current == null ) {
$(document.body).unbind('mousemove', update);
return;
}
// remove position helper classes
helper.parent.removeClass("viewport-right").removeClass("viewport-bottom");
var left = helper.parent[0].offsetLeft;
var top = helper.parent[0].offsetTop;
if (event) {
// position the helper 15 pixel to bottom right, starting from mouse position
left = event.pageX + settings(current).left;
top = event.pageY + settings(current).top;
var right='auto';
if (settings(current).positionLeft) {
right = $(window).width() - left;
left = 'auto';
}
helper.parent.css({
left: left,
right: right,
top: top
});
}
var v = viewport(),
h = helper.parent[0];
// check horizontal position
if (v.x + v.cx < h.offsetLeft + h.offsetWidth) {
left -= h.offsetWidth + 20 + settings(current).left;
helper.parent.css({left: left + 'px'}).addClass("viewport-right");
}
// check vertical position
if (v.y + v.cy < h.offsetTop + h.offsetHeight) {
top -= h.offsetHeight + 20 + settings(current).top;
helper.parent.css({top: top + 'px'}).addClass("viewport-bottom");
}
}
function viewport() {
return {
x: $(window).scrollLeft(),
y: $(window).scrollTop(),
cx: $(window).width(),
cy: $(window).height()
};
}
// hide helper and restore added classes and the title
function hide(event) {
if($.tooltip.blocked)
return;
// clear timeout if possible
if(tID)
clearTimeout(tID);
// no more current element
current = null;
var tsettings = settings(this);
function complete() {
helper.parent.removeClass( tsettings.extraClass ).hide().css("opacity", "");
}
if ((!IE || !$.fn.bgiframe) && tsettings.fade) {
if (helper.parent.is(':animated'))
helper.parent.stop().fadeTo(tsettings.fade, 0, complete);
else
helper.parent.stop().fadeOut(tsettings.fade, complete);
} else
complete();
if( settings(this).fixPNG )
helper.parent.unfixPNG();
}
})(jQuery);

View file

@ -0,0 +1,34 @@
<?php
/**
* Api module to reload FancyCaptcha
*
* @ingroup API
* @ingroup Extensions
*/
class ApiFancyCaptchaReload extends ApiBase {
public function execute() {
# Get a new FancyCaptcha form data
$captcha = new FancyCaptcha();
$captchaIndex = $captcha->getCaptchaIndex();
$result = $this->getResult();
$result->addValue( null, $this->getModuleName(), array ( 'index' => $captchaIndex ) );
return true;
}
public function getDescription() {
return 'Get a new FancyCaptcha.';
}
public function getAllowedParams() {
return array();
}
public function getParamDescription() {
return array();
}
public function getExamples() {
return array( 'api.php?action=fancycaptchareload&format=xml' );
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* @author Bachsau
* @author Niklas Laxström
*/
class Asirra extends SimpleCaptcha {
public $asirra_clientscript = '//challenge.asirra.com/js/AsirraClientSide.js';
// As we don't have to store anything but some other things to do,
// we're going to replace that constructor completely.
function __construct() {
global $wgExtensionAssetsPath;
$this->asirra_localpath = "$wgExtensionAssetsPath/ConfirmEdit";
}
function getForm() {
global $wgOut;
$wgOut->addModules( 'ext.confirmEdit.asirra' );
$js = Html::linkedScript( $this->asirra_clientscript );
$message = Xml::encodeJsVar( wfMessage( 'asirra-createaccount-fail' )->plain() );
$js .= Html::inlineScript( <<<JAVASCRIPT
var asirra_js_failed = '$message';
JAVASCRIPT
);
$js .= '<noscript>' . wfMessage( 'asirra-nojs' )->parse() . '</noscript>';
return $js;
}
function getMessage( $action ) {
$name = 'asirra-' . $action;
$text = wfMessage( $name )->text();
# Obtain a more tailored message, if possible, otherwise, fall
# back to the default for edits
return wfMessage( $name, $text )->isDisabled() ? wfMessage( 'asirra-edit' )->text() : $text;
}
function passCaptcha() {
global $wgRequest;
$ticket = $wgRequest->getVal( 'Asirra_Ticket' );
$api = 'http://challenge.asirra.com/cgi/Asirra?';
$params = array(
'action' => 'ValidateTicket',
'ticket' => $ticket,
);
$response = Http::get( $api . wfArrayToCgi( $params ) );
$xml = simplexml_load_string( $response );
$result = $xml->xpath( '/AsirraValidation/Result' );
return strval( $result[0] ) === 'Pass';
}
}

View file

@ -0,0 +1,549 @@
<?php
/**
* Internationalisation file for the Asirra module of the ConfirmEdit
* extension.
*
* @file
* @ingroup Extensions
*/
$messages = array();
$messages['en'] = array(
'asirra-desc' => 'Asirra module for ConfirmEdit',
'asirra-edit' => 'To protect the wiki against automated edit spam, we kindly ask you to select just the cat photos in the box below:',
'asirra-addurl' => 'Your edit includes new external links. To protect the wiki against automated edit spam, we kindly ask you to select just the cat photos in the box below:',
'asirra-badlogin' => 'To protect the wiki against automated password cracking, we kindly ask you to select just the cat photos in the box below:',
'asirra-createaccount' => 'To protect the wiki against automated account creation, we kindly ask you to select just the cat photos in the box below:',
'asirra-createaccount-fail' => "Please correctly identify the cats.",
'asirra-create' => 'To protect the wiki against automated page creation, we kindly ask you to select just the cat photos in the box below:',
'asirra-nojs' => '\'\'\'Please enable JavaScript and resubmit the page.\'\'\'',
'asirra-failed' => 'Please identify all cat images',
);
/** Message documentation (Message documentation)
* @author 2nd-player
* @author Beta16
* @author Raymond
* @author Shirayuki
*/
$messages['qqq'] = array(
'asirra-desc' => '{{desc|name=Asirra|url=http://www.mediawiki.org/wiki/Extension:Asirra}}',
'asirra-edit' => '{{Related|ConfirmEdit-edit}}',
'asirra-addurl' => '{{Related|ConfirmEdit-addurl}}',
'asirra-badlogin' => '{{Related|ConfirmEdit-badlogin}}',
'asirra-createaccount' => '{{Related|ConfirmEdit-createaccount}}',
'asirra-createaccount-fail' => 'Used as failure message in JavaScript code.
{{Related|ConfirmEdit-createaccount-fail}}',
'asirra-create' => '{{Related|ConfirmEdit-create}}',
'asirra-nojs' => 'Used in HTML <code><nowiki><noscript></nowiki></code> tag.',
'asirra-failed' => 'Used as alert message in JavaScript code.',
);
/** Asturian (asturianu)
* @author Xuacu
*/
$messages['ast'] = array(
'asirra-desc' => 'Módulu Asirra pa ConfirmEdit',
'asirra-edit' => "Pa protexer la wiki escontra'l spam d'edición automáticu, pidimos-y qu'esbille namái les fotos de gatos del cuadru d'abaxo:",
'asirra-addurl' => "La so edición incluye enllaces esternos nuevos. Pa protexer la wiki escontra'l spam automáticu, pidimos-y qu'esbille namái les fotos de gatos del cuadru d'abaxo:",
'asirra-badlogin' => "Pa protexer la wiki escontra'l frayamientu de claves automáticu, pidimos-y qu'esbille namái les fotos de gatos del cuadru d'abaxo:",
'asirra-createaccount' => "Pa protexer la wiki escontra la creación de cuentes automática, pidimos-y qu'esbille namái les fotos de gatos del cuadru d'abaxo:",
'asirra-createaccount-fail' => 'Identifique correutamente los gatos.',
'asirra-create' => "Pa protexer la wiki escontra la creación de páxines automática, pidimos-y qu'esbille namái les fotos de gatos del cuadru d'abaxo:",
'asirra-nojs' => "'''Por favor active JavaScript y vuelva a unviar la páxina.'''",
'asirra-failed' => 'Identifique toles imaxes de gatos',
);
/** Belarusian (Taraškievica orthography) (беларуская (тарашкевіца))
* @author EugeneZelenko
* @author Jim-by
* @author Wizardist
*/
$messages['be-tarask'] = array(
'asirra-desc' => 'Модуль Asirra для ConfirmEdit',
'asirra-edit' => 'Для абароны супраць спаму, калі ласка, выберыце толькі фота з катом ў полі ніжэй:', # Fuzzy
'asirra-addurl' => 'Вашае рэдагаваньне ўтрымлівае новыя вонкавыя спасылкі. Для абароны супраць спаму, калі ласка, выберыце толькі фота з катом ў полі ніжэй:', # Fuzzy
'asirra-badlogin' => 'Для абароны супраць аўтаматычнага падбору паролю, калі ласка, выберыце толькі фота з катом ў полі ніжэй:', # Fuzzy
'asirra-createaccount' => 'Для абароны супраць аўтаматычнага стварэньня рахункаў, калі ласка, выберыце толькі фота з катом ў полі ніжэй:', # Fuzzy
'asirra-createaccount-fail' => 'Калі ласка, слушна выберыце катоў.',
'asirra-create' => 'Для абароны супраць аўтаматычнага стварэньня старонак, калі ласка, выберыце толькі фота з катом ў полі ніжэй:', # Fuzzy
'asirra-nojs' => "'''Калі ласка, дазвольце JavaScript і дашліце старонку зноў.'''",
'asirra-failed' => 'Калі ласка, вызначце ўсе выявы з катамі',
);
/** Breton (brezhoneg)
* @author Fohanno
*/
$messages['br'] = array(
'asirra-nojs' => "'''Gweredekait JavaScript, mar plij, hag adkasit ar bajenn.'''",
);
/** Czech (česky)
* @author Vks
*/
$messages['cs'] = array(
'asirra-createaccount-fail' => 'Prosíme, správně identifikujte kočky.',
);
/** German (Deutsch)
* @author Kghbln
* @author Metalhead64
*/
$messages['de'] = array(
'asirra-desc' => 'Ermöglicht die Nutzung des Anti-Spam-Moduls Asirra',
'asirra-edit' => 'Zum Schutz des Wikis vor automatisiertem Spam bitten wir dich, nur die Fotos mit Katzen im untenstehenden Feld auszuwählen:',
'asirra-addurl' => 'Deine Bearbeitung enthält neue externe Links. Zum Schutz des Wikis vor automatisiertem Spam bitten wir dich, nur die Fotos mit Katzen im untenstehenden Feld auszuwählen:',
'asirra-badlogin' => 'Zum Schutz des Wikis gegen automatisiertes Knacken von Passwörtern bitten wir dich, nur die Fotos mit Katzen im untenstehenden Feld auszuwählen:',
'asirra-createaccount' => 'Zum Schutz des Wikis gegen automatisiertes Erstellen von Benutzerkonten bitten wir dich, nur die Fotos mit Katzen im untenstehenden Feld auszuwählen:',
'asirra-createaccount-fail' => 'Bitte wähle nur die Fotos mit Katzen aus.',
'asirra-create' => 'Zum Schutz des Wikis gegen automatisiertes Erstellen von Seiten bitten wir dich, nur die Fotos mit Katzen im untenstehenden Feld auszuwählen:',
'asirra-nojs' => "'''Bitte JavaScript aktivieren und die Seiten nochmals Speichern.'''",
'asirra-failed' => 'Bitte wähle nur die Fotos mit Katzen aus.',
);
/** German (formal address) (Deutsch (Sie-Form))
* @author Kghbln
*/
$messages['de-formal'] = array(
'asirra-addurl' => 'Ihre Bearbeitung enthält neue externe Links. Zum Schutz vor automatisiertem Spam bitten wir Sie, nur die Fotos mit Katzen im untenstehenden Feld auszuwählen:', # Fuzzy
'asirra-badlogin' => 'Zum Schutz gegen automatisiertes Knacken von Passwörtern bitten wir Sie, nur die Fotos mit Katzen im untenstehenden Feld auszuwählen:', # Fuzzy
'asirra-createaccount' => 'Zum Schutz gegen automatisiertes Erstellen von Benutzerkonten bitten wir Sie, nur die Fotos mit Katzen im untenstehenden Feld auszuwählen:', # Fuzzy
'asirra-createaccount-fail' => 'Bitte wählen Sie nur die Fotos mit Katzen aus.',
'asirra-create' => 'Zum Schutz gegen automatisiertes Erstellen von Seiten bitten wir Sie, nur die Fotos mit Katzen im untenstehenden Feld auszuwählen:', # Fuzzy
'asirra-failed' => 'Bitte wählen Sie nur die Fotos mit Katzen aus.',
);
/** Zazaki (Zazaki)
* @author Erdemaslancan
*/
$messages['diq'] = array(
'asirra-desc' => 'Qandê Asirra modulê RaştkerdenVurnen',
);
/** Spanish (español)
* @author Armando-Martin
*/
$messages['es'] = array(
'asirra-desc' => 'Módulo de Asirra para ConfirmEdit',
'asirra-edit' => 'Para ayudar a protegernos contra el spam de edición automática, seleccione sólo las fotos de gatos en el cuadro siguiente:', # Fuzzy
'asirra-addurl' => 'Su edición incluye nuevos enlaces externos. Para ayudar a protegernos contra el spam automatizado, por favor, seleccione solo las fotos de gato en el cuadro siguiente:', # Fuzzy
'asirra-badlogin' => 'Para ayudar a protegernos del robo automatizado de contraseñas, seleccione sólo las fotos de gatos en el cuadro siguiente:', # Fuzzy
'asirra-createaccount' => 'Para ayudar a protegernos contra la creación automatizada de cuentas, seleccione sólo las fotos de gato en el cuadro siguiente:', # Fuzzy
'asirra-createaccount-fail' => 'Identifique correctamente los gatos.',
'asirra-create' => 'Para ayudar a protegernos contra la creación automática de páginas, seleccione sólo las fotos de gato en el cuadro siguiente:', # Fuzzy
'asirra-nojs' => "'''Por favor active JavaScript y vuelva a la página.'''",
'asirra-failed' => 'Identifique todas las imágenes de gatos',
);
/** Finnish (suomi)
* @author VezonThunder
*/
$messages['fi'] = array(
'asirra-desc' => 'Asirra-moduuli muokkauksen varmennukseen',
'asirra-edit' => 'Suojana automaattisia roskamuokkauksia vastaan sinun on valittava kissan kuvat laatikosta alla:', # Fuzzy
'asirra-addurl' => 'Muokkauksesi sisältää uusia ulkoisia linkkejä. Suojana automaattista roskapostia vastaan sinun on valittava kissan kuvat laatikosta alla:', # Fuzzy
'asirra-badlogin' => 'Suojana automaattisia salasanamurtoja vastaan sinun on valittava kissan kuvat laatikosta alla:', # Fuzzy
'asirra-createaccount' => 'Suojana automaattista tunnusten luontia vastaan sinun on valittava kissan kuvat laatikosta alla:', # Fuzzy
'asirra-createaccount-fail' => 'Tunnista kissat.',
'asirra-create' => 'Suojana automaattista sivujen luontia vastaan sinun on valittava kissan kuvat laatikosta alla:', # Fuzzy
'asirra-nojs' => "'''Salli JavaScript ja lähetä uudelleen.'''",
'asirra-failed' => 'Tunnista kaikki kissan kuvat',
);
/** French (français)
* @author Gomoko
* @author Nicolas NALLET
* @author Seb35
*/
$messages['fr'] = array(
'asirra-desc' => 'Module Asirra pour ConfirmEdit',
'asirra-edit' => 'Pour protéger le wiki contre le spam dédition automatique, nous vous demandons de bien vouloir sélectionner uniquement les photos de chats dans la boîte ci-dessous :',
'asirra-addurl' => 'Votre édition contient des liens externes. Pour protéger le wiki contre le spam de modification automatique, nous vous demandons de bien vouloir sélectionner uniquement les photos de chats dans la boîte ci-dessous :',
'asirra-badlogin' => 'Pour protéger le wiki des essais automatiques de cassage de mot de passe, nous vous demandons de bien vouloir sélectionner uniquement les photos de chats dans la boîte ci-dessous :',
'asirra-createaccount' => 'Pour protéger le wiki contre la création automatique de comptes, nous vous demandons de bien vouloir sélectionner uniquement les photos de chats dans la boîte ci-dessous :',
'asirra-createaccount-fail' => 'Veuillez identifier correctement les chats.',
'asirra-create' => 'Pour protéger le wiki contre la création automatique de pages, nous vous demandons de bien vouloir sélectionner uniquement les photos de chats dans la boîte ci-dessous :',
'asirra-nojs' => "'''Veuillez activer le JavaScript et re-soumettre la page.'''",
'asirra-failed' => 'Veuillez identifier toutes les images de chat',
);
/** Galician (galego)
* @author Toliño
*/
$messages['gl'] = array(
'asirra-desc' => 'Módulo Asirra para ConfirmEdit',
'asirra-edit' => 'Para protexer o wiki contra o spam automático, seleccione só as fotos de gatos na caixa:',
'asirra-addurl' => 'A súa edición inclúe novas ligazóns externas. Para protexer o wiki contra o spam automático, seleccione só as fotos de gatos na caixa:',
'asirra-badlogin' => 'Para protexer o wiki contra o roubo de contrasinais, seleccione só as fotos de gatos na caixa:',
'asirra-createaccount' => 'Para protexer o wiki contra a creación automática de contas, seleccione só as fotos de gatos na caixa:',
'asirra-createaccount-fail' => 'Identifique correctamente os gatos.',
'asirra-create' => 'Para protexer o wiki contra a creación automática de páxinas, seleccione só as fotos de gatos na caixa:',
'asirra-nojs' => "'''Active o JavaScript e volva enviar a páxina.'''",
'asirra-failed' => 'Identifique todas as fotos de gatos',
);
/** Upper Sorbian (hornjoserbsce)
* @author Michawiki
*/
$messages['hsb'] = array(
'asirra-desc' => 'Modul Asirra za ConfirmEdit',
'asirra-edit' => 'Za škit přećiwo awtomatizowanemu spamej, prošu wubjer jenož fota kóčkow w slědowacym polu:', # Fuzzy
'asirra-addurl' => 'Twoja změna wobsahuje nowe eksterne wotkazy. Za škit přećiwo awtomatizowanemu spamej, prošu wubjer jenož fota kóčkow w slědowacym polu:', # Fuzzy
'asirra-badlogin' => 'Za škit přećiwo awtomatizowanemu złamanju hesłow, prošu wubjer jenož fota kóčkow w slědowacym polu:', # Fuzzy
'asirra-createaccount' => 'Za škit přećiwo awtomatiskemu wutworjenju konta, prošu wubjer jenož fota kóčkow w slědowacym polu:', # Fuzzy
'asirra-createaccount-fail' => 'Prošu identifikuj kóčki.',
'asirra-create' => 'Za škit přećiwo awtomatiskemu wutworjenju strony, prošu wubjer jenož fota kóčkow w slědowacym polu:', # Fuzzy
'asirra-nojs' => "'''Prošu zmóžń JavaScript a składuj stronu hišće raz.'''",
'asirra-failed' => 'Prošu identifikuj wšě wobrazy z kóčkami',
);
/** Interlingua (interlingua)
* @author McDutchie
*/
$messages['ia'] = array(
'asirra-desc' => 'Modulo de Asirra pro ConfirmEdit',
'asirra-edit' => 'Pro adjutar a proteger le wiki contra le spam automatisate, per favor selige solmente le photos de cattos in le quadro sequente:',
'asirra-addurl' => 'Iste modification include nove ligamines externe. Pro adjutar a proteger le wiki contra le spam automatisate, per favor selige solmente le photos de cattos in le quadro sequente:',
'asirra-badlogin' => 'Pro adjutar a proteger le wiki contra le furto automatisate de contrasignos, per favor selige solmente le photos de cattos in le quadro sequente:',
'asirra-createaccount' => 'Pro adjutar a proteger le wiki contra le creation automatisate de contos, per favor selige solmente le photos de cattos in le quadro sequente:',
'asirra-createaccount-fail' => 'Per favor identifica correctemente le cattos.',
'asirra-create' => 'Pro adjutar a proteger le wiki contra le creation automatisate de paginas, per favor selige solmente le photos de cattos in le quadro sequente:',
'asirra-nojs' => "'''Per favor activa JavaScript e resubmitte le pagina.'''",
'asirra-failed' => 'Per favor identifica tote le imagines de cattos',
);
/** Italian (italiano)
* @author Beta16
*/
$messages['it'] = array(
'asirra-desc' => 'Modulo ASIRRA per ConfirmEdit',
'asirra-edit' => 'Per proteggere il wiki dalle modifiche automatiche che aggiungono spam, ti chiediamo gentilmente di selezionare solo le foto di gatti nel riquadro sottostante:',
'asirra-addurl' => 'La tua modifica aggiunge qualche nuovo collegamento esterno. Per proteggere il wiki dallo spam automatico, ti chiediamo gentilmente di selezionare solo le foto di gatti nel riquadro sottostante:',
'asirra-badlogin' => 'Per proteggere il wiki dalla forzatura automatica delle password, ti chiediamo gentilmente di selezionare solo le foto di gatti nel riquadro sottostante:',
'asirra-createaccount' => 'Per proteggere il wiki dalla creazione automatica di nuovi accessi, ti chiediamo gentilmente di selezionare solo le foto di gatti nel riquadro sottostante:',
'asirra-createaccount-fail' => 'Identifica correttamente i gatti.',
'asirra-create' => 'Per proteggere il wiki dalla creazione automatica di pagine, ti chiediamo gentilmente di selezionare solo le foto di gatti nel riquadro sottostante:',
'asirra-nojs' => "'''Attiva JavaScript ed invia di nuovo la pagina.'''",
'asirra-failed' => 'Identifica tutte le immagini di gatti',
);
/** Japanese (日本語)
* @author 2nd-player
* @author Shirayuki
*/
$messages['ja'] = array(
'asirra-desc' => 'ConfirmEdit 用 Asirra モジュール',
'asirra-edit' => 'ウィキでの自動編集のスパム攻撃を防ぐため、お手数をおかけしますが猫が写っている画像を以下から選択してください:',
'asirra-addurl' => 'あなたは新しい外部リンクを追加しようとしています。ウィキへの自動スパム攻撃を防ぐため、お手数をおかけしますが猫が写っている画像を以下から選択してください:',
'asirra-badlogin' => 'ウィキへの自動パスワードクラック攻撃を防ぐため、お手数をおかけしますが猫が写っている画像を以下から選択してください:',
'asirra-createaccount' => 'ウィキでのアカウント自動作成を防ぐため、お手数をおかけしますが猫が写っている画像を以下から選択してください:',
'asirra-createaccount-fail' => '猫を正しく選択してください。',
'asirra-create' => 'ウィキでのページ自動作成を防ぐため、お手数をおかけしますが猫が写っている画像を以下から選択してください:',
'asirra-nojs' => "'''JavaScript を有効にしてページを再読込してください。'''",
'asirra-failed' => '猫が写っている画像をすべて選択してください',
);
/** Korean (한국어)
* @author 아라
*/
$messages['ko'] = array(
'asirra-desc' => 'ConfirmEdit에 대한 Asirra 모듈',
'asirra-edit' => '자동화된 편집 스팸으로부터 보호하기 위해, 아래 상자에 있는 고양이 사진을 선택하세요:', # Fuzzy
'asirra-addurl' => '편집에 새로운 바깥 링크가 포함되어 있습니다. 자동화된 스팸으로부터 보호하기 위해, 아래 상자에 있는 고양이 사진을 선택하세요:', # Fuzzy
'asirra-badlogin' => '비밀번호 깨기로부터 보호하기 위해, 아래 상자에 있는 고양이 사진을 선택하세요:', # Fuzzy
'asirra-createaccount' => '자동화된 계정 만들기로부터 보호하기 위해, 아래 상자에 있는 고양이 사진을 선택하세요:', # Fuzzy
'asirra-createaccount-fail' => '고양이를 올바르게 선택하세요.',
'asirra-create' => '자동화된 문서 만들기로부터 보호하기 위해, 아래 상자에 있는 고양이 사진을 선택하세요:', # Fuzzy
'asirra-nojs' => "'''자바스크립트를 활성화하고 문서를 다시 제출하세요.'''",
'asirra-failed' => '고양이 그림을 모두 선택하세요',
);
/** Colognian (Ripoarisch)
* @author Purodha
*/
$messages['ksh'] = array(
'asirra-desc' => 'Dä Zohsaz <i lang="en">Asirra</i> för et Zohsazprojramm <i lang="en">ConfirmEdit</i>.',
'asirra-edit' => 'Heh dat Wiki well sesch jääje <i lang="en">SPAM</i> schöze. Dröm moß mer beim Ändere noch en Prööfong aflääje, dat mer ene Minsch un kei Projramm es. Söhk bloß de Katzebelder em Kaßte us:', # Fuzzy
'asirra-addurl' => 'Heh dat Wiki well sesch jääje <i lang="en">SPAM</i> schöze. Dröm moß mer, wam_mer lengks noh ußerhallef enfööje well, noch en Prööfong aflääje, dat mer ene Minsch un kei Projramm es. Söhk bloß de Katzebelder em Kaßte us:', # Fuzzy
'asirra-badlogin' => 'Heh dat Wiki well sesch jääje et automattesche Paßwoot_Knacke schöze. Dröm moß mer heh nor_en Prööfong aflääje, dat mer ene Minsch un kei Projramm es. Söhk bloß de Katzebelder em Kaßte us:', # Fuzzy
'asirra-createaccount' => 'Heh dat Wiki well sesch jääje automattesch aanjelaate „Metmaacher“ schöze. Dröm moß mer heh nor_en Prööfong aflääje, dat mer ene Minsch un kei Projramm es. Söhk bloß de Katzebelder em Kaßte us:', # Fuzzy
'asirra-createaccount-fail' => 'Bes esu jood un don de Kazebelder ußwähle.',
'asirra-create' => 'Heh dat Wiki well sesch jääje automattesch neu aanjelaate Sigge schöze. Dröm moß mer heh nor_en Prööfong aflääje, dat mer ene Minsch un kei Projramm es. Söhk bloß de Katzebelder em Kaßte us:', # Fuzzy
'asirra-nojs' => "'''Bes esu jood un donn JavaSkrep en Dingem Brauser aanschallde un scheck heh di Sigg norr_ens af.'''",
'asirra-failed' => 'Bes esu jood un don all de Kazebelder ußwähle.',
);
/** Luxembourgish (Lëtzebuergesch)
* @author Robby
*/
$messages['lb'] = array(
'asirra-desc' => 'Asirra-Modul fir ConfirmEdit',
'asirra-edit' => "Fir d'Wiki géint automatiséierte Spam ze schützen froe mir Iech just d'Fotoe mat Kazen, déi Dir an der Këscht ënnendrënner gesitt, erauszesichen:",
'asirra-addurl' => "An Ärer Ännerung sinn nei extern Linken. Fir d'Wiki géint automatiséierte Spam ze schützen, froe mir Iech d'Kategorie vun de Fotoen an der Këscht ënnendrënner erauszesichen:",
'asirra-createaccount-fail' => "Identifizéiert d'Kaze w.e.g. richteg.",
'asirra-nojs' => "'''Aktivéiert w.e.g. JavaScript a schéckt d'Säit nachemol.'''",
'asirra-failed' => 'Identifizéiert w.e.g. all Biller wou Kazen drop sinn',
);
/** Macedonian (македонски)
* @author Bjankuloski06
*/
$messages['mk'] = array(
'asirra-desc' => 'Asirra-модул за ПотврдиУредување',
'asirra-edit' => 'Како заштитна мерка против автоматизиран спам, би ве замолиле да ги изберете само сликите со мачка прикажани во полето:',
'asirra-addurl' => 'Во вашите измени има нови надворешни врски. Како заштитна мерка против автоматизиран спам, би ве замолиле да ги изберете само сликите со мачка прикажани во полето:',
'asirra-badlogin' => 'Како заштитна мерка против автоматизирано провалување на лозинки, би ве замолиле да ги изберете само сликите со мачка прикажани во полето:',
'asirra-createaccount' => 'Како заштитна мерка против автоматизирано создавање на сметки, би ве замолиле да ги изберете само сликите со мачка прикажани во полето:',
'asirra-createaccount-fail' => 'Посочете кои од следниве се мачки.',
'asirra-create' => 'Како заштитна мерка против автоматизирано создавање на страници, би ве замолиле да ги изберете само сликите со мачка прикажани во полето:',
'asirra-nojs' => "'''Овозможете JavaScript и поднесете ја страницата повторно.'''",
'asirra-failed' => 'Изберете ги сликите што имаат мачка',
);
/** Malay (Bahasa Melayu)
* @author Anakmalaysia
*/
$messages['ms'] = array(
'asirra-desc' => 'Modul Asirra untuk ConfirmEdit',
'asirra-edit' => 'Untuk mencegah suntingan spam automatik, sila pilih gambar-gambar kucing sahaja dalam petak di bawah:', # Fuzzy
'asirra-addurl' => 'Suntingan anda mengandungi pautan luar yang baru. Untuk mencegah spam janaan automatik, sila pilih gambar-gambar kucing sahaja dalam petak di bawah:', # Fuzzy
'asirra-badlogin' => 'Untuk mencegah pemecahan kata laluan automatik, sila pilih gambar-gambar kucing sahaja dalam petak di bawah:', # Fuzzy
'asirra-createaccount' => 'Untuk mencegah pembukaan akaun automatik, sila pilih gambar-gambar kucing sahaja dalam petak di bawah:', # Fuzzy
'asirra-createaccount-fail' => 'Sila kenal pasti kucing-kucing dengan betul.',
'asirra-create' => 'Untuk mencegah pembukaan halaman automatik, sila pilih gambar-gambar kucing sahaja dalam petak di bawah:', # Fuzzy
'asirra-nojs' => "'''Sila hidupkan JavaScript dan hantar semula halaman ini.'''",
'asirra-failed' => 'Sila kenal pasti semua gambar kucing',
);
/** Maltese (Malti)
* @author Chrisportelli
*/
$messages['mt'] = array(
'asirra-desc' => 'Modulu ASIRRA għal ConfirmEdit',
'asirra-edit' => "Sabiex tgħinna nipproteġu kontra l-modifiki li jżidu spam, jekk jogħġbok agħżel ir-ritratti tal-qtates fil-kaxxa t'hawn taħt:", # Fuzzy
'asirra-addurl' => "Il-modifika tiegħek tinkludi ħoloq esterni ġodda. Sabiex tipproteġi kontra spam awtomatiku, jekk jogħġbok agħżel ir-ritratti tal-qtates fil-kaxxa t'hawn taħt:", # Fuzzy
'asirra-badlogin' => "Sabiex tgħinna nipproteġu kontra l-infurzar awtomatiku tal-passwords, jekk jogħġbok agħżel ir-ritratti tal-qtates fil-kaxxa t'hawn taħt:", # Fuzzy
'asirra-createaccount' => "Sabiex tgħinna nipproteġu kontra l-ħolqien awtomatiku ta' kontijiet ġodda, jekk jogħġbok agħżel ir-ritratti tal-qtates fil-kaxxa t'hawn taħt:", # Fuzzy
'asirra-createaccount-fail' => 'Sib il-qtates.',
'asirra-create' => "Sabiex tgħinna nipproteġu kontra l-ħolqien awtomatiku ta' paġni, jekk jogħġbok agħżel ir-ritratti tal-qtates fil-kaxxa t'hawn taħt:", # Fuzzy
'asirra-nojs' => "'''Jekk jogħġbok attiva l-JavaScript u erġa' ibgħat din il-paġna.'''",
'asirra-failed' => 'Sib l-istampi kollha tal-qtates',
);
/** Norwegian Bokmål (norsk bokmål)
* @author Event
*/
$messages['nb'] = array(
'asirra-desc' => 'Assirra-modulen for ConfirmEdit',
'asirra-edit' => 'Som beskyttelse mot automatisk redigert spam, vennligst velg kun kattebildene i boksen under:', # Fuzzy
'asirra-addurl' => 'Din redigering inneholder nye eksterne lenker. Som beskyttelse mot automatisk redigert spam, vennligst velg kun kattebildene i boksen under:', # Fuzzy
'asirra-badlogin' => 'Som beskyttelse mot automatisk passordknekking, vennligst velg kun kattebildene i boksen under:', # Fuzzy
'asirra-createaccount' => 'Som beskyttelse mot automatisk opprettelse av brukerkonto, vennligst velg kun kattebildene i boksen under:', # Fuzzy
'asirra-createaccount-fail' => 'Vennligst angi hva som er katter.',
'asirra-create' => 'Som beskyttelse mot automatisk opprettelse av sider, vennligst velg kun kattebildene i boksen under:', # Fuzzy
'asirra-nojs' => "'''Vennligst åpne for JavaScript og lagre siden en gang til.'''",
'asirra-failed' => 'Vennligst merk alle kattebilder',
);
/** Dutch (Nederlands)
* @author HanV
* @author SPQRobin
* @author Siebrand
*/
$messages['nl'] = array(
'asirra-desc' => 'Asirra-module voor ConfirmEdit',
'asirra-edit' => 'Kies ter bescherming tegen geautomatiseerde spam de afbeeldingen met een poes in het onderstaande venster:',
'asirra-addurl' => "Uw bewerking bevat nieuwe externe koppelingen. Selecteer de foto's van katten in het vak hieronder om de wiki te beschermen tegen geautomatiseerde spam:",
'asirra-badlogin' => 'Kies ter bescherming tegen het automatisch kraken van wachtwoorden de afbeeldingen met een poes in het onderstaande venster:',
'asirra-createaccount' => 'Kies om het automatisch aanmaken van gebruikers tegen te gaan de afbeeldingen met een poes in het onderstaande venster:',
'asirra-createaccount-fail' => 'Identificeer de katten juist.',
'asirra-create' => 'Kies om het automatisch aanmaken van een pagina tegen te gaan de afbeeldingen met een poes in het onderstaande venster:',
'asirra-nojs' => "'''Schakel JavaScript in en probeer de pagina opnieuw op te slaan.'''",
'asirra-failed' => 'Identificeer alle afbeeldingen van katten.',
);
/** Nederlands (informeel) (Nederlands (informeel))
* @author Siebrand
*/
$messages['nl-informal'] = array(
'asirra-addurl' => "Je bewerking bevat nieuwe externe koppelingen. Selecteer de foto's van katten in het vak hieronder om te helpen beschermen tegen geautomatiseerde spam:", # Fuzzy
);
/** Polish (polski)
* @author BeginaFelicysym
*/
$messages['pl'] = array(
'asirra-desc' => 'Moduł Asirra dla ConfirmEdit',
'asirra-edit' => 'Aby uchronić się przed automatami wprowadzającymi spam, proszę wybrać tylko zdjęcia kotów w poniższym polu:', # Fuzzy
'asirra-addurl' => 'Wprowadzony przez ciebie tekst zawiera linki zewnętrzne. Aby uchronić nas przed automatycznym spamem, proszę wskazać tylko zdjęcia kotów w poniższym polu:', # Fuzzy
'asirra-badlogin' => 'Aby uchronić się przed zautomatyzowanym łamaniem haseł, proszę wybrać tylko zdjęcia kotów w poniższym polu:', # Fuzzy
'asirra-createaccount' => 'Aby uchronić się przed automatycznym tworzeniem kont, proszę wybrać tylko zdjęcia kotów w poniższym polu:', # Fuzzy
'asirra-createaccount-fail' => 'Prosimy prawidłowo zidentyfikować koty.',
'asirra-create' => 'Aby uchronić się przed automatycznym tworzeniem stron, proszę wybrać tylko zdjęcia kotów w poniższym polu:', # Fuzzy
'asirra-nojs' => "'''Prosimy włączyć obsługę języka JavaScript i ponowne przesłanie strony.'''",
'asirra-failed' => 'Prosimy wskazać wszystkie obrazy kotów',
);
/** Piedmontese (Piemontèis)
* @author Borichèt
* @author Dragonòt
*/
$messages['pms'] = array(
'asirra-desc' => 'Mòdul Asirra për ConfirmEdit',
'asirra-edit' => "Për giuté a protege contra la rumenta dle modìfiche automàtiche, për piasì ch'a selession-a mach le fòto ëd gat ant ël quàder sì-sota:", # Fuzzy
'asirra-addurl' => "Soa modìfica a conten dle liure esterne neuve. Për giuté a protege contra la rumenta dle modìfiche automàtiche, për piasì ch'a selession-a mach le fòto ëd gat ant ël quàder sì-sota:", # Fuzzy
'asirra-badlogin' => "Për giuté a protege contra la forsadura automatisà ëd le ciav, për piasì ch'a selession-a mach la fòto dël gat ant ël quàder sì-sota:", # Fuzzy
'asirra-createaccount' => "Për giuté a protege contra la creassion automatisà ëd cont, për piasì ch'a selession-a mach la fòto dël gat ant ël quàder sì-sota:", # Fuzzy
'asirra-createaccount-fail' => 'Për piasì identifica coretament ij gat.',
'asirra-create' => "Për giuté a protege contra la creassion automatisà ëd pàgine, për piasì ch'a selession-a mach le fòto ëd gat ant ël quàder sì-sota:", # Fuzzy
'asirra-nojs' => "'''Për piasì, ch'a abìlita JavaScript e ch'a spedissa torna la pàgina.'''",
'asirra-failed' => 'Për piasì identìfica tute le figure ëd gat',
);
/** Portuguese (português)
* @author Hamilton Abreu
* @author Luckas
*/
$messages['pt'] = array(
'asirra-desc' => 'Módulo Asirra para o ConfirmEdit',
'asirra-edit' => "Como prevenção contra sistemas automatizados de inserção de ''spam'', selecione só as fotografias de gatos na caixa abaixo:", # Fuzzy
'asirra-addurl' => "A sua edição contém links externos novos. Como prevenção contra sistemas automatizados de inserção de ''spam'', selecione só as fotografias de gatos na caixa abaixo:", # Fuzzy
'asirra-badlogin' => 'Como prevenção com sistemas automatizados de descoberta de palavras-chave, selecione só as fotografias de gatos na caixa abaixo:', # Fuzzy
'asirra-createaccount' => 'Como prevenção contra sistemas automatizados de criação de contas, selecione só as fotografias de gatos na caixa abaixo:', # Fuzzy
'asirra-createaccount-fail' => 'Identifique corretamente os gatos, por favor.',
'asirra-create' => 'Como prevenção contra sistemas automatizados de criação de páginas, selecione só as fotografias de gatos na caixa abaixo:', # Fuzzy
'asirra-nojs' => "'''Possibilite o uso de JavaScript e reenvie a página, por favor.'''",
);
/** tarandíne (tarandíne)
* @author Joetaras
*/
$messages['roa-tara'] = array(
'asirra-desc' => 'Module Asirra pe confermà le cangiaminde',
'asirra-edit' => "Pe darne 'na màne a proteggere condre le cangiaminde automatece de le rummate, pe piacere scacchie ìa categorije de le fote jndr'à buatte aqquà sotte:", # Fuzzy
'asirra-addurl' => "Le cangiaminde tune 'ngludone collegaminde de fore nuève. Pe darne 'na màne a proteggere condre le cangiaminde automatece de le rummate, pe piacere scacchie 'a categorije d'a fote 'ndruche jndr'à buatte aqquà sotte:", # Fuzzy
'asirra-badlogin' => "Pe darne 'na màne a proteggere condre le futteminde automatece de le passuord, pe piacere scacchie 'a categorije de le fote jndr'à buatte aqquà sotte:", # Fuzzy
'asirra-createaccount' => "Pe darne 'na màne a proteggere condre le ccrejaziune automatece de le cunde, pe piacere scacchie 'a categorije de le fote jndr'à buatte aqquà sotte:", # Fuzzy
'asirra-createaccount-fail' => 'Pe piacere idendifiche correttamende le categorije.',
'asirra-create' => "Pe darne 'na màne a proteggere condre le ccrejaziune automatece de le pàggene, pe piacere scacchie 'a categorije de le fote jndr'à buatte aqquà sotte:", # Fuzzy
'asirra-nojs' => "'''Pe piacere abbilite JavaScript e conferme arrete 'a pàgene.'''",
'asirra-failed' => 'Pe piacere idendifiche tutte le categorije de le immaggine',
);
/** Russian (русский)
* @author DCamer
* @author Lockal
* @author Okras
*/
$messages['ru'] = array(
'asirra-desc' => 'Модуль Asirra для ConfirmEdit',
'asirra-edit' => 'В целях защиты проекта от автоматического спама в правках, просим вас выбрать среди нижеприведённых изображений только фотографии кошек:',
'asirra-addurl' => 'Ваша правка содержит новые внешние ссылки. В целях защиты проекта от автоматического спама в правках просим вас выбрать среди нижеприведённых изображений только фотографии кошек:',
'asirra-badlogin' => 'В целях защиты от автоматического подбора пароля просим вас выбрать среди нижеприведённых изображений только фотографии кошек:',
'asirra-createaccount' => 'В целях защиты от автоматического создания учётных записей просим вас выбрать среди нижеприведённых изображений только фотографии кошек:',
'asirra-createaccount-fail' => 'Пожалуйста правильно идентифицируйте котов.',
'asirra-create' => 'В целях защиты от автоматического создания страниц просим вас выбрать среди нижеприведённых изображений только фотографии кошек:',
'asirra-nojs' => "'''Пожалуйста, включите JavaScript и обновите страницу.'''",
'asirra-failed' => 'Пожалуйста, идентифицируйте все фотографии кота',
);
/** Sinhala (සිංහල)
* @author පසිඳු කාවින්ද
*/
$messages['si'] = array(
'asirra-desc' => 'ConfirmEdit සඳහා Asirra මොඩියුලය',
'asirra-failed' => 'කරුණාකර සියලුම cat පින්තූරයන් හඳුනාගන්න',
);
/** Swedish (svenska)
* @author Jopparn
* @author Rotsee
* @author WikiPhoenix
*/
$messages['sv'] = array(
'asirra-edit' => 'För att skydda wikin mot spam ber vi dig att markera de fotografier som föreställer katter i rutan nedan:',
'asirra-addurl' => 'Din redigering innehåller nya externa länkar. För att skydda wikin mot automatiserat redigerings-spam ber vi dig att endast markera fotografierna på katter i rutan nedan:',
'asirra-badlogin' => 'För att skydda wikin mot automatiserade försök att knäcka lösenord ber vi dig att endast markera fotografierna på katter i rutan nedan:',
'asirra-createaccount' => 'För att skydda wikin mot automatiserat kontoskapande ber vi dig att endast markera fotografierna på katter i rutan nedan:',
'asirra-create' => 'För att skydda wikin mot automatiserat sidskapande ber vi dig att endast markera fotografierna på katter i rutan nedan:',
'asirra-nojs' => "'''Var god aktivera JavaScript och hämta sidan igen.'''",
'asirra-failed' => 'Var god identifiera alla kattbilder',
);
/** Tagalog (Tagalog)
* @author AnakngAraw
*/
$messages['tl'] = array(
'asirra-desc' => 'Modyul ng Asirra para sa ConfirmEdit',
'asirra-edit' => 'Upang makatulong sa pagprutekta laban sa kusang basurang pamamatnugot, paki piliin iyong mga litrato lamang ng pusa na nasa loob ng kahong nasa ibaba:', # Fuzzy
'asirra-addurl' => 'Ang pagbabago mo ay nagsasama ng bagong panlabas na mga kawing. Upang makatulong sa pagprutekta laban sa kusang paglusob ng basurang-liham, paki piliin iyong mga litrato lamang ng pusa na nasa loob ng kahong nasa ibaba:', # Fuzzy
'asirra-badlogin' => 'Upang makatulong sa pagprutekta laban sa kusang pag-alam ng hudyat, paki piliin lamang iyong mga litrato ng pusa na nasa loob ng kahong nasa ibaba:', # Fuzzy
'asirra-createaccount' => 'Upang makatulong sa pagprutekta laban sa kusang paglikha ng akawnt, paki piliin lamang iyong mga litrato ng pusa na nasa loob ng kahong nasa ibaba:', # Fuzzy
'asirra-createaccount-fail' => 'Paki kilalanin ng tama ang mga pusa.',
'asirra-create' => 'Upang makatulong sa pagprutekta laban sa kusang paglikha ng pahina, paki piliin lamang iyong mga litrato ng pusa na nasa loob ng kahong nasa ibaba:', # Fuzzy
'asirra-nojs' => "'''Paki paganahin ang JavaScript at muling ipasa ang pahina.'''",
'asirra-failed' => 'Paki kilalanin ang lahat ng mga imahe ng pusa',
);
/** Ukrainian (українська)
* @author Andriykopanytsia
* @author Base
*/
$messages['uk'] = array(
'asirra-desc' => 'Модуль Asirra для ConfirmEdit',
'asirra-edit' => 'З метою захисту вікі від автоматичного спаму у статтях, просимо вас обрати фотографії кота, у блоці нижче:',
'asirra-addurl' => 'Ваше повідомлення включає нові зовнішні посилання. З метою захисту вікі проти автоматичного спаму у статтях, просимо вас обрати фотографії кота, у блоці нижче:',
'asirra-badlogin' => 'З метою захисту вікі проти автоматичного підбору паролю, просимо вас обрати фотографії кота у блоці нижче:',
'asirra-createaccount' => 'З метою захисту вікі проти автоматичного створення облікових записів просимо вас обрати фотографії кота у блоці нижче:',
'asirra-createaccount-fail' => 'Будь ласка, правильно ідентифікуйте котів.',
'asirra-create' => 'З метою захисту вікі проти автоматичного створення статей просимо вас обрати фотографії кота у блоці нижче:',
'asirra-nojs' => "'''Будь ласка увімкніть JavaScript і оновіть сторінку.'''",
'asirra-failed' => 'Будь ласка, ідентифікуйте усі фотографії кота',
);
/** Walloon (walon)
* @author Srtxg
*/
$messages['wa'] = array(
'asirra-desc' => "Module Asirra pol passete d' acertinaedje des candjmints (ConfirmEdit)",
'asirra-edit' => "Po s' mete a houte des des robots di spam, nos vs dimandans d' acertiner ki vos estoz bén ene djin, po çoula tchoezixhoz seulmint les imådjes avou des tchets e l' boesse chal pa dzo:", # Fuzzy
'asirra-addurl' => "Dins vos candjmints i gn a des dfoûtrinnès hårdêyes (URL).
Po s' mete a houte des des robots di spam, nos vs dimandans d' acertiner ki vos estoz bén ene djin, po çoula tchoezixhoz seulmint les imådjes avou des tchets e l' boesse chal pa dzo:", # Fuzzy
'asirra-badlogin' => "Po s' mete a houte des des robots ki sayèt d' adviner les screts, nos vs dimandans d' acertiner ki vos estoz bén ene djin, po çoula tchoezixhoz seulmint les imådjes avou des tchets e l' boesse chal pa dzo:", # Fuzzy
'asirra-createaccount' => "Po s' mete a houte des des robots k' ahivèt des contes otomaticmint, nos vs dimandans d' acertiner ki vos estoz bén ene djin, po çoula tchoezixhoz seulmint les imådjes avou des tchets e l' boesse chal pa dzo:", # Fuzzy
'asirra-createaccount-fail' => 'Tchoezixhoz comifåt les tchets (les biesses ki gnawèt).',
'asirra-create' => "Po s' mete a houte des des robots k' ahivèt des pådjes otomaticmint, nos vs dimandans d' acertiner ki vos estoz bén ene djin, po çoula tchoezixhoz seulmint les imådjes avou des tchets e l' boesse chal pa dzo:", # Fuzzy
'asirra-nojs' => "'''Metoz s' i vs plait en alaedje li JavaScrit et s' revoyî l' pådje.'''",
'asirra-failed' => 'Idintifyî totes les imådjes avou des tchets',
);
/** Simplified Chinese (中文(简体)‎)
* @author Fantasticfears
* @author Hzy980512
*/
$messages['zh-hans'] = array(
'asirra-desc' => 'ConfirmEdit的Asirra模块',
'asirra-edit' => '为了防止垃圾编辑攻击,请在下面的方框中选出猫的图片:',
'asirra-addurl' => '您的编辑包含新的外部链接。为了帮助防止垃圾编辑攻击,请在下面的方框中选出猫的图片:',
'asirra-badlogin' => '为防止自动程序破解密码,请在下面的方框中选出猫的图片:',
'asirra-createaccount' => '为了防止自动程序创建帐户,请在下面的方框中选出猫的图片:',
'asirra-createaccount-fail' => '请正确辨认出猫的图片。',
'asirra-create' => '为了防止自动程序创建页面,请在下面的方框中选出猫的图片:',
'asirra-nojs' => "'''请启动JavaScript后再提交页面。'''",
'asirra-failed' => '请选出所有猫的图片',
);
/** Traditional Chinese (中文(繁體)‎)
*/
$messages['zh-hant'] = array(
'asirra-desc' => 'ConfirmEdit的Asirra模塊',
'asirra-edit' => '為了防止垃圾編輯攻擊,請在下面的方框中選出貓的圖片:', # Fuzzy
'asirra-addurl' => '您的編輯包含新的外部鏈接。為了幫助防止自動垃圾郵件,請在下面的方框中選出貓的圖片:', # Fuzzy
'asirra-badlogin' => '為防止自動程序破解密碼,請在下面的方框中選出貓的圖片:', # Fuzzy
'asirra-createaccount' => '為了防止自動程序創建帳戶,請在下面的方框中選出貓的圖片:', # Fuzzy
'asirra-createaccount-fail' => '請正確辨認出貓的圖片。',
'asirra-create' => '為了防止自動程序創建頁面,請在下面的方框中選出貓的圖片:', # Fuzzy
'asirra-nojs' => "'''請啟動JavaScript後再提交頁面。'''",
'asirra-failed' => '請選出所有貓的圖片',
);

View file

@ -0,0 +1,42 @@
<?php
/**
* Asirra CAPTCHA module for the ConfirmEdit MediaWiki extension.
* @author Bachsau
* @author Niklas Laxström
*
* Makes use of the Asirra (Animal Species Image Recognition for
* Restricting Access) CAPTCHA service, developed by John Douceur, Jeremy
* Elson and Jon Howell at Microsoft Research.
*
* Asirra uses a large set of images from http://petfinder.com.
*
* For more information about Asirra, see:
* http://research.microsoft.com/en-us/um/redmond/projects/asirra/
*
* This MediaWiki code is released into the public domain, without any
* warranty. YOU CAN DO WITH IT WHATEVER YOU LIKE!
*
* @file
* @ingroup Extensions
*/
if ( !defined( 'MEDIAWIKI' ) ) {
exit;
}
$dir = __DIR__;
require_once( $dir . '/ConfirmEdit.php' );
$wgCaptchaClass = 'Asirra';
$wgExtensionMessagesFiles['Asirra'] = $dir . '/Asirra.i18n.php';
$wgAutoloadClasses['Asirra'] = $dir . '/Asirra.class.php';
$wgResourceModules['ext.confirmEdit.asirra'] = array(
'localBasePath' => $dir . '/resources',
'remoteExtPath' => 'ConfirmEdit/resources',
'scripts' => 'ext.confirmEdit.asirra.js',
'messages' => array(
'asirra-failed',
),
);

View file

@ -0,0 +1,736 @@
<?php
class SimpleCaptcha {
function getCaptcha() {
$a = mt_rand( 0, 100 );
$b = mt_rand( 0, 10 );
/* Minus sign is used in the question. UTF-8,
since the api uses text/plain, not text/html */
$op = mt_rand( 0, 1 ) ? '+' : '';
// No space before and after $op, to ensure correct
// directionality.
$test = "$a$op$b";
$answer = ( $op == '+' ) ? ( $a + $b ) : ( $a - $b );
return array( 'question' => $test, 'answer' => $answer );
}
function addCaptchaAPI( &$resultArr ) {
$captcha = $this->getCaptcha();
$index = $this->storeCaptcha( $captcha );
$resultArr['captcha']['type'] = 'simple';
$resultArr['captcha']['mime'] = 'text/plain';
$resultArr['captcha']['id'] = $index;
$resultArr['captcha']['question'] = $captcha['question'];
}
/**
* Insert a captcha prompt into the edit form.
* This sample implementation generates a simple arithmetic operation;
* it would be easy to defeat by machine.
*
* Override this!
*
* @return string HTML
*/
function getForm() {
$captcha = $this->getCaptcha();
$index = $this->storeCaptcha( $captcha );
return "<p><label for=\"wpCaptchaWord\">{$captcha['question']}</label> = " .
Xml::element( 'input', array(
'name' => 'wpCaptchaWord',
'id' => 'wpCaptchaWord',
'size' => 5,
'autocomplete' => 'off',
'tabindex' => 1 ) ) . // tab in before the edit textarea
"</p>\n" .
Xml::element( 'input', array(
'type' => 'hidden',
'name' => 'wpCaptchaId',
'id' => 'wpCaptchaId',
'value' => $index ) );
}
/**
* Insert the captcha prompt into an edit form.
* @param OutputPage $out
*/
function editCallback( &$out ) {
$out->addWikiText( $this->getMessage( $this->action ) );
$out->addHTML( $this->getForm() );
}
/**
* Show a message asking the user to enter a captcha on edit
* The result will be treated as wiki text
*
* @param $action string Action being performed
* @return string
*/
function getMessage( $action ) {
$name = 'captcha-' . $action;
$text = wfMessage( $name )->text();
# Obtain a more tailored message, if possible, otherwise, fall back to
# the default for edits
return wfMessage( $name, $text )->isDisabled() ? wfMessage( 'captcha-edit' )->text() : $text;
}
/**
* Inject whazawhoo
* @fixme if multiple thingies insert a header, could break
* @param $form HTMLForm
* @return bool true to keep running callbacks
*/
function injectEmailUser( &$form ) {
global $wgCaptchaTriggers, $wgOut, $wgUser;
if ( $wgCaptchaTriggers['sendemail'] ) {
if ( $wgUser->isAllowed( 'skipcaptcha' ) ) {
wfDebug( "ConfirmEdit: user group allows skipping captcha on email sending\n" );
return true;
}
$form->addFooterText(
"<div class='captcha'>" .
$wgOut->parse( $this->getMessage( 'sendemail' ) ) .
$this->getForm() .
"</div>\n" );
}
return true;
}
/**
* Inject whazawhoo
* @fixme if multiple thingies insert a header, could break
* @param QuickTemplate $template
* @return bool true to keep running callbacks
*/
function injectUserCreate( &$template ) {
global $wgCaptchaTriggers, $wgOut, $wgUser;
if ( $wgCaptchaTriggers['createaccount'] ) {
if ( $wgUser->isAllowed( 'skipcaptcha' ) ) {
wfDebug( "ConfirmEdit: user group allows skipping captcha on account creation\n" );
return true;
}
$template->set( 'header',
"<div class='captcha'>" .
$wgOut->parse( $this->getMessage( 'createaccount' ) ) .
$this->getForm() .
"</div>\n" );
}
return true;
}
/**
* Inject a captcha into the user login form after a failed
* password attempt as a speedbump for mass attacks.
* @fixme if multiple thingies insert a header, could break
* @param $template QuickTemplate
* @return bool true to keep running callbacks
*/
function injectUserLogin( &$template ) {
if ( $this->isBadLoginTriggered() ) {
global $wgOut;
$template->set( 'header',
"<div class='captcha'>" .
$wgOut->parse( $this->getMessage( 'badlogin' ) ) .
$this->getForm() .
"</div>\n" );
}
return true;
}
/**
* When a bad login attempt is made, increment an expiring counter
* in the memcache cloud. Later checks for this may trigger a
* captcha display to prevent too many hits from the same place.
* @param User $user
* @param string $password
* @param int $retval authentication return value
* @return bool true to keep running callbacks
*/
function triggerUserLogin( $user, $password, $retval ) {
global $wgCaptchaTriggers, $wgCaptchaBadLoginExpiration, $wgMemc;
if ( $retval == LoginForm::WRONG_PASS && $wgCaptchaTriggers['badlogin'] ) {
$key = $this->badLoginKey();
$count = $wgMemc->get( $key );
if ( !$count ) {
$wgMemc->add( $key, 0, $wgCaptchaBadLoginExpiration );
}
$wgMemc->incr( $key );
}
return true;
}
/**
* Check if a bad login has already been registered for this
* IP address. If so, require a captcha.
* @return bool
* @access private
*/
function isBadLoginTriggered() {
global $wgMemc, $wgCaptchaTriggers, $wgCaptchaBadLoginAttempts;
return $wgCaptchaTriggers['badlogin'] && intval( $wgMemc->get( $this->badLoginKey() ) ) >= $wgCaptchaBadLoginAttempts;
}
/**
* Check if the IP is allowed to skip captchas
*/
function isIPWhitelisted() {
global $wgCaptchaWhitelistIP;
if ( $wgCaptchaWhitelistIP ) {
global $wgRequest;
$ip = $wgRequest->getIP();
foreach ( $wgCaptchaWhitelistIP as $range ) {
if ( IP::isInRange( $ip, $range ) ) {
return true;
}
}
}
return false;
}
/**
* Internal cache key for badlogin checks.
* @return string
* @access private
*/
function badLoginKey() {
global $wgRequest;
$ip = $wgRequest->getIP();
return wfMemcKey( 'captcha', 'badlogin', 'ip', $ip );
}
/**
* Check if the submitted form matches the captcha session data provided
* by the plugin when the form was generated.
*
* Override this!
*
* @param string $answer
* @param array $info
* @return bool
*/
function keyMatch( $answer, $info ) {
return $answer == $info['answer'];
}
// ----------------------------------
/**
* @param EditPage $editPage
* @param string $action (edit/create/addurl...)
* @return bool true if action triggers captcha on editPage's namespace
*/
function captchaTriggers( &$editPage, $action ) {
global $wgCaptchaTriggers, $wgCaptchaTriggersOnNamespace;
// Special config for this NS?
if ( isset( $wgCaptchaTriggersOnNamespace[$editPage->mTitle->getNamespace()][$action] ) )
return $wgCaptchaTriggersOnNamespace[$editPage->mTitle->getNamespace()][$action];
return ( !empty( $wgCaptchaTriggers[$action] ) ); // Default
}
/**
* @param $editPage EditPage
* @param $newtext string
* @param $section string
* @param $merged bool
* @return bool true if the captcha should run
*/
function shouldCheck( &$editPage, $newtext, $section, $merged = false ) {
$this->trigger = '';
$title = $editPage->mArticle->getTitle();
global $wgUser;
if ( $wgUser->isAllowed( 'skipcaptcha' ) ) {
wfDebug( "ConfirmEdit: user group allows skipping captcha\n" );
return false;
}
if ( $this->isIPWhitelisted() )
return false;
global $wgEmailAuthentication, $ceAllowConfirmedEmail;
if ( $wgEmailAuthentication && $ceAllowConfirmedEmail &&
$wgUser->isEmailConfirmed() ) {
wfDebug( "ConfirmEdit: user has confirmed mail, skipping captcha\n" );
return false;
}
if ( $this->captchaTriggers( $editPage, 'edit' ) ) {
// Check on all edits
global $wgUser;
$this->trigger = sprintf( "edit trigger by '%s' at [[%s]]",
$wgUser->getName(),
$title->getPrefixedText() );
$this->action = 'edit';
wfDebug( "ConfirmEdit: checking all edits...\n" );
return true;
}
if ( $this->captchaTriggers( $editPage, 'create' ) && !$editPage->mTitle->exists() ) {
// Check if creating a page
global $wgUser;
$this->trigger = sprintf( "Create trigger by '%s' at [[%s]]",
$wgUser->getName(),
$title->getPrefixedText() );
$this->action = 'create';
wfDebug( "ConfirmEdit: checking on page creation...\n" );
return true;
}
if ( $this->captchaTriggers( $editPage, 'addurl' ) ) {
// Only check edits that add URLs
if ( $merged ) {
// Get links from the database
$oldLinks = $this->getLinksFromTracker( $title );
// Share a parse operation with Article::doEdit()
$editInfo = $editPage->mArticle->prepareTextForEdit( $newtext );
$newLinks = array_keys( $editInfo->output->getExternalLinks() );
} else {
// Get link changes in the slowest way known to man
$oldtext = $this->loadText( $editPage, $section );
$oldLinks = $this->findLinks( $editPage, $oldtext );
$newLinks = $this->findLinks( $editPage, $newtext );
}
$unknownLinks = array_filter( $newLinks, array( &$this, 'filterLink' ) );
$addedLinks = array_diff( $unknownLinks, $oldLinks );
$numLinks = count( $addedLinks );
if ( $numLinks > 0 ) {
global $wgUser;
$this->trigger = sprintf( "%dx url trigger by '%s' at [[%s]]: %s",
$numLinks,
$wgUser->getName(),
$title->getPrefixedText(),
implode( ", ", $addedLinks ) );
$this->action = 'addurl';
return true;
}
}
global $wgCaptchaRegexes;
if ( $wgCaptchaRegexes ) {
// Custom regex checks. Reuse $oldtext if set above.
$oldtext = isset( $oldtext ) ? $oldtext : $this->loadText( $editPage, $section );
foreach ( $wgCaptchaRegexes as $regex ) {
$newMatches = array();
if ( preg_match_all( $regex, $newtext, $newMatches ) ) {
$oldMatches = array();
preg_match_all( $regex, $oldtext, $oldMatches );
$addedMatches = array_diff( $newMatches[0], $oldMatches[0] );
$numHits = count( $addedMatches );
if ( $numHits > 0 ) {
global $wgUser;
$this->trigger = sprintf( "%dx %s at [[%s]]: %s",
$numHits,
$regex,
$wgUser->getName(),
$title->getPrefixedText(),
implode( ", ", $addedMatches ) );
$this->action = 'edit';
return true;
}
}
}
}
return false;
}
/**
* Filter callback function for URL whitelisting
* @param $url string to check
* @return bool true if unknown, false if whitelisted
* @access private
*/
function filterLink( $url ) {
global $wgCaptchaWhitelist;
$source = wfMessage( 'captcha-addurl-whitelist' )->inContentLanguage()->text();
$whitelist = wfMessage( 'captcha-addurl-whitelist', $source )->isDisabled()
? false
: $this->buildRegexes( explode( "\n", $source ) );
$cwl = $wgCaptchaWhitelist !== false ? preg_match( $wgCaptchaWhitelist, $url ) : false;
$wl = $whitelist !== false ? preg_match( $whitelist, $url ) : false;
return !( $cwl || $wl );
}
/**
* Build regex from whitelist
* @param $lines string from [[MediaWiki:Captcha-addurl-whitelist]]
* @return string Regex or bool false if whitelist is empty
* @access private
*/
function buildRegexes( $lines ) {
# Code duplicated from the SpamBlacklist extension (r19197)
# Strip comments and whitespace, then remove blanks
$lines = array_filter( array_map( 'trim', preg_replace( '/#.*$/', '', $lines ) ) );
# No lines, don't make a regex which will match everything
if ( count( $lines ) == 0 ) {
wfDebug( "No lines\n" );
return false;
} else {
# Make regex
# It's faster using the S modifier even though it will usually only be run once
// $regex = 'http://+[a-z0-9_\-.]*(' . implode( '|', $lines ) . ')';
// return '/' . str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $regex) ) . '/Si';
$regexes = '';
$regexStart = '/^https?:\/\/+[a-z0-9_\-.]*(';
$regexEnd = ')/Si';
$regexMax = 4096;
$build = false;
foreach ( $lines as $line ) {
// FIXME: not very robust size check, but should work. :)
if ( $build === false ) {
$build = $line;
} elseif ( strlen( $build ) + strlen( $line ) > $regexMax ) {
$regexes .= $regexStart .
str_replace( '/', '\/', preg_replace( '|\\\*/|', '/', $build ) ) .
$regexEnd;
$build = $line;
} else {
$build .= '|' . $line;
}
}
if ( $build !== false ) {
$regexes .= $regexStart .
str_replace( '/', '\/', preg_replace( '|\\\*/|', '/', $build ) ) .
$regexEnd;
}
return $regexes;
}
}
/**
* Load external links from the externallinks table
* @param $title Title
* @return Array
*/
function getLinksFromTracker( $title ) {
$dbr = wfGetDB( DB_SLAVE );
$id = $title->getArticleID(); // should be zero queries
$res = $dbr->select( 'externallinks', array( 'el_to' ),
array( 'el_from' => $id ), __METHOD__ );
$links = array();
foreach ( $res as $row ) {
$links[] = $row->el_to;
}
return $links;
}
/**
* Backend function for confirmEdit() and confirmEditAPI()
* @param $editPage EditPage
* @param $newtext string
* @param $section
* @param $merged bool
* @return bool false if the CAPTCHA is rejected, true otherwise
*/
private function doConfirmEdit( $editPage, $newtext, $section, $merged = false ) {
global $wgRequest;
if ( $wgRequest->getVal( 'captchaid' ) ) {
$wgRequest->setVal( 'wpCaptchaId', $wgRequest->getVal( 'captchaid' ) );
}
if ( $wgRequest->getVal( 'captchaword' ) ) {
$wgRequest->setVal( 'wpCaptchaWord', $wgRequest->getVal( 'captchaword' ) );
}
if ( $this->shouldCheck( $editPage, $newtext, $section, $merged ) ) {
return $this->passCaptcha();
} else {
wfDebug( "ConfirmEdit: no need to show captcha.\n" );
return true;
}
}
/**
* The main callback run on edit attempts.
* @param EditPage $editPage
* @param string $newtext
* @param string $section
* @param bool $merged
* @return bool true to continue saving, false to abort and show a captcha form
*/
function confirmEdit( $editPage, $newtext, $section, $merged = false ) {
if ( defined( 'MW_API' ) ) {
# API mode
# The CAPTCHA was already checked and approved
return true;
}
if ( !$this->doConfirmEdit( $editPage, $newtext, $section, $merged ) ) {
$editPage->showEditForm( array( &$this, 'editCallback' ) );
return false;
}
return true;
}
/**
* A more efficient edit filter callback based on the text after section merging
* @param EditPage $editPage
* @param string $newtext
* @return bool
*/
function confirmEditMerged( $editPage, $newtext ) {
return $this->confirmEdit( $editPage, $newtext, false, true );
}
function confirmEditAPI( $editPage, $newtext, &$resultArr ) {
if ( !$this->doConfirmEdit( $editPage, $newtext, false, false ) ) {
$this->addCaptchaAPI( $resultArr );
return false;
}
return true;
}
/**
* Hook for user creation form submissions.
* @param User $u
* @param string $message
* @return bool true to continue, false to abort user creation
*/
function confirmUserCreate( $u, &$message ) {
global $wgCaptchaTriggers, $wgUser;
if ( $wgCaptchaTriggers['createaccount'] ) {
if ( $wgUser->isAllowed( 'skipcaptcha' ) ) {
wfDebug( "ConfirmEdit: user group allows skipping captcha on account creation\n" );
return true;
}
if ( $this->isIPWhitelisted() )
return true;
$this->trigger = "new account '" . $u->getName() . "'";
if ( !$this->passCaptcha() ) {
$message = wfMessage( 'captcha-createaccount-fail' )->text();
return false;
}
}
return true;
}
/**
* Hook for user login form submissions.
* @param $u User
* @param $pass
* @param $retval
* @return bool true to continue, false to abort user creation
*/
function confirmUserLogin( $u, $pass, &$retval ) {
if ( $this->isBadLoginTriggered() ) {
if ( $this->isIPWhitelisted() )
return true;
$this->trigger = "post-badlogin login '" . $u->getName() . "'";
if ( !$this->passCaptcha() ) {
// Emulate a bad-password return to confuse the shit out of attackers
$retval = LoginForm::WRONG_PASS;
return false;
}
}
return true;
}
/**
* Check the captcha on Special:EmailUser
* @param $from MailAddress
* @param $to MailAddress
* @param $subject String
* @param $text String
* @param $error String reference
* @return Bool true to continue saving, false to abort and show a captcha form
*/
function confirmEmailUser( $from, $to, $subject, $text, &$error ) {
global $wgCaptchaTriggers, $wgUser;
if ( $wgCaptchaTriggers['sendemail'] ) {
if ( $wgUser->isAllowed( 'skipcaptcha' ) ) {
wfDebug( "ConfirmEdit: user group allows skipping captcha on email sending\n" );
return true;
}
if ( $this->isIPWhitelisted() )
return true;
if ( defined( 'MW_API' ) ) {
# API mode
# Asking for captchas in the API is really silly
$error = wfMessage( 'captcha-disabledinapi' )->text();
return false;
}
$this->trigger = "{$wgUser->getName()} sending email";
if ( !$this->passCaptcha() ) {
$error = wfMessage( 'captcha-sendemail-fail' )->text();
return false;
}
}
return true;
}
/**
* @param $module ApiBase
* @return bool
*/
protected function isAPICaptchaModule( $module ) {
return $module instanceof ApiEditPage;
}
/**
* @param $module ApiBase
* @param $params array
* @param $flags int
* @return bool
*/
public function APIGetAllowedParams( &$module, &$params, $flags ) {
if ( $flags && $this->isAPICaptchaModule( $module ) ) {
$params['captchaword'] = null;
$params['captchaid'] = null;
}
return true;
}
/**
* @param $module ApiBase
* @param $desc array
* @return bool
*/
public function APIGetParamDescription( &$module, &$desc ) {
if ( $this->isAPICaptchaModule( $module ) ) {
$desc['captchaid'] = 'CAPTCHA ID from previous request';
$desc['captchaword'] = 'Answer to the CAPTCHA';
}
return true;
}
/**
* Given a required captcha run, test form input for correct
* input on the open session.
* @return bool if passed, false if failed or new session
*/
function passCaptcha() {
$info = $this->retrieveCaptcha();
if ( $info ) {
global $wgRequest;
if ( $this->keyMatch( $wgRequest->getVal( 'wpCaptchaWord' ), $info ) ) {
$this->log( "passed" );
$this->clearCaptcha( $info );
return true;
} else {
$this->clearCaptcha( $info );
$this->log( "bad form input" );
return false;
}
} else {
$this->log( "new captcha session" );
return false;
}
}
/**
* Log the status and any triggering info for debugging or statistics
* @param string $message
*/
function log( $message ) {
wfDebugLog( 'captcha', 'ConfirmEdit: ' . $message . '; ' . $this->trigger );
}
/**
* Generate a captcha session ID and save the info in PHP's session storage.
* (Requires the user to have cookies enabled to get through the captcha.)
*
* A random ID is used so legit users can make edits in multiple tabs or
* windows without being unnecessarily hobbled by a serial order requirement.
* Pass the returned id value into the edit form as wpCaptchaId.
*
* @param array $info data to store
* @return string captcha ID key
*/
function storeCaptcha( $info ) {
if ( !isset( $info['index'] ) ) {
// Assign random index if we're not udpating
$info['index'] = strval( mt_rand() );
}
CaptchaStore::get()->store( $info['index'], $info );
return $info['index'];
}
/**
* Fetch this session's captcha info.
* @return mixed array of info, or false if missing
*/
function retrieveCaptcha() {
global $wgRequest;
$index = $wgRequest->getVal( 'wpCaptchaId' );
return CaptchaStore::get()->retrieve( $index );
}
/**
* Clear out existing captcha info from the session, to ensure
* it can't be reused.
*/
function clearCaptcha( $info ) {
CaptchaStore::get()->clear( $info['index'] );
}
/**
* Retrieve the current version of the page or section being edited...
* @param EditPage $editPage
* @param string $section
* @return string
* @access private
*/
function loadText( $editPage, $section ) {
$rev = Revision::newFromTitle( $editPage->mTitle, false, Revision::READ_LATEST );
if ( is_null( $rev ) ) {
return "";
} else {
$text = $rev->getText();
if ( $section != '' ) {
global $wgParser;
return $wgParser->getSection( $text, $section );
} else {
return $text;
}
}
}
/**
* Extract a list of all recognized HTTP links in the text.
* @param $editpage EditPage
* @param $text string
* @return array of strings
*/
function findLinks( &$editpage, $text ) {
global $wgParser, $wgUser;
$options = new ParserOptions();
$text = $wgParser->preSaveTransform( $text, $editpage->mTitle, $wgUser, $options );
$out = $wgParser->parse( $text, $editpage->mTitle, $options );
return array_keys( $out->getExternalLinks() );
}
/**
* Show a page explaining what this wacky thing is.
*/
function showHelp() {
global $wgOut;
$wgOut->setPageTitle( wfMessage( 'captchahelp-title' )->text() );
$wgOut->addWikiMsg( 'captchahelp-text' );
if ( CaptchaStore::get()->cookiesNeeded() ) {
$wgOut->addWikiMsg( 'captchahelp-cookies-needed' );
}
}
}

View file

@ -0,0 +1,116 @@
<?php
abstract class CaptchaStore {
/**
* Store the correct answer for a given captcha
* @param $index String
* @param $info String the captcha result
*/
public abstract function store( $index, $info );
/**
* Retrieve the answer for a given captcha
* @param $index String
* @return String
*/
public abstract function retrieve( $index );
/**
* Delete a result once the captcha has been used, so it cannot be reused
* @param $index
*/
public abstract function clear( $index );
/**
* Whether this type of CaptchaStore needs cookies
* @return Bool
*/
public abstract function cookiesNeeded();
/**
* The singleton instance
* @var CaptchaStore
*/
private static $instance;
/**
* Get somewhere to store captcha data that will persist between requests
*
* @throws MWException
* @return CaptchaStore
*/
public final static function get() {
if ( !self::$instance instanceof self ) {
global $wgCaptchaStorageClass;
if ( in_array( 'CaptchaStore', class_parents( $wgCaptchaStorageClass ) ) ) {
self::$instance = new $wgCaptchaStorageClass;
} else {
throw new MWException( "Invalid CaptchaStore class $wgCaptchaStorageClass" );
}
}
return self::$instance;
}
/**
* Protected constructor: no creating instances except through the factory method above
*/
protected function __construct() {}
}
class CaptchaSessionStore extends CaptchaStore {
protected function __construct() {
// Make sure the session is started
if ( session_id() === '' ) {
wfSetupSession();
}
}
function store( $index, $info ) {
$_SESSION['captcha' . $info['index']] = $info;
}
function retrieve( $index ) {
if ( isset( $_SESSION['captcha' . $index] ) ) {
return $_SESSION['captcha' . $index];
} else {
return false;
}
}
function clear( $index ) {
unset( $_SESSION['captcha' . $index] );
}
function cookiesNeeded() {
return true;
}
}
class CaptchaCacheStore extends CaptchaStore {
function store( $index, $info ) {
global $wgMemc, $wgCaptchaSessionExpiration;
$wgMemc->set( wfMemcKey( 'captcha', $index ), $info,
$wgCaptchaSessionExpiration );
}
function retrieve( $index ) {
global $wgMemc;
$info = $wgMemc->get( wfMemcKey( 'captcha', $index ) );
if ( $info ) {
return $info;
} else {
return false;
}
}
function clear( $index ) {
global $wgMemc;
$wgMemc->delete( wfMemcKey( 'captcha', $index ) );
}
function cookiesNeeded() {
return false;
}
}

View file

@ -0,0 +1,120 @@
<?php
/**
* Aliases for special pages
*
* @file
* @ingroup Extensions
*/
// @codingStandardsIgnoreFile
$specialPageAliases = array();
/** English (English) */
$specialPageAliases['en'] = array(
'Captcha' => array( 'Captcha' ),
);
/** Arabic (العربية) */
$specialPageAliases['ar'] = array(
'Captcha' => array( 'كابتشا' ),
);
/** Egyptian Spoken Arabic (مصرى) */
$specialPageAliases['arz'] = array(
'Captcha' => array( 'كابتشا' ),
);
/** Min Dong Chinese (Mìng-dĕ̤ng-ngṳ̄) */
$specialPageAliases['cdo'] = array(
'Captcha' => array( '驗證碼' ),
);
/** Zazaki (Zazaki) */
$specialPageAliases['diq'] = array(
'Captcha' => array( 'Kodaİtimadi' ),
);
/** Esperanto (Esperanto) */
$specialPageAliases['eo'] = array(
'Captcha' => array( 'Kontraŭspamilo' ),
);
/** Estonian (eesti) */
$specialPageAliases['et'] = array(
'Captcha' => array( 'Robotilõks' ),
);
/** Persian (فارسی) */
$specialPageAliases['fa'] = array(
'Captcha' => array( 'کپچا' ),
);
/** Finnish (suomi) */
$specialPageAliases['fi'] = array(
'Captcha' => array( 'Ihmiskäyttäjävarmistus' ),
);
/** Galician (galego) */
$specialPageAliases['gl'] = array(
'Captcha' => array( 'Captcha' ),
);
/** Hindi (हिन्दी) */
$specialPageAliases['hi'] = array(
'Captcha' => array( 'कैप्चा', 'कैपचा' ),
);
/** Japanese (日本語) */
$specialPageAliases['ja'] = array(
'Captcha' => array( '画像認証' ),
);
/** Korean (한국어) */
$specialPageAliases['ko'] = array(
'Captcha' => array( '캡차' ),
);
/** Colognian (Ripoarisch) */
$specialPageAliases['ksh'] = array(
'Captcha' => array( 'Kaptscha' ),
);
/** Cornish (kernowek) */
$specialPageAliases['kw'] = array(
'Captcha' => array( 'Captcha' ),
);
/** Macedonian (македонски) */
$specialPageAliases['mk'] = array(
'Captcha' => array( 'Captcha' ),
);
/** Malayalam (മലയാളം) */
$specialPageAliases['ml'] = array(
'Captcha' => array( 'ക്യാപ്ച' ),
);
/** Serbian (Cyrillic script) (српски (ћирилица)) */
$specialPageAliases['sr-ec'] = array(
'Captcha' => array( отврдни_код' ),
);
/** Turkish (Türkçe) */
$specialPageAliases['tr'] = array(
'Captcha' => array( 'GüvenlikKodu' ),
);
/** Cantonese (粵語) */
$specialPageAliases['yue'] = array(
'Captcha' => array( '驗證碼' ),
);
/** Simplified Chinese (中文(简体)‎) */
$specialPageAliases['zh-hans'] = array(
'Captcha' => array( '验证码' ),
);
/** Traditional Chinese (中文(繁體)‎) */
$specialPageAliases['zh-hant'] = array(
'Captcha' => array( '驗證碼' ),
);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,216 @@
<?php
/**
* ConfirmEdit MediaWiki extension.
*
* This is a framework that holds a variety of CAPTCHA tools. The
* default one, 'SimpleCaptcha', is not intended as a production-
* level CAPTCHA system, and another one of the options provided
* should be used in its place for any real usages.
*
* Copyright (C) 2005-2007 Brion Vibber <brion@wikimedia.org>
* http://www.mediawiki.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Extensions
*/
if ( !defined( 'MEDIAWIKI' ) ) {
exit;
}
$wgExtensionFunctions[] = 'confirmEditSetup';
$wgExtensionCredits['antispam'][] = array(
'path' => __FILE__,
'name' => 'ConfirmEdit',
'author' => array( 'Brion Vibber', '...' ),
'url' => 'https://www.mediawiki.org/wiki/Extension:ConfirmEdit',
'version' => '1.2',
'descriptionmsg' => 'captcha-desc',
);
/**
* The 'skipcaptcha' permission key can be given out to
* let known-good users perform triggering actions without
* having to go through the captcha.
*
* By default, sysops and registered bot accounts will be
* able to skip, while others have to go through it.
*/
$wgGroupPermissions['*' ]['skipcaptcha'] = false;
$wgGroupPermissions['user' ]['skipcaptcha'] = false;
$wgGroupPermissions['autoconfirmed']['skipcaptcha'] = false;
$wgGroupPermissions['bot' ]['skipcaptcha'] = true; // registered bots
$wgGroupPermissions['sysop' ]['skipcaptcha'] = true;
$wgAvailableRights[] = 'skipcaptcha';
/**
* List of IP ranges to allow to skip the captcha, similar to the group setting:
* "$wgGroupPermission[...]['skipcaptcha'] = true"
*
* Specific IP addresses or CIDR-style ranges may be used,
* for instance:
* $wgCaptchaWhitelistIP = array('192.168.1.0/24', '10.1.0.0/16');
*/
$wgCaptchaWhitelistIP = false;
$wgCaptcha = null;
$wgCaptchaClass = 'SimpleCaptcha';
/**
* Actions which can trigger a captcha
*
* If the 'edit' trigger is on, *every* edit will trigger the captcha.
* This may be useful for protecting against vandalbot attacks.
*
* If using the default 'addurl' trigger, the captcha will trigger on
* edits that include URLs that aren't in the current version of the page.
* This should catch automated linkspammers without annoying people when
* they make more typical edits.
*
* The captcha code should not use $wgCaptchaTriggers, but CaptchaTriggers()
* which also takes into account per namespace triggering.
*/
$wgCaptchaTriggers = array();
$wgCaptchaTriggers['edit'] = false; // Would check on every edit
$wgCaptchaTriggers['create'] = false; // Check on page creation.
$wgCaptchaTriggers['sendemail'] = false; // Special:Emailuser
$wgCaptchaTriggers['addurl'] = true; // Check on edits that add URLs
$wgCaptchaTriggers['createaccount'] = true; // Special:Userlogin&type=signup
$wgCaptchaTriggers['badlogin'] = true; // Special:Userlogin after failure
/**
* You may wish to apply special rules for captcha triggering on some namespaces.
* $wgCaptchaTriggersOnNamespace[<namespace id>][<trigger>] forces an always on /
* always off configuration with that trigger for the given namespace.
* Leave unset to use the global options ($wgCaptchaTriggers).
*
* Shall not be used with 'createaccount' (it is not checked).
*/
$wgCaptchaTriggersOnNamespace = array();
# Example:
# $wgCaptchaTriggersOnNamespace[NS_TALK]['create'] = false; //Allow creation of talk pages without captchas.
# $wgCaptchaTriggersOnNamespace[NS_PROJECT]['edit'] = true; //Show captcha whenever editing Project pages.
/**
* Indicate how to store per-session data required to match up the
* internal captcha data with the editor.
*
* 'CaptchaSessionStore' uses PHP's session storage, which is cookie-based
* and may fail for anons with cookies disabled.
*
* 'CaptchaCacheStore' uses $wgMemc, which avoids the cookie dependency
* but may be fragile depending on cache configuration.
*/
$wgCaptchaStorageClass = 'CaptchaSessionStore';
/**
* Number of seconds a captcha session should last in the data cache
* before expiring when managing through CaptchaCacheStore class.
*
* Default is a half hour.
*/
$wgCaptchaSessionExpiration = 30 * 60;
/**
* Number of seconds after a bad login that a captcha will be shown to
* that client on the login form to slow down password-guessing bots.
*
* Has no effect if 'badlogin' is disabled in $wgCaptchaTriggers or
* if there is not a caching engine enabled.
*
* Default is five minutes.
*/
$wgCaptchaBadLoginExpiration = 5 * 60;
/**
* Allow users who have confirmed their email addresses to post
* URL links without being harassed by the captcha.
*/
$ceAllowConfirmedEmail = false;
/**
* Number of bad login attempts before triggering the captcha. 0 means the
* captcha is presented on the first login.
*/
$wgCaptchaBadLoginAttempts = 3;
/**
* Regex to whitelist URLs to known-good sites...
* For instance:
* $wgCaptchaWhitelist = '#^https?://([a-z0-9-]+\\.)?(wikimedia|wikipedia)\.org/#i';
* Local admins can define a whitelist under [[MediaWiki:captcha-addurl-whitelist]]
*/
$wgCaptchaWhitelist = false;
/**
* Additional regexes to check for. Use full regexes; can match things
* other than URLs such as junk edits.
*
* If the new version matches one and the old version doesn't,
* toss up the captcha screen.
*
* @fixme Add a message for local admins to add items as well.
*/
$wgCaptchaRegexes = array();
/** Register special page */
$wgSpecialPages['Captcha'] = 'CaptchaSpecialPage';
$wgConfirmEditIP = __DIR__;
$wgExtensionMessagesFiles['ConfirmEdit'] = "$wgConfirmEditIP/ConfirmEdit.i18n.php";
$wgExtensionMessagesFiles['ConfirmEditAlias'] = "$wgConfirmEditIP/ConfirmEdit.alias.php";
$wgHooks['EditFilterMerged'][] = 'ConfirmEditHooks::confirmEditMerged';
$wgHooks['UserCreateForm'][] = 'ConfirmEditHooks::injectUserCreate';
$wgHooks['AbortNewAccount'][] = 'ConfirmEditHooks::confirmUserCreate';
$wgHooks['LoginAuthenticateAudit'][] = 'ConfirmEditHooks::triggerUserLogin';
$wgHooks['UserLoginForm'][] = 'ConfirmEditHooks::injectUserLogin';
$wgHooks['AbortLogin'][] = 'ConfirmEditHooks::confirmUserLogin';
$wgHooks['EmailUserForm'][] = 'ConfirmEditHooks::injectEmailUser';
$wgHooks['EmailUser'][] = 'ConfirmEditHooks::confirmEmailUser';
# Register API hook
$wgHooks['APIEditBeforeSave'][] = 'ConfirmEditHooks::confirmEditAPI';
$wgHooks['APIGetAllowedParams'][] = 'ConfirmEditHooks::APIGetAllowedParams';
$wgHooks['APIGetParamDescription'][] = 'ConfirmEditHooks::APIGetParamDescription';
$wgAutoloadClasses['ConfirmEditHooks'] = "$wgConfirmEditIP/ConfirmEditHooks.php";
$wgAutoloadClasses['SimpleCaptcha'] = "$wgConfirmEditIP/Captcha.php";
$wgAutoloadClasses['CaptchaStore'] = "$wgConfirmEditIP/CaptchaStore.php";
$wgAutoloadClasses['CaptchaSessionStore'] = "$wgConfirmEditIP/CaptchaStore.php";
$wgAutoloadClasses['CaptchaCacheStore'] = "$wgConfirmEditIP/CaptchaStore.php";
$wgAutoloadClasses['CaptchaSpecialPage'] = "$wgConfirmEditIP/ConfirmEditHooks.php";
$wgAutoloadClasses['HTMLCaptchaField'] = "$wgConfirmEditIP/HTMLCaptchaField.php";
/**
* Set up $wgWhitelistRead
*/
function confirmEditSetup() {
global $wgGroupPermissions, $wgCaptchaTriggers;
if ( !$wgGroupPermissions['*']['read'] && $wgCaptchaTriggers['badlogin'] ) {
// We need to ensure that the captcha interface is accessible
// so that unauthenticated users can actually get in after a
// mistaken password typing.
global $wgWhitelistRead;
$image = SpecialPage::getTitleFor( 'Captcha', 'image' );
$help = SpecialPage::getTitleFor( 'Captcha', 'help' );
$wgWhitelistRead[] = $image->getPrefixedText();
$wgWhitelistRead[] = $help->getPrefixedText();
}
}

View file

@ -0,0 +1,88 @@
<?php
class ConfirmEditHooks {
/**
* Get the global Captcha instance
*
* @return Captcha|SimpleCaptcha
*/
static function getInstance() {
global $wgCaptcha, $wgCaptchaClass;
static $done = false;
if ( !$done ) {
$done = true;
$wgCaptcha = new $wgCaptchaClass;
}
return $wgCaptcha;
}
static function confirmEditMerged( $editPage, $newtext ) {
return self::getInstance()->confirmEditMerged( $editPage, $newtext );
}
static function confirmEditAPI( $editPage, $newtext, &$resultArr ) {
return self::getInstance()->confirmEditAPI( $editPage, $newtext, $resultArr );
}
static function injectUserCreate( &$template ) {
return self::getInstance()->injectUserCreate( $template );
}
static function confirmUserCreate( $u, &$message ) {
return self::getInstance()->confirmUserCreate( $u, $message );
}
static function triggerUserLogin( $user, $password, $retval ) {
return self::getInstance()->triggerUserLogin( $user, $password, $retval );
}
static function injectUserLogin( &$template ) {
return self::getInstance()->injectUserLogin( $template );
}
static function confirmUserLogin( $u, $pass, &$retval ) {
return self::getInstance()->confirmUserLogin( $u, $pass, $retval );
}
static function injectEmailUser( &$form ) {
return self::getInstance()->injectEmailUser( $form );
}
static function confirmEmailUser( $from, $to, $subject, $text, &$error ) {
return self::getInstance()->confirmEmailUser( $from, $to, $subject, $text, $error );
}
// Default $flags to 1 for backwards-compatible behavior
public static function APIGetAllowedParams( &$module, &$params, $flags = 1 ) {
return self::getInstance()->APIGetAllowedParams( $module, $params, $flags );
}
public static function APIGetParamDescription( &$module, &$desc ) {
return self::getInstance()->APIGetParamDescription( $module, $desc );
}
}
class CaptchaSpecialPage extends UnlistedSpecialPage {
public function __construct() {
parent::__construct( 'Captcha' );
}
function execute( $par ) {
$this->setHeaders();
$instance = ConfirmEditHooks::getInstance();
switch( $par ) {
case "image":
if ( method_exists( $instance, 'showImage' ) ) {
return $instance->showImage();
}
case "help":
default:
return $instance->showHelp();
}
}
}

View file

@ -0,0 +1,407 @@
<?php
class FancyCaptcha extends SimpleCaptcha {
/**
* @return FileBackend
*/
public function getBackend() {
global $wgCaptchaFileBackend, $wgCaptchaDirectory;
if ( $wgCaptchaFileBackend ) {
return FileBackendGroup::singleton()->get( $wgCaptchaFileBackend );
} else {
static $backend = null;
if ( !$backend ) {
$backend = new FSFileBackend( array(
'name' => 'captcha-backend',
'lockManager' => 'nullLockManager',
'containerPaths' => array( 'captcha-render' => $wgCaptchaDirectory ),
'fileMode' => 777
) );
}
return $backend;
}
}
/**
* @return integer Estimate of the number of captchas files
*/
public function estimateCaptchaCount() {
global $wgCaptchaDirectoryLevels;
$factor = 1;
$sampleDir = $this->getBackend()->getRootStoragePath() . '/captcha-render';
if ( $wgCaptchaDirectoryLevels >= 1 ) { // 1/16 sample if 16 shards
$sampleDir .= '/' . dechex( mt_rand( 0, 15 ) );
$factor = 16;
}
if ( $wgCaptchaDirectoryLevels >= 3 ) { // 1/256 sample if 4096 shards
$sampleDir .= '/' . dechex( mt_rand( 0, 15 ) );
$factor = 256;
}
$count = 0;
foreach ( $this->getBackend()->getFileList( array( 'dir' => $sampleDir ) ) as $file ) {
++$count;
}
return ( $count * $factor );
}
/**
* Check if the submitted form matches the captcha session data provided
* by the plugin when the form was generated.
*
* @param string $answer
* @param array $info
* @return bool
*/
function keyMatch( $answer, $info ) {
global $wgCaptchaSecret;
$digest = $wgCaptchaSecret . $info['salt'] . $answer . $wgCaptchaSecret . $info['salt'];
$answerHash = substr( md5( $digest ), 0, 16 );
if ( $answerHash == $info['hash'] ) {
wfDebug( "FancyCaptcha: answer hash matches expected {$info['hash']}\n" );
return true;
} else {
wfDebug( "FancyCaptcha: answer hashes to $answerHash, expected {$info['hash']}\n" );
return false;
}
}
function addCaptchaAPI( &$resultArr ) {
$info = $this->pickImage();
if ( !$info ) {
$resultArr['captcha']['error'] = 'Out of images';
return;
}
$index = $this->storeCaptcha( $info );
$title = SpecialPage::getTitleFor( 'Captcha', 'image' );
$resultArr['captcha']['type'] = 'image';
$resultArr['captcha']['mime'] = 'image/png';
$resultArr['captcha']['id'] = $index;
$resultArr['captcha']['url'] = $title->getLocalUrl( 'wpCaptchaId=' . urlencode( $index ) );
}
/**
* Insert the captcha prompt into the edit form.
*/
function getForm() {
global $wgOut, $wgExtensionAssetsPath, $wgEnableAPI;
// Uses addModuleStyles so it is loaded when JS is disabled.
$wgOut->addModuleStyles( 'ext.confirmEdit.fancyCaptcha.styles' );
$title = SpecialPage::getTitleFor( 'Captcha', 'image' );
$index = $this->getCaptchaIndex();
if ( $wgEnableAPI ) {
// Loaded only if JS is enabled
$wgOut->addModules( 'ext.confirmEdit.fancyCaptcha' );
$captchaReload = Html::element(
'small',
array(
'class' => 'confirmedit-captcha-reload fancycaptcha-reload'
),
wfMessage( 'fancycaptcha-reload-text' )->text()
);
} else {
$captchaReload = '';
}
return "<div class='fancycaptcha-wrapper'><div class='fancycaptcha-image-container'>" .
Html::element( 'img', array(
'class' => 'fancycaptcha-image',
'src' => $title->getLocalUrl( 'wpCaptchaId=' . urlencode( $index ) ),
'alt' => ''
)
) .
$captchaReload .
"</div>\n" .
'<p>' .
Html::element( 'label', array(
'for' => 'wpCaptchaWord',
),
parent::getMessage( 'label' ) . wfMessage( 'colon-separator' )->text()
) .
Html::element( 'input', array(
'name' => 'wpCaptchaWord',
'id' => 'wpCaptchaWord',
'type' => 'text',
'size' => '12', // max_length in captcha.py plus fudge factor
'autocomplete' => 'off',
'autocorrect' => 'off',
'autocapitalize' => 'off',
'required' => 'required',
'tabindex' => 1
)
) . // tab in before the edit textarea
Html::element( 'input', array(
'type' => 'hidden',
'name' => 'wpCaptchaId',
'id' => 'wpCaptchaId',
'value' => $index
)
) .
"</p>\n" .
"</div>\n";;
}
/**
* Get captcha index key
* @return string captcha ID key
*/
function getCaptchaIndex() {
$info = $this->pickImage();
if ( !$info ) {
throw new MWException( "Ran out of captcha images" );
}
// Generate a random key for use of this captcha image in this session.
// This is needed so multiple edits in separate tabs or windows can
// go through without extra pain.
$index = $this->storeCaptcha( $info );
return $index;
}
/**
* Select a previously generated captcha image from the queue.
* @return mixed tuple of (salt key, text hash) or false if no image to find
*/
protected function pickImage() {
global $wgCaptchaDirectoryLevels;
$lockouts = 0; // number of times another process claimed a file before this one
$baseDir = $this->getBackend()->getRootStoragePath() . '/captcha-render';
return $this->pickImageDir( $baseDir, $wgCaptchaDirectoryLevels, $lockouts );
}
/**
* @param $directory string
* @param $levels integer
* @param $lockouts integer
* @return Array|bool
*/
protected function pickImageDir( $directory, $levels, &$lockouts ) {
global $wgMemc;
if ( $levels <= 0 ) { // $directory has regular files
return $this->pickImageFromDir( $directory, $lockouts );
}
$backend = $this->getBackend();
$key = "fancycaptcha:dirlist:{$backend->getWikiId()}:" . sha1( $directory );
$dirs = $wgMemc->get( $key ); // check cache
if ( !is_array( $dirs ) || !count( $dirs ) ) { // cache miss
$dirs = array(); // subdirs actually present...
foreach ( $backend->getTopDirectoryList( array( 'dir' => $directory ) ) as $entry ) {
if ( ctype_xdigit( $entry ) && strlen( $entry ) == 1 ) {
$dirs[] = $entry;
}
}
wfDebug( "Cache miss for $directory subdirectory listing.\n" );
if ( count( $dirs ) ) {
$wgMemc->set( $key, $dirs, 86400 );
}
}
if ( !count( $dirs ) ) {
// Remove this directory if empty so callers don't keep looking here
$backend->clean( array( 'dir' => $directory ) );
return false; // none found
}
$place = mt_rand( 0, count( $dirs ) - 1 ); // pick a random subdir
// In case all dirs are not filled, cycle through next digits...
for ( $j = 0; $j < count( $dirs ); $j++ ) {
$char = $dirs[( $place + $j ) % count( $dirs )];
$info = $this->pickImageDir( "$directory/$char", $levels - 1, $lockouts );
if ( $info ) {
return $info; // found a captcha
} else {
wfDebug( "Could not find captcha in $directory.\n" );
$wgMemc->delete( $key ); // files changed on disk?
}
}
return false; // didn't find any images in this directory... empty?
}
/**
* @param $directory string
* @param $lockouts integer
* @return Array|bool
*/
protected function pickImageFromDir( $directory, &$lockouts ) {
global $wgMemc;
$backend = $this->getBackend();
$key = "fancycaptcha:filelist:{$backend->getWikiId()}:" . sha1( $directory );
$files = $wgMemc->get( $key ); // check cache
if ( !is_array( $files ) || !count( $files ) ) { // cache miss
$files = array(); // captcha files
foreach ( $backend->getTopFileList( array( 'dir' => $directory ) ) as $entry ) {
$files[] = $entry;
if ( count( $files ) >= 500 ) { // sanity
wfDebug( 'Skipping some captchas; $wgCaptchaDirectoryLevels set too low?.' );
break;
}
}
if ( count( $files ) ) {
$wgMemc->set( $key, $files, 86400 );
}
wfDebug( "Cache miss for $directory captcha listing.\n" );
}
if ( !count( $files ) ) {
// Remove this directory if empty so callers don't keep looking here
$backend->clean( array( 'dir' => $directory ) );
return false;
}
$info = $this->pickImageFromList( $directory, $files, $lockouts );
if ( !$info ) {
wfDebug( "Could not find captcha in $directory.\n" );
$wgMemc->delete( $key ); // files changed on disk?
}
return $info;
}
/**
* @param $directory string
* @param $files array
* @param $lockouts integer
* @return boolean
*/
protected function pickImageFromList( $directory, array $files, &$lockouts ) {
global $wgMemc, $wgCaptchaDeleteOnSolve;
if ( !count( $files ) ) {
return false; // none found
}
$backend = $this->getBackend();
$place = mt_rand( 0, count( $files ) - 1 ); // pick a random file
$misses = 0; // number of files in listing that don't actually exist
for ( $j = 0; $j < count( $files ); $j++ ) {
$entry = $files[( $place + $j ) % count( $files )];
if ( preg_match( '/^image_([0-9a-f]+)_([0-9a-f]+)\\.png$/', $entry, $matches ) ) {
if ( $wgCaptchaDeleteOnSolve ) { // captcha will be deleted when solved
$key = "fancycaptcha:filelock:{$backend->getWikiId()}:" . sha1( $entry );
// Try to claim this captcha for 10 minutes (for the user to solve)...
if ( ++$lockouts <= 10 && !$wgMemc->add( $key, '1', 600 ) ) {
continue; // could not acquire (skip it to avoid race conditions)
}
}
if ( !$backend->fileExists( array( 'src' => "$directory/$entry" ) ) ) {
if ( ++$misses >= 5 ) { // too many files in the listing don't exist
break; // listing cache too stale? break out so it will be cleared
}
continue; // try next file
}
return array(
'salt' => $matches[1],
'hash' => $matches[2],
'viewed' => false,
);
}
}
return false; // none found
}
function showImage() {
global $wgOut;
$wgOut->disable();
$info = $this->retrieveCaptcha();
if ( $info ) {
$timestamp = new MWTimestamp();
$info['viewed'] = $timestamp->getTimestamp();
$this->storeCaptcha( $info );
$salt = $info['salt'];
$hash = $info['hash'];
return $this->getBackend()->streamFile( array(
'src' => $this->imagePath( $salt, $hash ),
'headers' => array( "Cache-Control: private, s-maxage=0, max-age=3600" )
) )->isOK();
}
wfHttpError( 500, 'Internal Error', 'Requested bogus captcha image' );
return false;
}
/**
* @param $salt string
* @param $hash string
* @return string
*/
public function imagePath( $salt, $hash ) {
global $wgCaptchaDirectoryLevels;
$file = $this->getBackend()->getRootStoragePath() . '/captcha-render/';
for ( $i = 0; $i < $wgCaptchaDirectoryLevels; $i++ ) {
$file .= $hash{ $i } . '/';
}
$file .= "image_{$salt}_{$hash}.png";
return $file;
}
/**
* @param $basename string
* @return Array (salt, hash)
* @throws MWException
*/
public function hashFromImageName( $basename ) {
if ( preg_match( '/^image_([0-9a-f]+)_([0-9a-f]+)\\.png$/', $basename, $matches ) ) {
return array( $matches[1], $matches[2] );
} else {
throw new MWException( "Invalid filename '$basename'.\n" );
}
}
/**
* Show a message asking the user to enter a captcha on edit
* The result will be treated as wiki text
*
* @param $action string Action being performed
* @return string
*/
function getMessage( $action ) {
$name = 'fancycaptcha-' . $action;
$text = wfMessage( $name )->text();
# Obtain a more tailored message, if possible, otherwise, fall back to
# the default for edits
return wfMessage( $name, $text )->isDisabled() ?
wfMessage( 'fancycaptcha-edit' )->text() : $text;
}
/**
* Delete a solved captcha image, if $wgCaptchaDeleteOnSolve is true.
*/
function passCaptcha() {
global $wgCaptchaDeleteOnSolve;
$info = $this->retrieveCaptcha(); // get the captcha info before it gets deleted
$pass = parent::passCaptcha();
if ( $pass && $wgCaptchaDeleteOnSolve ) {
$this->getBackend()->quickDelete( array(
'src' => $this->imagePath( $info['salt'], $info['hash'] )
) );
}
return $pass;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,76 @@
<?php
/**
* Experimental image-based captcha plugin, using images generated by an
* external tool.
*
* Copyright (C) 2005, 2006 Brion Vibber <brion@pobox.com>
* http://www.mediawiki.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Extensions
*/
if ( !defined( 'MEDIAWIKI' ) ) {
exit;
}
$dir = __DIR__;
require_once $dir . '/ConfirmEdit.php';
$wgCaptchaClass = 'FancyCaptcha';
/**
* The name of a file backend ($wgFileBackends) to be used for storing files.
* Defaults to FSFileBackend using $wgCaptchaDirectory as a base path.
*/
$wgCaptchaFileBackend = '';
global $wgCaptchaDirectory;
$wgCaptchaDirectory = "$wgUploadDirectory/captcha"; // bad default :D
global $wgCaptchaDirectoryLevels;
$wgCaptchaDirectoryLevels = 0; // To break into subdirectories
global $wgCaptchaSecret;
$wgCaptchaSecret = "CHANGE_THIS_SECRET!";
/**
* By default the FancyCaptcha rotates among all available captchas.
* Setting $wgCaptchaDeleteOnSolve to true will delete the captcha
* files when they are correctly solved. Thus the user will need
* something like a cron creating new thumbnails to avoid drying up.
*/
$wgCaptchaDeleteOnSolve = false;
$wgExtensionMessagesFiles['FancyCaptcha'] = $dir . '/FancyCaptcha.i18n.php';
$wgAutoloadClasses['FancyCaptcha'] = $dir . '/FancyCaptcha.class.php';
$wgResourceModules['ext.confirmEdit.fancyCaptcha.styles'] = array(
'localBasePath' => $dir . '/resources',
'remoteExtPath' => 'ConfirmEdit/resources',
'styles' => 'ext.confirmEdit.fancyCaptcha.css',
);
$wgResourceModules['ext.confirmEdit.fancyCaptcha'] = array(
'localBasePath' => $dir . '/resources',
'remoteExtPath' => 'ConfirmEdit/resources',
'scripts' => 'ext.confirmEdit.fancyCaptcha.js',
'dependencies' => 'mediawiki.api',
);
$wgAutoloadClasses['ApiFancyCaptchaReload'] = $dir . '/ApiFancyCaptchaReload.php';
$wgAPIModules['fancycaptchareload'] = 'ApiFancyCaptchaReload';

View file

@ -0,0 +1,81 @@
<?php
/**
* HTMLFormField for inserting Captchas into a form.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @class
*/
class HTMLCaptchaField extends HTMLFormField {
/**
* @var Captcha
*/
private $captcha;
public $prefix = '';
/**
* @var Bool|Array
*/
private $validationResult;
public function __construct( $params ) {
parent::__construct( $params );
// For differentiating the type of form, mainly
if ( isset( $params['prefix'] ) ) {
$this->prefix = $params['prefix'];
}
}
/**
* Get the captcha body. Don't include any of the surrounding table cells/rows
*
* @param $value String
* @return String
*/
public function getInputHTML( $value ) {
# TODO
}
public function validate( $data, $alldata ) {
// We sent back the exists status of the captcha before. If it *doesn't* exist
// we actually want to validate this as true, because we don't want an angry red
// error message, just for the user to put the captcha in again
if ( $data === false ) {
return true;
}
}
/**
* @param $request WebRequest
* @return void
*/
public function loadDataFromRequest( $request ) {
$this->captcha = Captcha::factory();
$this->captcha->loadFromRequest( $request, $this );
if ( !$this->captcha->exists() ) {
// The captcha doesn't exist; probably because it's already been used and
// then deleted for security. Load the field up with a new captcha which
// will be shown to the user when the validation of said new object fails
$this->captcha = Captcha::newRandom();
}
// This will be useful as the difference between "the captcha doesn't exist" and
// "you answered the captcha wrongly"
return $this->captcha->exists();
}
}

View file

@ -0,0 +1,50 @@
<?php
class MathCaptcha extends SimpleCaptcha {
/** Validate a captcha response */
function keyMatch( $answer, $info ) {
return (int)$answer == (int)$info['answer'];
}
function addCaptchaAPI( &$resultArr ) {
list( $sum, $answer ) = $this->pickSum();
$index = $this->storeCaptcha( array( 'answer' => $answer ) );
$resultArr['captcha']['type'] = 'math';
$resultArr['captcha']['mime'] = 'text/tex';
$resultArr['captcha']['id'] = $index;
$resultArr['captcha']['question'] = $sum;
}
/** Produce a nice little form */
function getForm() {
list( $sum, $answer ) = $this->pickSum();
$index = $this->storeCaptcha( array( 'answer' => $answer ) );
$form = '<table><tr><td>' . $this->fetchMath( $sum ) . '</td>';
$form .= '<td>' . Html::input( 'wpCaptchaWord', false, false, array( 'tabindex' => '1', 'autocomplete' => 'off', 'required' ) ) . '</td></tr></table>';
$form .= Html::hidden( 'wpCaptchaId', $index );
return $form;
}
/** Pick a random sum */
function pickSum() {
$a = mt_rand( 0, 100 );
$b = mt_rand( 0, 10 );
$op = mt_rand( 0, 1 ) ? '+' : '-';
$sum = "{$a} {$op} {$b} = ";
$ans = $op == '+' ? ( $a + $b ) : ( $a - $b );
return array( $sum, $ans );
}
/** Fetch the math */
function fetchMath( $sum ) {
if ( class_exists( 'MathRenderer' ) ) {
$math = MathRenderer::getRenderer( $sum, array(), MW_MATH_PNG );
} else {
throw new MWException( 'MathCaptcha requires the Math extension for MediaWiki versions 1.18 and above.' );
}
$html = $math->render();
return preg_replace( '/alt=".*?"/', '', $html );
}
}

View file

@ -0,0 +1,22 @@
<?php
/**
* Captcha class using simple sums and the math renderer
* Not brilliant, but enough to dissuade casual spam bots
*
* @file
* @ingroup Extensions
* @author Rob Church <robchur@gmail.com>
* @copyright © 2006 Rob Church
* @licence GNU General Public Licence 2.0
*/
if ( !defined( 'MEDIAWIKI' ) ) {
exit;
}
$dir = __DIR__;
require_once $dir . '/ConfirmEdit.php';
$wgCaptchaClass = 'MathCaptcha';
$wgAutoloadClasses['MathCaptcha'] = $dir . '/MathCaptcha.class.php';

View file

@ -0,0 +1,73 @@
<?php
/**
* QuestyCaptcha class
*
* @file
* @author Benjamin Lees <emufarmers@gmail.com>
* @ingroup Extensions
*/
class QuestyCaptcha extends SimpleCaptcha {
/** Validate a captcha response */
function keyMatch( $answer, $info ) {
if ( is_array( $info['answer'] ) ) {
return in_array( strtolower( $answer ), $info['answer'] );
} else {
return strtolower( $answer ) == strtolower( $info['answer'] );
}
}
function addCaptchaAPI( &$resultArr ) {
$captcha = $this->getCaptcha();
$index = $this->storeCaptcha( $captcha );
$resultArr['captcha']['type'] = 'question';
$resultArr['captcha']['mime'] = 'text/plain';
$resultArr['captcha']['id'] = $index;
$resultArr['captcha']['question'] = $captcha['question'];
}
function getCaptcha() {
global $wgCaptchaQuestions;
return $wgCaptchaQuestions[mt_rand( 0, count( $wgCaptchaQuestions ) - 1 )]; // pick a question, any question
}
function getForm() {
$captcha = $this->getCaptcha();
if ( !$captcha ) {
die( "No questions found; set some in LocalSettings.php using the format from QuestyCaptcha.php." );
}
$index = $this->storeCaptcha( $captcha );
return "<p><label for=\"wpCaptchaWord\">{$captcha['question']}</label> " .
Html::element( 'input', array(
'name' => 'wpCaptchaWord',
'id' => 'wpCaptchaWord',
'required',
'autocomplete' => 'off',
'tabindex' => 1 ) ) . // tab in before the edit textarea
"</p>\n" .
Xml::element( 'input', array(
'type' => 'hidden',
'name' => 'wpCaptchaId',
'id' => 'wpCaptchaId',
'value' => $index ) );
}
function getMessage( $action ) {
$name = 'questycaptcha-' . $action;
$text = wfMessage( $name )->text();
# Obtain a more tailored message, if possible, otherwise, fall back to
# the default for edits
return wfMessage( $name, $text )->isDisabled() ? wfMessage( 'questycaptcha-edit' )->text() : $text;
}
function showHelp() {
global $wgOut;
$wgOut->setPageTitle( wfMessage( 'captchahelp-title' )->text() );
$wgOut->addWikiMsg( 'questycaptchahelp-text' );
if ( CaptchaStore::get()->cookiesNeeded() ) {
$wgOut->addWikiMsg( 'captchahelp-cookies-needed' );
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,46 @@
<?php
/**
* A question-based captcha plugin.
*
* Copyright (C) 2009 Benjamin Lees <emufarmers@gmail.com>
* http://www.mediawiki.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Extensions
*/
if ( !defined( 'MEDIAWIKI' ) ) {
exit;
}
$dir = __DIR__;
require_once $dir . '/ConfirmEdit.php';
$wgCaptchaClass = 'QuestyCaptcha';
global $wgCaptchaQuestions;
$wgCaptchaQuestions = array();
// Add your questions in LocalSettings.php using this format
// $wgCaptchaQuestions[] = array( 'question' => "A question?", 'answer' => "An answer!" );
// $wgCaptchaQuestions[] = array( 'question' => 'How much wood would a woodchuck chuck if a woodchuck could chuck wood?', 'answer' => 'as much wood as...' );
// $wgCaptchaQuestions[] = array( 'question' => "What is this wiki's name?", 'answer' => "$wgSitename" );
// You can also provide several acceptable answers to a given question (the answers shall be in lowercase):
// $wgCaptchaQuestions[] = array( 'question' => "2 + 2 ?", 'answer' => array( '4', 'four' ) );
$wgExtensionMessagesFiles['QuestyCaptcha'] = $dir . '/QuestyCaptcha.i18n.php';
$wgAutoloadClasses['QuestyCaptcha'] = $dir . '/QuestyCaptcha.class.php';

View file

@ -0,0 +1,48 @@
ConfirmEdit extension for MediaWiki
This extension provides various CAPTCHA tools for MediaWiki, to allow
for protection against spambots and other automated tools.
For more information, see the extension homepage at:
http://www.mediawiki.org/wiki/Extension:ConfirmEdit
== Overview ==
The following modules are included in ConfirmEdit:
* SimpleCaptcha - users have to solve an arithmetic math problem
* MathCaptcha - users have to solve a math problem that's displayed as
an image
* FancyCaptcha - users have to identify a series of characters, displayed
in a stylized way
* QuestyCaptcha - users have to answer a question, out of a series of
questions defined by the administrator(s)
* ReCaptcha - users have to identify a series of characters, either
visually or audially, from a widget provided by the reCAPTCHA service
* Asirra - users have to identify the cats in a set of photos of cats
and dogs, from a widget provided by the Microsoft Asirra service
== License ==
ConfirmEdit is published under the GPL license.
== Authors ==
The main framework, and the SimpleCaptcha and FancyCaptcha modules, were
written by Brion Vibber.
The MathCaptcha module was written by Rob Church.
The QuestyCaptcha module was written by Benjamin Lees.
The reCAPTCHA module was written by Mike Crawford and Ben Maurer.
The Asirra module was written by Bachsau.
Additional maintenance work was done by Yaron Koren.
== Changelog ==
= Version 1.2
Fixes bug 46132 - ConfirmEdit fatal error when using MathCaptcha and current Math extension.
See <https://bugzilla.wikimedia.org/show_bug.cgi?id=46132>.

View file

@ -0,0 +1,101 @@
<?php
class ReCaptcha extends SimpleCaptcha {
// reCAPTHCA error code returned from recaptcha_check_answer
private $recaptcha_error = null;
/**
* Displays the reCAPTCHA widget.
* If $this->recaptcha_error is set, it will display an error in the widget.
*
*/
function getForm() {
global $wgReCaptchaPublicKey, $wgReCaptchaTheme;
$useHttps = ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' );
$js = 'var RecaptchaOptions = ' . Xml::encodeJsVar( array( 'theme' => $wgReCaptchaTheme, 'tabindex' => 1 ) );
return Html::inlineScript( $js ) . recaptcha_get_html( $wgReCaptchaPublicKey, $this->recaptcha_error, $useHttps );
}
/**
* Calls the library function recaptcha_check_answer to verify the users input.
* Sets $this->recaptcha_error if the user is incorrect.
* @return boolean
*
*/
function passCaptcha() {
global $wgReCaptchaPrivateKey, $wgRequest;
// API is hardwired to return wpCaptchaId and wpCaptchaWord, so use that if the standard two are empty
$challenge = $wgRequest->getVal( 'recaptcha_challenge_field', $wgRequest->getVal( 'wpCaptchaId' ) );
$response = $wgRequest->getVal( 'recaptcha_response_field', $wgRequest->getVal( 'wpCaptchaWord' ) );
if ( $response === null ) {
// new captcha session
return false;
}
$ip = $wgRequest->getIP();
$recaptcha_response = recaptcha_check_answer(
$wgReCaptchaPrivateKey,
$ip,
$challenge,
$response
);
if ( !$recaptcha_response->is_valid ) {
$this->recaptcha_error = $recaptcha_response->error;
return false;
}
$recaptcha_error = null;
return true;
}
function addCaptchaAPI( &$resultArr ) {
global $wgReCaptchaPublicKey;
$resultArr['captcha']['type'] = 'recaptcha';
$resultArr['captcha']['mime'] = 'image/png';
$resultArr['captcha']['key'] = $wgReCaptchaPublicKey;
$resultArr['captcha']['error'] = $this->recaptcha_error;
}
/**
* Show a message asking the user to enter a captcha on edit
* The result will be treated as wiki text
*
* @param $action string Action being performed
* @return string
*/
function getMessage( $action ) {
$name = 'recaptcha-' . $action;
$text = wfMessage( $name )->text();
# Obtain a more tailored message, if possible, otherwise, fall back to
# the default for edits
return wfMessage( $name, $text )->isDisabled() ? wfMessage( 'recaptcha-edit' )->text() : $text;
}
public function APIGetAllowedParams( &$module, &$params, $flags ) {
if ( $flags && $this->isAPICaptchaModule( $module ) ) {
$params['recaptcha_challenge_field'] = null;
$params['recaptcha_response_field'] = null;
}
return true;
}
public function APIGetParamDescription( &$module, &$desc ) {
if ( $this->isAPICaptchaModule( $module ) ) {
$desc['recaptcha_challenge_field'] = 'Field from the ReCaptcha widget';
$desc['recaptcha_response_field'] = 'Field from the ReCaptcha widget';
}
return true;
}
}

View file

@ -0,0 +1,596 @@
<?php
/**
* Internationalisation file for the reCAPTCHA module of the ConfirmEdit
* extension.
*
* @addtogroup Extensions
*/
$messages = array();
$messages['en'] = array(
'recaptcha-desc' => 'reCAPTCHA module for Confirm Edit',
'recaptcha-edit' => 'To protect the wiki against automated edit spam, we kindly ask you to type the two words you see in the box below:',
'recaptcha-addurl' => 'Your edit includes new external links. To protect the wiki against automated spam, we kindly ask you to type the two words you see in the box below:',
'recaptcha-badlogin' => 'To protect the wiki against automated password cracking, we kindly ask you to type the two words you see in the box below:',
'recaptcha-createaccount' => 'To protect the wiki against automated account creation, we kindly ask you to type the two words you see in the box below:',
'recaptcha-createaccount-fail' => "Incorrect or missing reCAPTCHA answer.",
'recaptcha-create' => 'To protect the wiki against automated page creation, we kindly ask you to type the two words you see in the box below:',
);
/** Message documentation (Message documentation)
* @author Raymond
* @author Shirayuki
* @author Umherirrender
*/
$messages['qqq'] = array(
'recaptcha-desc' => '{{Optional}}
{{desc}}',
'recaptcha-edit' => '{{Related|ConfirmEdit-edit}}',
'recaptcha-addurl' => '{{Related|ConfirmEdit-addurl}}',
'recaptcha-badlogin' => '{{Related|ConfirmEdit-badlogin}}',
'recaptcha-createaccount' => '{{Related|ConfirmEdit-createaccount}}',
'recaptcha-createaccount-fail' => '{{Related|ConfirmEdit-createaccount-fail}}',
'recaptcha-create' => '{{Related|ConfirmEdit-create}}',
);
/** Arabic (العربية)
* @author Alexknight12
* @author Ciphers
* @author Meno25
*/
$messages['ar'] = array(
'recaptcha-desc' => 'موديل reCAPTCHA لConfirm Edit',
'recaptcha-edit' => 'للمساعدة في الحماية ضد التحرير السبام، يرجى كتابة الكلمتين التي تراها في المربع أدناه :', # Fuzzy
'recaptcha-addurl' => 'إن تعديلك يتضمن وصلات خارجية جديدة. للمساعدة في الحماية ضد السبام، يرجى كتابة الكلمتين التي تراها في المربع أدناه :', # Fuzzy
'recaptcha-badlogin' => 'للمساعدة في الحماية ضد الكسر الآلي لكلمة السر، يرجى كتابة الكلمتين اللتين تراهما في المربع أدناه :', # Fuzzy
'recaptcha-createaccount' => 'للمساعدة في الحماية ضد الإنشاء الآلي للحسابات، رجاء اكتب الكلمتين اللتين تراهما في المربع أدناه:', # Fuzzy
'recaptcha-createaccount-fail' => 'جواب reCAPTCHA غير صحيح أو مفقود.',
'recaptcha-create' => 'للمساعدة في الحماية ضد الإنشاء الآلي للصفحات، يرجى كتابة الكلمتين اللتين تراهما في المربع أدناه :', # Fuzzy
);
/** Asturian (asturianu)
* @author Xuacu
*/
$messages['ast'] = array(
'recaptcha-edit' => "Pa protexer la wiki escontra'l spam d'edición automáticu, pidimos-y qu'escriba les dos pallabres que se ven na caxella d'abaxo:",
'recaptcha-addurl' => "La so edición incluye enllaces esternos nuevos. Pa protexer la wiki escontra'l spam automáticu, pidimos-y qu'escriba les dos pallabres que se ven na caxella d'abaxo:",
'recaptcha-badlogin' => "Pa protexer la wiki escontra'l descifráu de claves automáticu, pidimos-y qu'escriba les dos pallabres que se ven na caxella d'abaxo:",
'recaptcha-createaccount' => "Pa protexer la wiki escontra la creación de cuentes automática, pidimos-y qu'escriba les dos pallabres que se ven na caxella d'abaxo:",
'recaptcha-createaccount-fail' => 'Falta la rempuesta a la entruga de reCAPTCHA o ye incorreuta',
'recaptcha-create' => "Pa protexer la wiki escontra la creación de páxines automática, pidimos-y qu'escriba les dos pallabres que se ven na caxella d'abaxo:",
);
/** Belarusian (Taraškievica orthography) (беларуская (тарашкевіца))
* @author EugeneZelenko
* @author Jim-by
*/
$messages['be-tarask'] = array(
'recaptcha-edit' => 'Для абароны супраць спаму, калі ласка, увядзіце два словы, якія Вы бачыце ў полі ніжэй:', # Fuzzy
'recaptcha-addurl' => 'Вашае рэдагаваньне ўтрымлівае новыя вонкавыя спасылкі. Для абароны супраць спаму, калі ласка, увядзіце два словы, якія Вы бачыце ў полі ніжэй:', # Fuzzy
'recaptcha-badlogin' => 'Для абароны супраць узлому паролю, калі ласка, увядзіце два словы, якія Вы бачыце ў полі ніжэй:', # Fuzzy
'recaptcha-createaccount' => 'Для абароны супраць аўтаматычнага стварэньня рахункаў, калі ласка, увядзіце два словы, якія Вы бачыце ў полі ніжэй:', # Fuzzy
'recaptcha-createaccount-fail' => 'Адказ reCAPTCHA няслушны ці адсутнічае.',
'recaptcha-create' => 'Для абароны супраць аўтаматычнага стварэньня старонак, калі ласка, увядзіце два словы, якія Вы бачыце ў полі ніжэй:', # Fuzzy
);
/** Breton (brezhoneg)
* @author Fohanno
* @author Fulup
*/
$messages['br'] = array(
'recaptcha-edit' => "Evit sikour en em wareziñ diouzh ar stroboù emgefre, merkit an daou c'her a welit er voest dindan :",
'recaptcha-addurl' => "Liamm diavaez nevez zo bet degaset ganeoc'h. Evit sikour en em wareziñ diouzh ar stroboù, merkit an daou c'her a welit er voest dindan :",
'recaptcha-badlogin' => "Da sikour en em wareziñ diouzh an diskuliañ gerioù-tremen emgefre dre daolioù-esae, merkit an daou c'her a welit er voest dindan :", # Fuzzy
'recaptcha-createaccount' => "Da sikour en em wareziñ diouzh ar c'hrouiñ kontoù emgefre, merkit an daou c'her a welit er voest dindan :", # Fuzzy
'recaptcha-createaccount-fail' => 'Respont reCAPTHCA faziek pe ezvezant.',
'recaptcha-create' => "Da sikour en em wareziñ diouzh ar c'hrouiñ pajennoù emgefre, merkit an daou c'her a welit er voest dindan :", # Fuzzy
);
/** Bosnian (bosanski)
* @author CERminator
*/
$messages['bs'] = array(
'recaptcha-edit' => 'Da bismo pomogli protiv automatiziranog spam uređivanja, molimo upišite dvije riječi koje vidite u kutiju ispod:', # Fuzzy
'recaptcha-addurl' => 'Vaše izmjene uključuju nove vanjske linkove. Da bismo pomogli protiv automatiziranog spama, molimo upišite dvije riječi koje vidite u kutiju ispod.', # Fuzzy
'recaptcha-badlogin' => 'Da bismo pomogli protiv automatiziranog probijanja šifre, molimo upišite dvije riječi koje vidite u kutiju ispod:', # Fuzzy
'recaptcha-createaccount' => 'Da bismo pomogli protiv automatiziranog pravljenja računa, molimo upišite dvije riječi koje vidite u kutiju ispod:', # Fuzzy
'recaptcha-createaccount-fail' => 'Neispravan ili nedostajući reCAPTCHA odgovor.',
'recaptcha-create' => 'Da bismo pomogli protiv automatiziranog pravljenja stranica, molimo upišite dvije riječi koje vidite u kutiju ispod:', # Fuzzy
);
/** Czech (česky)
* @author Jkjk
* @author Mormegil
*/
$messages['cs'] = array(
'recaptcha-desc' => 'Podpora reCAPTCHA pro rozšíření Confirm Edit',
'recaptcha-edit' => 'V zájmu ochrany před automatickým spamováním opište dvě slova, která vidíte na obrázku:', # Fuzzy
'recaptcha-addurl' => 'Vaše editace obsahuje nové externí odkazy. V zájmu ochrany před automatickým spamováním opište dvě slova, která vidíte na obrázku:', # Fuzzy
'recaptcha-badlogin' => 'V zájmu ochrany proti automatickým pokusům uhodnout heslo opište dvě slova, která vidíte na obrázku:', # Fuzzy
'recaptcha-createaccount' => 'V rámci ochrany před automatickým vytvářením účtů opište dvě slova, která vidíte na obrázku:', # Fuzzy
'recaptcha-createaccount-fail' => 'Nesprávná nebo chybějící odpověď na reCAPTCHA.',
'recaptcha-create' => 'V zájmu ochrany před automatickým zakládáním stránek opište dvě slova, která vidíte na obrázku:', # Fuzzy
);
/** Welsh (Cymraeg)
* @author Lloffiwr
*/
$messages['cy'] = array(
'recaptcha-edit' => "Er mwyn arbed y wici rhag i olygyddion awtomatig osod sbam, byddwch gystal â theipio'r ddau air sydd i'w gweld yn y blwch isod:",
'recaptcha-addurl' => "Mae'ch golygiad yn cynnwys cysylltiadau allanol newydd. Er mwyn arbed y wici rhag sbam awtomatig, byddwch gystal â theipio'r ddau air sydd i'w gweld yn y blwch isod:",
'recaptcha-badlogin' => "Er mwyn arbed y wici rhag peiriannau sy'n datrys cyfrineiriau'n awtomatig, byddwch gystal â theipio'r ddau air sydd i'w gweld yn y blwch isod:",
'recaptcha-createaccount' => "Er mwyn arbed y wici rhag peiriannau sy'n dechrau cyfrifon yn awtomatig, byddwch gystal â theipio'r ddau air sydd i'w gweld yn y blwch isod:",
'recaptcha-createaccount-fail' => "Mae'r ateb i'r pos CAPTCHA yn anghywir neu yn eisiau.",
'recaptcha-create' => "Er mwyn arbed y wici rhag peiriannau sy'n dechrau tudalennau yn awtomatig, byddwch gystal â theipio'r ddau air sydd i'w gweld yn y blwch isod:",
);
/** German (Deutsch)
* @author Kghbln
* @author Metalhead64
* @author Umherirrender
*/
$messages['de'] = array(
'recaptcha-desc' => 'reCAPTCHA-Module für die Erweiterung „Confirm Edit“',
'recaptcha-edit' => 'Zum Schutz des Wikis vor automatisiertem Spam bitten wir dich, die beiden folgenden Wörter in das untenstehende Feld einzugeben:',
'recaptcha-addurl' => 'Deine Bearbeitung enthält neue externe Links. Zum Schutz des Wikis vor automatisiertem Spam bitten wir dich, die beiden folgenden Wörter in das folgende Feld einzugeben:',
'recaptcha-badlogin' => 'Zum Schutz des Wikis gegen automatisiertes Knacken von Passwörtern bitten wir dich, die beiden folgenden Wörter in das folgende Feld einzugeben:',
'recaptcha-createaccount' => 'Zum Schutz des Wikis gegen die automatisierte Erstellung von Benutzerkonten bitten wir dich, die folgenden beiden Wörter in das untenstehende Feld einzugeben:',
'recaptcha-createaccount-fail' => 'Fehlerhafte oder fehlende reCAPTCHA Antwort.',
'recaptcha-create' => 'Zum Schutz des Wikis gegen automatisierte Erstellung von Seiten bitten wir dich, die beiden folgenden Wörter in das folgende Feld einzugeben:',
);
/** German (formal address) (Deutsch (Sie-Form))
* @author Kghbln
*/
$messages['de-formal'] = array(
'recaptcha-createaccount' => 'Zum Schutz gegen die automatisierte Erstellung von Benutzerkonten bitten wir Sie, die folgenden beiden Wörter in das untenstehende Feld einzugeben:', # Fuzzy
);
/** Lower Sorbian (dolnoserbski)
* @author Michawiki
*/
$messages['dsb'] = array(
'recaptcha-createaccount-fail' => 'Wopacne abo felujuce wótegrono reCAPTCHA.',
);
/** Spanish (español)
* @author Fitoschido
*/
$messages['es'] = array(
'recaptcha-edit' => 'Para protegernos de la publicidad automatizada, escribe las dos palabras visibles en el cuadro de abajo:', # Fuzzy
'recaptcha-addurl' => 'Tu edición incluye nuevos enlaces externos. Para ayudar a protegernos contra el spam automatizado, por favor, escribe las dos palabras que ves en el cuadro a continuación:', # Fuzzy
'recaptcha-badlogin' => 'Para protegernos del robo automatizado de contraseñas, escribe las dos palabras visibles en el cuadro de abajo:', # Fuzzy
'recaptcha-createaccount' => 'Para protegernos de la creación automática de cuentas, escribe las dos palabras que ves en el cuadro de abajo:', # Fuzzy
'recaptcha-createaccount-fail' => 'ReCAPTCHA incorrecto o sin responder.',
'recaptcha-create' => 'Para protegernos de la creación automática de páginas, escribe las dos palabras que ves en el cuadro de abajo:', # Fuzzy
);
/** Finnish (suomi)
* @author Nedergard
* @author VezonThunder
*/
$messages['fi'] = array(
'recaptcha-edit' => 'Suojana automaattisia roskamuokkauksia vastaan sinun on syötettävä kaksi näkemääsi sanaa laatikkoon alla:', # Fuzzy
'recaptcha-addurl' => 'Muokkauksesi sisältää uusia ulkoisia linkkejä. Suojana automaattista roskapostia vastaan sinun on syötettävä kaksi näkemääsi sanaa laatikkoon alla:', # Fuzzy
'recaptcha-badlogin' => 'Suojana automaattisia salasanamurtoja vastaan sinun on syötettävä kaksi näkemääsi sanaa laatikkoon alla:', # Fuzzy
'recaptcha-createaccount' => 'Suojana automaattista tunnusten luontia vastaan sinun on syötettävä kaksi näkemääsi sanaa laatikkoon alla:', # Fuzzy
'recaptcha-createaccount-fail' => 'Väärä tai puuttuva reCAPTCHA-vastaus.',
'recaptcha-create' => 'Suojana automaattista sivujen luontia vastaan sinun on syötettävä kaksi näkemääsi sanaa laatikkoon alla:', # Fuzzy
);
/** French (français)
* @author Gomoko
* @author Urhixidur
*/
$messages['fr'] = array(
'recaptcha-edit' => 'Afin de protéger le wiki du spam des modifications automatisées, nous vous demandons de bien vouloir écrire les deux mots visibles dans le cadre ci-dessous :',
'recaptcha-addurl' => 'Votre contribution contient des liens vers un site externe. Pour protéger le wiki contre le spam automatisé, nous vous demandons de bien vouloir écrire les deux mots visibles dans le cadre qui suit :',
'recaptcha-badlogin' => 'Pour protéger le wiki des essais automatiques de cassage de mot de passe, nous vous demandons de bien vouloir écrire les deux mots visibles dans le cadre qui suit:',
'recaptcha-createaccount' => 'Pour protéger le wiki des créations automatiques de compte, nous vous demandons de bien vouloir écrire les deux mots visibles dans le cadre qui suit :',
'recaptcha-createaccount-fail' => 'Réponse de reCAPTCHA fausse ou manquante.',
'recaptcha-create' => 'Pour protéger le wiki des créations automatiques de pages, nous vous demandons de bien vouloir écrire les deux mots visibles dans le cadre qui suit :',
);
/** Galician (galego)
* @author Toliño
*/
$messages['gl'] = array(
'recaptcha-desc' => 'Módulo de reCAPTCHA para Confirm Edit',
'recaptcha-edit' => 'Para protexer o wiki contra o spam automático, introduza as dúas palabras que vexa na caixa:',
'recaptcha-addurl' => 'A súa edición inclúe novas ligazóns externas. Para protexer o wiki contra o spam automático, introduza as dúas palabras que vexa na caixa:',
'recaptcha-badlogin' => 'Para protexer o wiki contra o roubo de contrasinais, introduza as dúas palabras que vexa na caixa:',
'recaptcha-createaccount' => 'Para protexer o wiki contra a creación automática de contas, introduza as dúas palabras que vexa na caixa:',
'recaptcha-createaccount-fail' => 'Falta a resposta ao reCAPTCHA ou esta é incorrecta.',
'recaptcha-create' => 'Para protexer o wiki contra a creación automática de páxinas, introduza as dúas palabras que vexa na caixa:',
);
/** Swiss German (Alemannisch)
* @author Als-Holder
*/
$messages['gsw'] = array(
'recaptcha-edit' => 'Zum Schutz vor automatisiertem Spam, gib bitte di beide Werter in s Fäld unten yy:', # Fuzzy
'recaptcha-addurl' => 'In Dyre Bearbeitg het s neji extärni Links. Zum Schutz vor automatisiertem Spam, gib bitte di beide Werter in s Fäld unten yy:', # Fuzzy
'recaptcha-badlogin' => 'Zum Schutz gege automatisiert Knacken vu Passwerter, gib bitte di beide Werter in s Fäld unten yy:', # Fuzzy
'recaptcha-createaccount' => 'Zum Schutz gege s automatisiert Aalege vu Benutzerkonte, gib bitte di beide Werter in s Fäld unten yy:', # Fuzzy
'recaptcha-createaccount-fail' => 'D reCAPTCHA-Antwort isch fählerhaft oder si fählt.',
'recaptcha-create' => 'Zum Schutz gege s automatisiert Aalege vu Syte, gib bitte di beide Werter in s Fäld unten yy:', # Fuzzy
);
/** Hebrew (עברית)
*/
$messages['he'] = array(
'recaptcha-edit' => 'אינכם משתמש רשום.כהגנה מפני ספאם אוטומטי, אנא הקלידו את שתי המילים שלהלן. תודה.', # Fuzzy
'recaptcha-addurl' => 'אינכם משתמש רשום.כהגנה מפני ספאם אוטומטי, אנא הקלידו את שתי המילים שלהלן. תודה.', # Fuzzy
'recaptcha-badlogin' => 'כהגנה מפני מפצחי סיסמאות אוטומטיים אנא הקלידו את שתי המילים שלהלן:', # Fuzzy
'recaptcha-createaccount' => 'כהגנה מפני יצירת חשבונות פיקטיביים ע"י אוטומטים אנא הקלידו את שתי המילים שלהלן:', # Fuzzy
'recaptcha-createaccount-fail' => 'לא הוקלדו מילות האישור, או שהוקלדו מילים לא נכונות. נסו שנית.',
'recaptcha-create' => 'אינכם משתמש רשום.כהגנה מפני ספאם אוטומטי, אנא הקלידו את שתי המילים שלהלן. תודה.', # Fuzzy
);
/** Upper Sorbian (hornjoserbsce)
* @author Michawiki
*/
$messages['hsb'] = array(
'recaptcha-edit' => 'Za škit přećiwo awtomatizowanemu spamej, prošu zapisaj dwě słowje, kotrejž w slědowacym polu widźiš:', # Fuzzy
'recaptcha-addurl' => 'Twoja změna wobsahuje nowe eksterne wotkazy. Za škit přećiwo awtomatizowanemu spamej, prošu zapisaj dwě słowje, kotrerjž w slědowacym polu widźiš:', # Fuzzy
'recaptcha-badlogin' => 'Za škit přećiwo awtomatizowanemu złamanju hesłow, zapisaj prošu wobě słowje, kotrejž widźiš, do slědowaceho pola:', # Fuzzy
'recaptcha-createaccount' => 'Za škit přećiwo awtomatiskemu wutworjenju konta, prošu zapisaj tej wobě słowje, kotrejž w slědowacym polu widźiš:', # Fuzzy
'recaptcha-createaccount-fail' => 'Wopačna abo falowaca wotmołwa reCAPTCHA.',
'recaptcha-create' => 'Za škit přećiwo awtomatiskemu wutworjenju strony, prošu zapisaj tej wobě słowje, kotrejž w slědowacym polu widźiš:', # Fuzzy
);
/** Interlingua (interlingua)
* @author McDutchie
*/
$messages['ia'] = array(
'recaptcha-edit' => 'Pro adjutar a proteger le wiki contra le spam automatisate, per favor dactylographa le duo parolas que tu vide in le quadro sequente:',
'recaptcha-addurl' => 'Iste modification include nove ligamines externe. Pro adjutar a proteger le wiki contra le spam automatisate, per favor dactylographa le duo parolas que tu vide in le quadro sequente:',
'recaptcha-badlogin' => 'Pro adjutar a proteger le wiki contra le furto automatisate de contrasignos, per favor dactylographa le duo parolas que tu vide in le quadro sequente:',
'recaptcha-createaccount' => 'Pro adjutar a proteger le wiki contra le creation automatisate de contos, per favor dactylographa le duo parolas que tu vide in le quadro sequente:',
'recaptcha-createaccount-fail' => 'Responsa reCAPTCHA incorrecte o mancante.',
'recaptcha-create' => 'Pro adjutar a proteger le wiki contra le creation automatisate de paginas, per favor dactylographa le duo parolas que tu vide in le quadro sequente:',
);
/** Indonesian (Bahasa Indonesia)
* @author IvanLanin
*/
$messages['id'] = array(
'recaptcha-edit' => 'Untuk membantu pelindungan terhadap spam penyuntingan otomatis, silakan ketik dua kata yang Anda lihat dalam kotak di bawah ini:', # Fuzzy
'recaptcha-addurl' => 'Suntingan Anda menyertakan pranala eksternal baru. Untuk membantu pelindungan terhadap spam otomatis, silakan ketik dua kata yang Anda lihat dalam kotak di bawah ini:', # Fuzzy
'recaptcha-badlogin' => 'Untuk membantu pelindungan terhadap perengkahan sandi otomatis, silakan ketik dua kata yang Anda lihat dalam kotak di bawah ini:', # Fuzzy
'recaptcha-createaccount' => 'Untuk membantu pelindungan terhadap pembuatan akun otomatis, silakan ketik dua kata yang Anda lihat dalam kotak di bawah ini:', # Fuzzy
'recaptcha-createaccount-fail' => 'Jawaban reCAPTCHA tidak benar atau tidak dimasukkan.',
'recaptcha-create' => 'Untuk membantu pelindungan terhadap pembuatan halaman otomatis, silakan ketik dua kata yang Anda lihat dalam kotak di bawah ini:', # Fuzzy
);
/** Italian (italiano)
* @author Beta16
* @author Nemo bis
*/
$messages['it'] = array(
'recaptcha-edit' => 'Per proteggere il wiki dalle modifiche automatiche che aggiungono spam, ti chiediamo gentilmente di scrivere le due parole mostrate nel riquadro sottostante:',
'recaptcha-addurl' => 'La tua modifica aggiunge qualche nuovo collegamento esterno. Per proteggere il wiki dallo spam automatico, ti chiediamo gentilmente di scrivere le due parole mostrate nel riquadro sottostante:',
'recaptcha-badlogin' => 'Per proteggere il wiki dalla forzatura automatica delle password, ti chiediamo gentilmente di scrivere le due parole mostrate nel riquadro sottostante:',
'recaptcha-createaccount' => 'Per proteggere il wiki dalla creazione automatica di nuovi accessi, ti chiediamo gentilmente di scrivere le due parole mostrate nel riquadro sottostante:',
'recaptcha-createaccount-fail' => 'Risposta reCAPTCHA mancante o errata.',
'recaptcha-create' => 'Per proteggere il wiki dalla creazione automatica di pagine, ti chiediamo gentilmente di scrivere le due parole mostrate nel riquadro sottostante:',
);
/** Japanese (日本語)
* @author Shirayuki
*/
$messages['ja'] = array(
'recaptcha-desc' => 'Confirm Edit 用 reCAPTCHA モジュール',
'recaptcha-edit' => 'ウィキへの自動編集スパム攻撃を防ぐため、お手数をおかけしますが下の画像に表示されている 2 つの単語を入力してください:',
'recaptcha-addurl' => 'あなたは新しい外部リンクを追加しようとしています。ウィキへの自動スパム攻撃を防ぐため、お手数をおかけしますが下の画像に表示されている 2 つの単語を入力してください:',
'recaptcha-badlogin' => 'ウィキへの自動パスワードクラック攻撃を防ぐため、お手数をおかけしますが下の画像に表示されている 2 つの単語を入力してください:',
'recaptcha-createaccount' => 'ウィキでのアカウント自動作成を防ぐため、お手数をおかけしますが下の画像に表示されている 2 つの単語を入力してください:',
'recaptcha-createaccount-fail' => '文字列が正しくない、または入力されていません。',
'recaptcha-create' => 'ウィキでのページの自動作成を防ぐため、お手数をおかけしますが下の画像に表示されている 2 つの単語を入力してください:',
);
/** Korean (한국어)
* @author 아라
*/
$messages['ko'] = array(
'recaptcha-desc' => '편집 확인에 대한 reCAPCHA 모듈',
'recaptcha-edit' => '자동화된 편집 스팸으로부터 보호하기 위해, 아래 상자에 보이는 두 낱말을 입력하세요:', # Fuzzy
'recaptcha-addurl' => '편집에 새로운 바깥 링크가 포함되어 있습니다. 자동화된 스팸으로부터 보호하기 위해, 아래 상자에 보이는 두 낱말을 입력하세요:', # Fuzzy
'recaptcha-badlogin' => '자동화된 비밀번호 깨기로부터 보호하기 위해, 아래 상자에 보이는 두 낱말을 입력하세요:', # Fuzzy
'recaptcha-createaccount' => '자동화된 계정 만들기로부터 보호하기 위해, 아래 상자에 보이는 두 낱말을 입력하세요:', # Fuzzy
'recaptcha-createaccount-fail' => 'reCAPTCHA 답이 올바르지 않거나 없습니다.',
'recaptcha-create' => '자동화된 문서 만들기로부터 보호하기 위해, 아래 상자에 보이는 두 낱말을 입력하세요:', # Fuzzy
);
/** Colognian (Ripoarisch)
* @author Purodha
*/
$messages['ksh'] = array(
'recaptcha-edit' => 'Schrief di zwei Wööt en Kaste onge erin.
Dat sull jääje et automattesche Ändere un SPAM em Wiki schöze.', # Fuzzy
'recaptcha-addurl' => 'Schrief di zwei Wööt en Kaste onge erin.
Dat sull jääje automattesch en et Wiki jebraate SPAM schöze, nohdämm en Dingem Beidraach {{PLURAL:$1|ene neue Lengk dren es|neue Lengks dren sin|kein neue Lengks dren sin}}.', # Fuzzy
'recaptcha-badlogin' => 'Schrief di zwei Wööt en Kaste onge erin.
Dat sull jääje et automattesche Paßwoot Knacke em Wiki schöze.', # Fuzzy
'recaptcha-createaccount' => 'Schrief di zwei Wööt en Kaste onge erin.
Dat sull jääje SPAM un automattesch aanjelaate Zohjäng nohm Wiki schöze.', # Fuzzy
'recaptcha-createaccount-fail' => 'Dat wohr en verkehte udder jaa kei Antwoot op dat Kaptscha.', # Fuzzy
'recaptcha-create' => 'Schrief di zwei Wööt en Kaste onge erin.
Dat sull jääje SPAM un automattesch aanjelaate Sigge em Wiki schöze.', # Fuzzy
);
/** Luxembourgish (Lëtzebuergesch)
* @author Robby
*/
$messages['lb'] = array(
'recaptcha-edit' => "Fir d'Wiki géint automatiséierte Spam ze schützen, froe mir Iech déi zwee Wierder déi Dir gesitt an d'Këscht ënnendrënner anzeginn:",
'recaptcha-addurl' => "An Ärer Ännerung sinn nei extern Linken! Fir dës Wikie géint automatiséierte Spam ze schütze, froe mir Iech déi zwee Wierder déi Dir gesitt an d'Këscht ënnendrënner anzeginn:",
'recaptcha-badlogin' => "Fir d'Wiki géint d'automatiséiert Hacke vu Passwierder ze schützen, froe mir Iech déi zwee Wierder déi Dir gesitt an d'Këscht ënnendrënner anzeginn:",
'recaptcha-createaccount' => "Fir d'Wiki géint d'automatiséiert Uleeë vu Benotzerkonten ze schützen, froe mir Iech déi zwee Wierder déi Dir gesitt an d'Këscht ënnendrënner anzeginn:",
'recaptcha-createaccount-fail' => 'reCAPTCHA Äntwert feelt oder ass net korrekt.',
'recaptcha-create' => "Fir d'Wiki géint d'automatiséiert Uleeë vu Säiten ze schützen, froe mir Iech déi zwee Wierder déi Dir gesitt an d'Këscht ënnendrënner anzeginn:",
);
/** Macedonian (македонски)
* @author Bjankuloski06
*/
$messages['mk'] = array(
'recaptcha-desc' => 'Модул reCAPTCHA за потврда на уредувања',
'recaptcha-edit' => 'Како заштитна мерка против автоматизиран спам, би ве замолиле да ги внесете двата збора прикажани во полето:',
'recaptcha-addurl' => 'Во вашите измени има нови надворешни врски. Како заштитна мерка против автоматизиран спам, би ве замолиле да внесете двата збора прикажани во полето:',
'recaptcha-badlogin' => 'Како заштитна мерка против автоматизирано провалување на лозинки, би ве замолиле да внесете двата збора прикажани во полето:',
'recaptcha-createaccount' => 'Како заштитна мерка против автоматизирано создавање на сметки, би ве замолиле да внесете двата збора прикажани во полето:',
'recaptcha-createaccount-fail' => 'reCAPTCHA доби погрешен одговор или одговорот недостасува.',
'recaptcha-create' => 'Како заштитна мерка против автоматизирано создавање на страници, би ве замолиле да внесете двата збора прикажани во полето:',
);
/** Malay (Bahasa Melayu)
* @author Anakmalaysia
*/
$messages['ms'] = array(
'recaptcha-edit' => 'Untuk membanteras kegiatan spam automatik, sila taipkan dua perkataan yang anda lihat dalam petak di bawah:', # Fuzzy
'recaptcha-addurl' => 'Suntingan anda termasuk pautan luar baru. Untuk membanteras kegiatan spam automatik, sila taipkan dua perkataan yang anda lihat dalam petak di bawah:', # Fuzzy
'recaptcha-badlogin' => 'Untuk membanteras kegiatan meneka kata laluan secara automatik, sila taipkan dua perkataan yang anda lihat dalam petak di bawah:', # Fuzzy
'recaptcha-createaccount' => 'Untuk membanteras kegiatan membuka akaun secara automatik, sila taipkan dua perkataan yang anda lihat dalam petak di bawah:', # Fuzzy
'recaptcha-createaccount-fail' => 'Jawapan reCAPTCHA tidak betul atau tidak ada.',
'recaptcha-create' => 'Untuk membanteras kegiatan mencipta laman secara automatik, sila taipkan dua perkataan yang anda lihat dalam petak di bawah:', # Fuzzy
);
/** Maltese (Malti)
* @author Chrisportelli
*/
$messages['mt'] = array(
'recaptcha-edit' => "Sabiex tgħinna nipproteġu kontra l-modifiki li jżidu spam, jekk jogħġbok ikteb iż-żewġ kelmiet li tara fil-kaxxa t'hawn taħt:", # Fuzzy
'recaptcha-addurl' => "Il-modifika tiegħek tinkludi ħoloq esterni ġodda. Sabiex tipproteġi kontra spam awtomatiku, jekk jogħġbok ikteb iż-żewġ kelmiet li tara fil-kaxxa t'hawn taħt:", # Fuzzy
'recaptcha-badlogin' => "Sabiex tgħinna nipproteġu kontra l-infurzar awtomatiku tal-passwords, jekk jogħġbok ikteb iż-żewġ kelmiet li tara fil-kaxxa t'hawn taħt:", # Fuzzy
'recaptcha-createaccount' => "Sabiex tgħinna nipproteġu kontra l-ħolqien awtomatiku ta' kontijiet ġodda, jekk jogħġbok ikteb iż-żewġ kelmiet li tara fil-kaxxa t'hawn taħt:", # Fuzzy
'recaptcha-createaccount-fail' => 'Tweġiba reCAPTCHA ħażina jew nieqsa.',
'recaptcha-create' => "Sabiex tgħinna nipproteġu kontra l-ħolqien awtomatiku ta' paġni, jekk jogħġbok ikteb iż-żewġ kelmiet li tara fil-kaxxa t'hawn taħt:", # Fuzzy
);
/** Norwegian Bokmål (norsk bokmål)
* @author Event
*/
$messages['nb'] = array(
'recaptcha-edit' => 'Som beskyttelse mot automatisk redigert spam, vennligst skriv inn de to ordene du ser i boksen under:', # Fuzzy
'recaptcha-addurl' => 'Din redigering inneholder nye eksterne lenker. Som beskyttelse mot automatisk spam, vennligst skriv inn de to ordene du ser i boksen under:', # Fuzzy
'recaptcha-badlogin' => 'Som beskyttelse mot automatisk passordknekking, vennligst skriv inn de to ordene du ser i boksen under:', # Fuzzy
'recaptcha-createaccount' => 'Som beskyttelse mot automatisk opprettelse av brukerkonto, vennligst skriv inn de to ordene du ser i boksen under:', # Fuzzy
'recaptcha-createaccount-fail' => 'Feil eller manglende reCAPTCHA-svar.',
'recaptcha-create' => 'Som beskyttelse mot automatisk opprettelse av side, vennligst skriv inn de to ordene du ser i boksen under:', # Fuzzy
);
/** Dutch (Nederlands)
* @author HanV
* @author Siebrand
*/
$messages['nl'] = array(
'recaptcha-edit' => 'Voer ter bescherming tegen geautomatiseerde spam de twee woorden in die u in het onderstaande venster ziet:',
'recaptcha-addurl' => 'Uw bewerking bevat nieuwe externe koppelingen. Voer ter bescherming tegen geautomatiseerde spam de twee woorden in die u ziet in het onderstaande venster:',
'recaptcha-badlogin' => 'Voer ter bescherming tegen het automatisch kraken van wachtwoorden de twee woorden in die u ziet in het invoervenster:',
'recaptcha-createaccount' => 'Om het automatisch aanmaken van gebruikers tegen te gaan moet u de twee woorden in het onderstaande venster invoeren:',
'recaptcha-createaccount-fail' => 'Het reCAPTCHA-antwoord is onjuist of niet ingevoerd.',
'recaptcha-create' => 'Om het automatisch aanmaken van een pagina tegen te gaan moet u de twee woorden invoeren die in het onderstaande veld staan:',
);
/** Nederlands (informeel) (Nederlands (informeel))
* @author Siebrand
*/
$messages['nl-informal'] = array(
'recaptcha-edit' => 'Voer ter bescherming tegen geautomatiseerde spam de twee woorden die je ziet in het invoervenster in:', # Fuzzy
'recaptcha-addurl' => 'Je bewerking bevat nieuwe externe koppelingen. Voer ter bescherming tegen geautomatiseerde spam de twee woorden die je ziet in het invoervenster in:', # Fuzzy
'recaptcha-badlogin' => 'Voer ter bescherming tegen het automatisch kraken van wachtwoorden de twee woorden die je ziet in het invoervenster in:', # Fuzzy
'recaptcha-createaccount' => 'Om het automatisch aanmaken van gebruikers tegen te gaan moet je de twee woorden die je ziet invoeren:', # Fuzzy
'recaptcha-create' => "Om het automatisch aanmaken van pagina's tegen te gaan moet je de twee woorden die je ziet invoeren:", # Fuzzy
);
/** Occitan (occitan)
* @author Cedric31
*/
$messages['oc'] = array(
'recaptcha-createaccount-fail' => 'Responsa de reCAPTCHA faussa o mancanta.',
);
/** Polish (polski)
* @author WTM
*/
$messages['pl'] = array(
'recaptcha-edit' => 'Aby uchronić nas przed robotami, proszę wpisać dwa widoczne słowa w poniższym polu:', # Fuzzy
'recaptcha-addurl' => 'Twoja edycja zawiera linki zewnętrzne. Aby uchronić nas przed robotami, proszę wpisać dwa widoczne słowa w poniższym polu:', # Fuzzy
'recaptcha-badlogin' => 'Aby uchronić nas przed złamaniem automatycznym haseł, proszę wpisać dwa widoczne słowa w poniższym polu:', # Fuzzy
'recaptcha-createaccount' => 'Aby uchronić nas przed automatycznym tworzeniem użytkowników, proszę wpisać dwa widoczne słowa w poniższym polu:', # Fuzzy
'recaptcha-createaccount-fail' => 'Odpowiedź na reCAPTCHA jest fałszywa lub brakująca.',
'recaptcha-create' => 'Aby uchronić nas przed tworzeniem stron przez robotów, proszę wpisać dwa widoczne słowa w poniższym polu:', # Fuzzy
);
/** Piedmontese (Piemontèis)
* @author Borichèt
* @author Dragonòt
*/
$messages['pms'] = array(
'recaptcha-edit' => "Për giuté a protege contra la rumenta dle modìfiche automàtiche, për piasì ch'a scriva le doe paròle ch'a s-ciàira ant ël quàder sì-sota:", # Fuzzy
'recaptcha-addurl' => "Soa modìfica a conten d'anliure esterne neuv. Për giuté a protege contra la rumenta dle modìfiche automàtiche, për piasì ch'a scriva le doe paròle ch'a s-ciàira ant ël quàder sì-sota:", # Fuzzy
'recaptcha-badlogin' => "Për giuté a protege contra la forsadura automatisà ëd le ciav, për piasì ch'a scriva le doe paròle ch'a s-ciàira ant ël formolari sì-sota:", # Fuzzy
'recaptcha-createaccount' => "Për giuté a protege contra la creassion automatisà ëd cont, për piasì ch'a scriva le doe paròle ch'a s-ciàira ant ël formolari sì-sota:", # Fuzzy
'recaptcha-createaccount-fail' => 'Rispòsta ëd reCAPTCHA fàussa o mancanta.',
'recaptcha-create' => "Për giuté a protege contra la creassion automatisà ëd pàgine, për piasì ch'a scriv le doe paròle ch'a s-ciàira ant la casela sì-sota:", # Fuzzy
);
/** Portuguese (português)
* @author Hamilton Abreu
*/
$messages['pt'] = array(
'recaptcha-edit' => 'Para proteger-nos de spam, por favor escreva as duas palavras visíveis abaixo:', # Fuzzy
'recaptcha-addurl' => "A sua edição contém links externos novos. Como prevenção contra sistemas automatizados de inserção de ''spam'', escreva as duas palavras visíveis na caixa abaixo:", # Fuzzy
'recaptcha-badlogin' => 'Para proteger-nos de robots que tentam adivinhar senhas, por favor escreva as duas palavras visíveis abaixo:', # Fuzzy
'recaptcha-createaccount' => 'Para proteger-nos de criação automática de contas, por favor escreva as duas palavras visíveis abaixo:', # Fuzzy
'recaptcha-createaccount-fail' => 'A resposta ao reCAPTCHA é errada.',
'recaptcha-create' => 'Para proteger-nos da criação automática de páginas, por favor escreva as duas palavras visíveis abaixo:', # Fuzzy
);
/** Brazilian Portuguese (português do Brasil)
*/
$messages['pt-br'] = array(
'recaptcha-edit' => 'Para ajudar a prevenir contra vandalismos, por favor digite as duas palavras que você vê na caixa abaixo:', # Fuzzy
'recaptcha-addurl' => 'A sua edição inclui ligações externas. Para ajudar a prevenir contra vandalismos, por favor digite as duas palavras que você vê na caixa abaixo:', # Fuzzy
'recaptcha-badlogin' => 'Para ajudar a prevenir contra tentativas de desbloquear senhas, por favor digite as duas palavras que você vê na caixa abaixo:', # Fuzzy
'recaptcha-createaccount' => 'Para ajudar a prevenir contra criação automatizada de usuários, por favor digite as duas palavras que você vê na caixa abaixo:', # Fuzzy
'recaptcha-createaccount-fail' => 'Resposta incorreta ao reCAPTCHA.',
'recaptcha-create' => 'Para ajudar a prevenir contra criação automatizada de páginas, por favor digite as duas palavras que você vê na caixa abaixo:', # Fuzzy
);
/** tarandíne (tarandíne)
* @author Joetaras
*/
$messages['roa-tara'] = array(
'recaptcha-edit' => "Pe darne 'na màne a proteggere condre le cangiaminde automatece de le rummate, pe piacere scrive le doje parole ca 'ndruche jndr'à buatte aqquà sotte:", # Fuzzy
'recaptcha-addurl' => "Le cangiaminde tune 'ngludone collegaminde de fore nuève. Pe darne 'na màne a proteggere condre le cangiaminde automatece de le rummate, pe piacere scrive le doje parole ca 'ndruche jndr'à buatte aqquà sotte:", # Fuzzy
'recaptcha-badlogin' => "Pe darne 'na màne a proteggere condre le futteminde automatece de le passuord, pe piacere scrive le doje parole ca 'ndruche jndr'à buatte aqquà sotte:", # Fuzzy
'recaptcha-createaccount' => "Pe darne 'na màne a proteggere condre le ccrejaziune automatece de le cunde, pe piacere scrive le doje parole ca 'ndruche jndr'à buatte aqquà sotte:", # Fuzzy
'recaptcha-createaccount-fail' => 'Resposte reCAPTCHA ingorrette o mangande.',
'recaptcha-create' => "Pe darne 'na màne a proteggere condre le ccrejaziune automatece de le pàggene, pe piacere scrive le doje parole ca 'ndruche jndr'à buatte aqquà sotte:", # Fuzzy
);
/** Russian (русский)
* @author Okras
* @author Александр Сигачёв
*/
$messages['ru'] = array(
'recaptcha-edit' => 'В целях защиты от автоматического спама в правках просим вас ввести два слова, которые вы видите ниже:',
'recaptcha-addurl' => 'Ваша правка содержит новые внешние ссылки. В целях защиты от автоматического спама просим вас ввести два слова, которые вы видите в рамке ниже:',
'recaptcha-badlogin' => 'В целях защиты от автоматического подбора пароля просим вас ввести два слова, которые вы видите в рамке ниже:',
'recaptcha-createaccount' => 'В целях защиты от автоматического создания учётных записей просим вас ввести два слова, которые вы видите в рамке ниже:',
'recaptcha-createaccount-fail' => 'Ответ reCAPTCHA неправильный или отсутствует.',
'recaptcha-create' => 'В целях защиты от автоматического создания страниц просим вас ввести два слова, которые вы видите в рамке ниже:',
);
/** Sinhala (සිංහල)
* @author පසිඳු කාවින්ද
*/
$messages['si'] = array(
'recaptcha-createaccount-fail' => 'වැරදි හෝ දක්නට නොමැති reCAPTCHA පිළිතුර.',
);
/** Serbian (Cyrillic script) (српски (ћирилица))
* @author Rancher
*/
$messages['sr-ec'] = array(
'recaptcha-desc' => 'Модул reCAPTCHA за потврду уређивања',
);
/** Serbian (Latin script) (srpski (latinica))
*/
$messages['sr-el'] = array(
'recaptcha-desc' => 'Modul reCAPTCHA za potvrdu uređivanja',
);
/** Swedish (svenska)
* @author WikiPhoenix
*/
$messages['sv'] = array(
'recaptcha-edit' => 'För att skydda wikin mot automatiskt redigerings-spam ber vi dig att skriva de två orden du ser i rutan nedan.',
'recaptcha-addurl' => 'Din redigering innehåller nya externa länkar. För att skydda wikin mot automatiserat spam ber vi dig att skriva de två ordern du ser i rutan nedan:',
'recaptcha-badlogin' => 'För att skydda wikin mot automatiserade försök att knäcka lösenord ber vi dig att skriva in de två orden du ser i rutan nedan:',
'recaptcha-createaccount' => 'För att skydda wikin mot automatiskt kontoskapande ber vi dig att skriva de två orden som du ser i rutan nedan:',
'recaptcha-createaccount-fail' => 'Du har angivit ett felaktig svar för reCAPTCHA.',
'recaptcha-create' => 'För att skydda wikin mot automatiserade sidskapande ber vi dig att skriva in de två orden du ser i rutan nedan:',
);
/** Swahili (Kiswahili)
* @author Lloffiwr
*/
$messages['sw'] = array(
'recaptcha-edit' => 'Ili kusaidia kuzuia mashine zisihariri, tafadhali andika maneno mawili yanayoonekana katika kisanduku pakipo chini:', # Fuzzy
'recaptcha-addurl' => 'Haririo lako lina viungo vipya vya nje. Ili kusaidia katika kuzuisha viungo visivyotakiwa visiwekwe na mashine, tafadhali andika maneno mawili yanayoonekana katika kisanduku pakipo chini:', # Fuzzy
'recaptcha-badlogin' => 'Ili kusaidia katika kuzuia neno lako la siri lisigunduliwe na mashine, tafadhali andika maneno mawili yanayoonekana katika kisanduku pakipo chini:', # Fuzzy
'recaptcha-createaccount' => 'Ili kusaidia kuzuia akaunti zisifunguliwe nyingi na mashine, tafadhali andika maneno mawili yanayoonekana katika kisanduku pakipo chini:', # Fuzzy
'recaptcha-createaccount-fail' => 'Jibu la swali la CAPTCHA si sahihi au halipo.', # Fuzzy
'recaptcha-create' => 'Ili kusaidia kuzuia kurasa zisianzishwe nyingi na mashine, tafadhali andika maneno mawili yanayoonekana katika kisanduku pakipo chini:', # Fuzzy
);
/** Tagalog (Tagalog)
* @author AnakngAraw
*/
$messages['tl'] = array(
'recaptcha-desc' => 'Modulo ng reCAPTCHA para sa Pagtiyak ng Pagbago',
'recaptcha-edit' => 'Upang makatulong sa pagprutekta laban sa kusang basurang pamamatnugot, paki imakinilya ang dalawang mga salitang nakikita mo sa loob ng kahong nasa ibaba:', # Fuzzy
'recaptcha-addurl' => 'Ang pagbabago mo ay nagsasama ng bagong panlabas na mga kawing. Upang makatulong sa pagprutekta laban sa kusang paglusob ng basurang-liham, paki imakinilya ang dalawang mga salitang nakikita sa loob ng kahong nasa ibaba:', # Fuzzy
'recaptcha-badlogin' => 'Upang makatulong sa pagsasanggalang laban sa kusang panghuhula ng hudyat, paki imakinilya ang dalawang mga salitang nakikita mo sa loob ng kahong nasa ibaba:', # Fuzzy
'recaptcha-createaccount' => 'Upang makatulong sa pagprutekta laban sa kusang paglikha ng akawnt, paki imakinilya ang dalawang mga salitang nakikita mo sa loob ng kahong nasa ibaba:', # Fuzzy
'recaptcha-createaccount-fail' => 'Hindi tama o nawawalang sagot sa reCAPTCHA.',
'recaptcha-create' => 'Upang makatulong sa pagprutekta laban sa kusang paglikha ng pahina, paki imakinilya ang dalawang mga salitang nakikita mo sa loob ng kahong nasa ibaba:', # Fuzzy
);
/** Ukrainian (українська)
* @author Andriykopanytsia
* @author Base
* @author Тест
*/
$messages['uk'] = array(
'recaptcha-edit' => 'Для запобігання автоматичному спаму у редагуваннях просимо вас ввести два слова, які Ви бачите у блоці нижче:',
'recaptcha-addurl' => 'Ваше редагування містить зовнішні посилання. Для запобігання автоматичному спаму у редагуваннях просимо вас ввести два слова, які Ви бачите у блоці нижче:',
'recaptcha-badlogin' => 'Для запобігання автоматичному підбору паролю просимо вас ввести два слова, які Ви бачите у блоці нижче:',
'recaptcha-createaccount' => 'Для запобігання автоматичному створенню облікових записів просимо вас ввести два слова, які Ви бачите у блоці нижче:',
'recaptcha-createaccount-fail' => 'Відповідь reCAPTCHA неправильна або відсутня.',
'recaptcha-create' => 'З метою захисту вікі від автоматичного створення сторінки просимо вас ввести два слова, які ви бачите у блоці нижче:',
);
/** Vietnamese (Tiếng Việt)
* @author Minh Nguyen
*/
$messages['vi'] = array(
'recaptcha-desc' => 'Mô đun reCAPTCHA cho Confirm Edit',
'recaptcha-edit' => 'Để giúp tránh các sửa đổi spam tự động, xin vui lòng gõ hai từ mà bạn nhìn thấy vào hộp dưới đây:',
'recaptcha-addurl' => 'Sửa đổi của bạn có chứa liên kết ngoài mới. Để giúp tránh các sửa đổi spam tự động, xin vui lòng gõ hai từ mà bạn nhìn vào hộp dưới đây:',
'recaptcha-badlogin' => 'Để giúp tránh bẻ khóa mật khẩu tự động, xin vui lòng gõ hai từ mà bạn nhìn vào hộp dưới đây:',
'recaptcha-createaccount' => 'Để giúp tránh việc mở tài khoản tự động, xin vui lòng gõ hai từ mà bạn nhìn vào hộp dưới đây:',
'recaptcha-createaccount-fail' => 'Thiếu câu trả lời reCAPTCHA hoặc câu trả lời không đúng.',
'recaptcha-create' => 'Để giúp tránh việc tạo trang tự động, xin vui lòng gõ hai từ mà bạn nhìn vào hộp dưới đây:',
);
/** Simplified Chinese (中文(简体)‎)
* @author Hydra
* @author Hzy980512
* @author Onecountry
*/
$messages['zh-hans'] = array(
'recaptcha-edit' => '为了避免垃圾用户自动编辑,请键入下面框中的两个单词:',
'recaptcha-addurl' => '您的编辑包含新的外部链接。为了避免垃圾用户自动编辑,请键入下面框中的两个单词:',
'recaptcha-badlogin' => '为避免自动密码破解,请键入下面框中的两个单词:',
'recaptcha-createaccount' => '为了避免创建自动垃圾用户,请键入下面框中的两个单词:',
'recaptcha-createaccount-fail' => 'reCAPTCHA 答案不正确或未填写。',
'recaptcha-create' => '为了避免垃圾用户自动创建页面,请键入下面框中的两个单词:',
);
/** Traditional Chinese (中文(繁體)‎)
* @author StephDC
*/
$messages['zh-hant'] = array(
'recaptcha-edit' => '為了防止wiki被自動編輯的垃圾損害我們請您鍵入您在下面的框中看到的這兩個單詞',
'recaptcha-addurl' => '您的編輯包含新的外部鏈接。為了幫助防止自動垃圾郵件,請鍵入您在下面的框中看到這兩個詞:', # Fuzzy
'recaptcha-badlogin' => '為幫助防止自動密碼破解,請鍵入您看到下面的框中的兩個字:', # Fuzzy
'recaptcha-createaccount' => '為了幫助防止創建自動化的帳戶,請鍵入您在下面的框中看到的這兩個字:', # Fuzzy
'recaptcha-createaccount-fail' => '不正確或缺失的 reCAPTCHA 答案。',
'recaptcha-create' => '為了幫助防止自動的頁創建,請鍵入您在下面的框中看到這兩個詞:', # Fuzzy
);

View file

@ -0,0 +1,64 @@
<?php
/**
* Captcha class using the reCAPTCHA widget.
* Stop Spam. Read Books.
*
* @addtogroup Extensions
* @author Mike Crawford <mike.crawford@gmail.com>
* @copyright Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net
* @licence MIT/X11
*/
if ( !defined( 'MEDIAWIKI' ) ) {
exit;
}
$dir = __DIR__;
require_once $dir . '/ConfirmEdit.php';
$wgCaptchaClass = 'ReCaptcha';
$wgExtensionMessagesFiles['ReCaptcha'] = $dir . '/ReCaptcha.i18n.php';
$wgAutoloadClasses['ReCaptcha'] = $dir . '/ReCaptcha.class.php';
require_once( 'recaptchalib.php' );
// Set these in LocalSettings.php
$wgReCaptchaPublicKey = '';
$wgReCaptchaPrivateKey = '';
// For backwards compatibility
$recaptcha_public_key = '';
$recaptcha_private_key = '';
/**
* Sets the theme for ReCaptcha
*
* See http://code.google.com/apis/recaptcha/docs/customization.html
*/
$wgReCaptchaTheme = 'red';
$wgExtensionFunctions[] = 'efReCaptcha';
/**
* Make sure the keys are defined.
*/
function efReCaptcha() {
global $wgReCaptchaPublicKey, $wgReCaptchaPrivateKey;
global $recaptcha_public_key, $recaptcha_private_key;
global $wgServerName;
// Backwards compatibility
if ( $wgReCaptchaPublicKey == '' ) {
$wgReCaptchaPublicKey = $recaptcha_public_key;
}
if ( $wgReCaptchaPrivateKey == '' ) {
$wgReCaptchaPrivateKey = $recaptcha_private_key;
}
if ( $wgReCaptchaPublicKey == '' || $wgReCaptchaPrivateKey == '' ) {
die ( 'You need to set $wgReCaptchaPrivateKey and $wgReCaptchaPublicKey in LocalSettings.php to ' .
"use the reCAPTCHA plugin. You can sign up for a key <a href='" .
htmlentities( recaptcha_get_signup_url ( $wgServerName, "mediawiki" ) ) . "'>here</a>." );
}
}

Some files were not shown because too many files have changed in this diff Show more